Saltar a contenido

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.snippets ya está instalado con mkdocs-material
  • Contadores siempre actualizados: el build refleja el estado real del repositorio
  • Añadir un proyecto baja de 4 archivos a 2 (.md nuevo + 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.py antes de properdocs serve en 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.yml se 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