ADR-031: Automatización de contenido dinámico en la documentación¶
Estado: aceptado
Contexto¶
La documentación de SmallCountry contiene múltiples elementos que requieren actualización manual cada vez que se añade contenido nuevo:
- Contadores en la landing page (
index.md): "11 proyectos activos", "23 ADRs", "82 servicios" - Lista de proyectos en
proyectos/index.md: cada proyecto nuevo requiere añadir una card manualmente - Lista de ADRs en
adr/index.md: cada ADR nuevo requiere añadir una fila - Sección "Lo nuevo" en la landing page: se actualiza manualmente y queda obsoleta
- Metadatos (
title,description) ausentes en cientos de archivos
Esto genera tres problemas:
1. Desincronización: los contadores y listas se desactualizan porque añadir contenido no actualiza automáticamente las páginas que lo referencian
2. Fricción: añadir un proyecto nuevo requiere tocar 4 archivos (.md nuevo, index.md, mkdocs.yml, sc_config.yml)
3. Deuda de metadatos: 1219 avisos de campos opcionales ausentes, acumulados por falta de automatización
Decisión¶
Implementar un sistema de generación de contenido dinámico basado en dos mecanismos:
1. pymdownx.snippets para inclusión de fragmentos¶
La extensión pymdownx.snippets (ya incluida en pymdown-extensions, que es dependencia de mkdocs-material) permite incluir archivos Markdown dentro de otros mediante la sintaxis:
Esto permite que páginas como index.md deleguen sus secciones dinámicas a archivos generados automáticamente.
2. Script pre-build (scripts/generate_dynamic_content.py)¶
Un script Python que se ejecuta antes de cada build y genera fragmentos Markdown en docs/_generated/:
| Fragmento generado | Fuente de datos | Uso |
|---|---|---|
dashboard.md |
Filesystem + sc_config.yml |
Conteos en landing page |
adrs.md |
docs/adr/0*.md |
Lista de ADRs en adr/index.md |
proyectos_cards.md |
sc_config.yml |
Cards de proyectos en proyectos/index.md |
novedades.md |
git log --oneline (últimos 5 commits docs:) |
Sección "Lo nuevo" |
El directorio docs/_generated/ se excluye de git (.gitignore) porque su contenido es derivado y se regenera en cada build.
3. Script de inyección de metadatos (scripts/inject_metadata.py)¶
Un script independiente que extrae el primer H1 como title y el primer párrafo como description, y los inyecta en el frontmatter YAML de los archivos que no los tengan. Se ejecuta una vez (no en cada build) y los cambios se commitean.
Flujo en CI¶
- name: Generar contenido dinámico
run: python3 scripts/generate_dynamic_content.py
- name: Build ProperDocs
run: properdocs build --strict --quiet --site-dir /tmp/site-staging
Alternativas consideradas¶
A. mkdocs-macros-plugin¶
Permite embeber Python directamente en Markdown: {{ proyectos|length }}. Más elegante pero requiere instalar un plugin nuevo, añadirlo a requirements.txt, reconstruir la imagen Docker, y mantenerlo como dependencia adicional.
Descartada porque pymdownx.snippets logra el mismo resultado sin dependencias nuevas.
B. mkdocs-gen-files¶
Plugin oficial de MkDocs para generar archivos durante el build. Requiere instalación adicional y configuración en mkdocs.yml.
Descartada por la misma razón: añade una dependencia cuando snippets ya está disponible.
C. Mantener todo manual¶
Editar contadores y listas a mano cada vez que se añade contenido.
Descartada porque es la causa raíz del problema actual (desincronización, fricción, deuda).
D. mkdocs-awesome-pages-plugin¶
Auto-genera la navegación desde la estructura de directorios.
Descartada para el nav principal porque la navegación de SmallCountry es curatorial (no todo lo que existe en docs/ debe aparecer en el menú). Podría reconsiderarse para sub-secciones como ADRs o fichas.
Consecuencias¶
Positivas¶
- Cero dependencias nuevas:
pymdownx.snippetsya está instalado conmkdocs-material - Contadores siempre actualizados: el build refleja el estado real del repositorio
- Añadir un proyecto baja de 4 archivos a 2 (
.mdnuevo +sc_config.yml; el index se auto-genera) - "Lo nuevo" se actualiza solo: basta con hacer commits con prefijo
docs: - Metadatos reparables por lotes: el script de inyección reduce 1219 avisos a ~400 en una pasada
Negativas¶
- El contenido generado no se ve en local sin ejecutar el script: hay que acordarse de correr
generate_dynamic_content.pyantes deproperdocs serveen desarrollo docs/_generated/en.gitignore: un colaborador nuevo podría no entender por qué faltan archivos- El script depende de
git: si el build se ejecuta fuera de un repositorio (poco probable), fallaría la sección de novedades
Neutrales¶
- El nav de
mkdocs.ymlse mantiene manual: la curaduría de qué páginas aparecen en el menú es una decisión editorial, no automatizable - Los resúmenes de proyectos en el index siguen siendo editoriales: el script genera la estructura de las cards, pero el texto descriptivo viene de
sc_config.yml
Métricas de éxito¶
| Indicador | Antes | Objetivo |
|---|---|---|
| Archivos tocados al añadir proyecto | 4 | 2 |
| Avisos de metadatos | 1219 | < 400 |
| Contadores desactualizados | Frecuente | Nunca (por diseño) |
| Tiempo para añadir proyecto | ~10 min | ~3 min |