Do Obsidian ao blog com um commit — GitHub Actions, Marmite e Telegram
⚠️ Rascunho: Este post é um rascunho e pode não estar completo ainda.

Do Obsidian ao blog com um commit — GitHub Actions, Marmite e Telegram

Introdução

Por muito tempo meu fluxo de escrita era uma bagunça: rascunho no Obsidian, copiar manualmente pro repositório do blog, rodar o build local, fazer deploy na mão. Funcionava, mas qualquer fricção a mais e o post ficava na gaveta por semanas.

A solução foi montar um pipeline que faz tudo isso com um único git push. O Obsidian continua sendo meu editor — onde escrevo, reviso e organizo — mas agora ele está conectado diretamente ao repositório do blog. Um commit aciona o GitHub Actions, que roda o Marmite, gera o site estático e faz deploy. No final do job, uma mensagem chega no meu Telegram confirmando se deu certo ou errado.

Este post documenta exatamente como montar esse setup do zero.


Visão geral do pipeline

Antes de entrar nos detalhes, vale ter o mapa mental de tudo que vai acontecer:

Etapa Ferramenta O que faz
Edição Obsidian Escrever e revisar posts em Markdown
Versionamento Git + GitHub Vault como repositório, histórico de alterações
Build GitHub Actions Roda o Marmite e gera o _site/
Deploy GitHub Actions Copia o _site/ para o servidor via rsync
Notificação Telegram Bot Avisa o resultado do job no celular

O fluxo completo é: escrevo no Obsidian, dou push, e alguns minutos depois o post está no ar — ou tem uma notificação me avisando o que quebrou.


Configurando o Obsidian como repositório Git

Plugin obsidian-git

O plugin obsidian-git é o que transforma o vault em um repositório gerenciado. Ele faz commit e push automático em intervalos configuráveis, ou manualmente pelo command palette.

Instalação via Community Plugins do Obsidian. Após instalar, as configurações relevantes:

Vault backup interval (minutes): 0     ← desabilita auto-commit
Auto pull interval (minutes): 5        ← puxa mudanças remotas periodicamente
Commit message: vault: {{date}}
Pull updates on startup: true

Prefiro não usar auto-commit. O commit manual pelo Ctrl+P → Git: Commit and push me força a revisar o que estou mandando.

Estrutura do repositório

O vault inteiro vira o repositório, mas só uma pasta vai pro pipeline do blog:

vault/
├── .obsidian/          ← configurações do Obsidian (no .gitignore do blog)
├── notas/              ← notas pessoais, não publicadas
├── projetos/           ← docs de projetos, não publicadas
└── blog-content/
    └── content/
        ├── media/
        │   └── gallery/
        ├── 2026-04-19-meu-post.md
        └── marmite.yaml

O GitHub Actions vai olhar apenas para mudanças em blog-content/content/.


Configurando o Marmite

O Marmite é um gerador de sites estáticos minimalista escrito em Rust. Rápido, sem dependências de Node, binário único — ideal para rodar em CI.

marmite.yaml

O arquivo de configuração fica na raiz do content/:

name: raphazilla.rocks
tagline: notas, projetos e devaneios
url: https://raphazilla.rocks
language: pt-BR
authors:
  rapha:
    name: Rapha
    avatar: media/rapha.jpg

pagination: 10
date_format: "%d/%m/%Y"

extra:
  social:
    github: raphazilla
    mastodon: "@rapha@bolha.us"

Testando localmente

Antes de mandar pro CI, vale validar o build local:

# instala o binário (uma vez)
cargo install marmite

# na pasta blog-content/
marmite content/ _site/ --serve

O site sobe em http://localhost:8000 para revisão.


GitHub Actions — o coração do pipeline

O workflow fica em .github/workflows/deploy.yml na raiz do repositório.

name: Build e Deploy

