GitHub Actions para sincronizar Obsidian con tu blog
El mayor problema de tener el contenido en un repo separado al del blog es mantenerlos sincronizados. Si publicas desde Obsidian pero el blog vive en otro repo, alguien tiene que decirle al blog que hay contenido nuevo. Ese alguien puede ser GitHub Actions.
El problema con los submódulos
Mi vault de Obsidian vive en un repo privado. Mi blog referencia ese vault como un git submodule — específicamente la carpeta 020-Areas/Blog/ donde guardo los posts.
Los submódulos en git no se actualizan solos. Cuando haces push al repo del submódulo (el vault), el repo padre (el blog) sigue apuntando al commit anterior. Para que el blog vea los posts nuevos, alguien tiene que entrar al repo del blog, correr git submodule update --remote y hacer push.
Hacerlo manualmente es tedioso y se olvida. Automatizarlo con un Action lleva 20 minutos y nunca más vuelves a pensar en eso.
Cómo funciona el Action
El workflow vive en el vault. Se dispara en cada push a main que toque la carpeta 020-Areas/Blog/.
name: Sync blog content
on:
push:
branches: [main]
paths:
- "020-Areas/Blog/**"
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Trigger blog submodule update
uses: actions/github-script@v8
with:
github-token: ${{ secrets.BLOG_REPO_TOKEN }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'tu-usuario',
repo: 'tu-blog',
workflow_id: 'update-submodule.yml',
ref: 'main'
})
El blog tiene su propio workflow que, al recibir el dispatch, actualiza el submódulo:
name: Update vault submodule
on:
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: true
token: ${{ secrets.BLOG_REPO_TOKEN }}
- name: Update submodule
run: |
git submodule update --remote content/vault
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add content/vault
git diff --staged --quiet || git commit -m "chore: update vault submodule"
git push
Cuando Vercel detecta el push al repo del blog, redespliega automáticamente.
El flujo completo
- Escribo un post en Obsidian y cambio
draft: false - Obsidian Git hace push al vault
- El Action del vault detecta el push en
020-Areas/Blog/ - Dispara el workflow del blog via
workflow_dispatch - El blog actualiza el submódulo y hace push
- Vercel redespliega
Desde que termino el post hasta que aparece online: entre 2 y 5 minutos, sin que yo toque nada.
El token entre repos
Para que el Action del vault pueda disparar workflows en el blog (repo separado), necesitas un token con permiso para lanzar workflows en el repo del blog. Lo más seguro es un fine-grained Personal Access Token limitado a ese repo, con permiso de escritura en Actions (y en Contents, porque el workflow del blog también hace push). Guárdalo como secret en el vault repo con el nombre BLOG_REPO_TOKEN.
Si los repos están bajo la misma organización de GitHub, puedes usar una GitHub App de la organización en vez de uno personal para mayor seguridad.
Errores comunes
Resource not accessible by integrationo403: el token no tiene permisos sobre el repo del blog, o expiró. Los fine-grained tokens caducan: apunta la fecha.- El Action del vault no se dispara: revisa que el cambio toque de verdad la ruta del filtro
paths. Un push que solo modifica otras carpetas no lo lanza (es lo que quieres). - El workflow del blog falla en el checkout: el submódulo apunta a un repo privado, así que
actions/checkoutnecesita un token con acceso al vault, no elGITHUB_TOKENpor defecto. - El post no aparece aunque todo salió en verde: mira el frontmatter. Un
draft: trueolvidado o unadatecon formato raro hacen que el blog lo ignore.
Lo que habría hecho diferente
La solución del submódulo funciona, pero agrega complejidad al setup inicial. Una alternativa más simple: leer los archivos Markdown directamente desde la API de GitHub en build time, sin submódulo. Menos partes móviles, más fácil de debuggear cuando algo falla.
Si quieres ver el contexto completo de cómo está armado el blog, escribí sobre cómo lo construí con Next.js y Obsidian.