on:
  push:
    branches: [main]
    paths:
      - "blog-content/content/**"

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Instala Marmite
        run: |
          curl -sSL \
            https://github.com/rochacbruno/marmite/releases/latest/download/marmite-x86_64-unknown-linux-gnu.tar.gz \
            | tar xz -C /usr/local/bin

      - name: Build do site
        run: marmite blog-content/content/ _site/

      - name: Deploy via rsync
        env:
          SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
          DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
          DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
          DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
        run: |
          echo "$SSH_KEY" > /tmp/deploy_key
          chmod 600 /tmp/deploy_key
          rsync -avz --delete \
            -e "ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no" \
            _site/ \
            ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}

      - name: Notifica Telegram — sucesso
        if: success()
        run: |
          curl -s -X POST \
            "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
            -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
            -d text="Blog deployado com sucesso — commit \`${{ github.sha }}\`" \
            -d parse_mode="Markdown"

      - name: Notifica Telegram — falha
        if: failure()
        run: |
          curl -s -X POST \
            "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
            -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
            -d text="Falha no deploy do blog — veja em ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
            -d parse_mode="Markdown"

Secrets necessários

Todos esses valores ficam em Settings → Secrets and variables → Actions no repositório:

Secret Descrição
DEPLOY_SSH_KEY Chave privada SSH para o servidor
DEPLOY_HOST IP ou hostname do servidor
DEPLOY_USER Usuário SSH
DEPLOY_PATH Caminho no servidor, ex: /var/www/blog/
TELEGRAM_TOKEN Token do bot (obtido com o @BotFather)
TELEGRAM_CHAT_ID ID do seu chat ou grupo

Configurando o bot do Telegram

Criando o bot

  1. Abra o Telegram e fale com o @BotFather
  2. Envie /newbot e siga as instruções
  3. Guarde o token no formato 123456789:ABCdef...

Descobrindo o chat_id

Mande qualquer mensagem pro bot e consulte a API:

curl "https://api.telegram.org/bot<SEU_TOKEN>/getUpdates"

O chat_id aparece no campo message.chat.id do JSON retornado. Para grupos, é negativo (ex: -1001234567890).

Testando manualmente

curl -s -X POST \
  "https://api.telegram.org/bot<TOKEN>/sendMessage" \
  -d chat_id="<CHAT_ID>" \
  -d text="Teste de notificação"

Se chegou, está funcionando.


Lint antes do push — evitando builds quebrados

Adicionar um hook pre-push no vault evita mandar Markdown com erros que vão quebrar o linter do CI:

# .git/hooks/pre-push
#!/usr/bin/env bash
set -e

echo "Rodando markdownlint..."
markdownlint blog-content/content/**/*.md --config .markdownlint.json

echo "OK — enviando push."
chmod +x .git/hooks/pre-push

O .markdownlint.json reflete as mesmas regras do validate.yml do CI:

{
  "MD026": true,
  "MD034": true,
  "MD040": true,
  "MD047": true,
  "line-length": { "line_length": 100 }
}

O fluxo no dia a dia

Com tudo configurado, escrever e publicar ficou assim:

  1. Abro o Obsidian, crio ou edito um arquivo em blog-content/content/
  2. Quando estiver pronto, Ctrl+P → Git: Commit and push
  3. O GitHub Actions detecta a mudança, roda o build e faz deploy
  4. Alguns segundos depois, a notificação chega no Telegram

Se eu precisar só salvar rascunhos sem publicar, uso publish: false no frontmatter — o CI roda o build normalmente, mas o Marmite ignora o arquivo.


Conclusão

O pipeline completo — Obsidian escrevendo, Git versionando, Actions buildando, Marmite gerando e Telegram notificando — eliminou toda a fricção manual que me fazia procrastinar publicações. O custo de publicar um post caiu para zero: é só escrever e fazer push.

O código completo do workflow está no repositório em .github/workflows/deploy.yml. Se tiver dúvidas ou melhorias, abre uma issue por lá. 2026-04-19