Ingeniería

Un check de changelog para GitHub Actions

6 min de lectura

Todo equipo que mantiene un changelog a mano ha tenido la misma conversación tras el mismo incidente: un lanzamiento salió sin entrada, alguien pregunta por qué, y la respuesta honesta es que la persona que la habría escrito iba con prisa y el paso del changelog solo vivía en la memoria. Automatización de changelog cubre qué puede automatizar con seguridad una pipeline y qué todavía necesita a una persona; un check de changelog en CI es la otra mitad de este problema, porque automatizar la escritura no ayuda si nadie está obligado a activarla en primer lugar. GitHub Actions es donde la mayoría de los equipos ya ejecutan sus checks de pull request, así que ahí es también donde vive este.

¿Por qué falla “le pedimos a la gente que añada una entrada” con un patrón predecible?

Porque compite por atención con todo lo demás en un pull request, y es la única parte sin consecuencia inmediata por saltársela. Los tests fallan a gritos y bloquean el merge. Una entrada de changelog faltante no bloquea nada, así que pierde en cuanto alguien tiene prisa, que en la práctica es casi siempre. Una política impuesta por la memoria se degrada exactamente al ritmo que cabría esperar: bien las primeras semanas tras acordarla, luego abandonada en silencio en cuanto la persona a quien le importaba se va de vacaciones o cambia de equipo.

¿Qué verifica en realidad un check de CI para una entrada de changelog?

No la calidad de la redacción, solo que una entrada existe y está bien formada, que es el alcance correcto para un check de changelog que corre en CI en vez de en la cabeza de una persona. Una forma habitual: el check mira el diff del PR y exige o bien un archivo nuevo en un directorio de changesets (el patrón que usan Changesets y herramientas similares) o una línea modificada en un archivo de changelog, y falla el build si no existe ninguno de los dos. La revisión de qué dice realmente la entrada sigue pasando donde siempre pasó, en el code review, porque ese juicio no pertenece a un script.

Qué verifica el check de CIQué no verifica
Existe un changeset o una línea de changelog en el diffSi la redacción es clara
La entrada referencia el paquete correcto, en un monorepoSi el cambio merece siquiera una entrada
El archivo es sintácticamente válido (frontmatter, forma JSON)Si la entrada es honesta sobre el impacto

¿Necesita uno cada PR, o hay cambios exentos?

Algunos están exentos, y la lista de excepciones es donde estos sistemas realmente se construyen o se abandonan. Un bump de dependencia sin efecto visible, un cambio solo de tests, un refactor interno sin cambio de comportamiento: ninguno debería obligar a quien contribuye a inventar una entrada de changelog para algo que a nadie que lea el changelog le importa. El patrón que funciona es una etiqueta o flag que quien contribuye puede aplicar (no-changelog-needed) y que satisface el check de CI sin archivo, revisada por quien apruebe el PR, de modo que la propia excepción pasa por el mismo escrutinio que pasaría una entrada.

¿Qué pasa con las excepciones legítimas, como un hotfix urgente?

El gate pertenece al merge, no al deploy: un hotfix bajo presión real de tiempo puede mergear con una entrada de marcador de posición o un ticket de seguimiento, siempre que el check de CI se satisfaga con la intención en vez de solo con un párrafo terminado; algunos equipos aceptan un stub de una línea que una mantenedora pule antes del siguiente corte de lanzamiento. Lo que el gate nunca debería permitir es saltarse el paso en silencio, porque un stub que se olvida es un fallo menor que una entrada que nunca existió, y un stub al menos deja un rastro que alguien puede encontrar después.

# .github/workflows/changelog-check.yml
on:
  pull_request:
    types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
  changelog:
    if: >-
      !contains(github.event.pull_request.labels.*.name,
      'no-changelog-needed')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # el diff necesita la rama base
      - name: Require changelog entry
        run: |
          base="origin/${{ github.base_ref }}"
          if ! git diff --name-only "$base"...HEAD \
              | grep -q '^\.changeset/'; then
            echo "No changeset. Add one, or have a maintainer"
            echo "apply the no-changelog-needed label."
            exit 1
          fi

¿Cómo se sabe que el check en sí es correcto antes de que empiece a bloquear PRs reales?

Abre primero un pull request de prueba contra una rama desechable: uno con un changeset, uno sin él, y uno con la etiqueta de excepción, y confirma que los tres obtienen el resultado esperado antes de que el check aplique al trabajo de cualquier otra persona. Un check de changelog que falla abierto, aprobando todos los PRs porque una condición se escribió al revés, es peor que no tener ningún check, porque parece cobertura que en realidad no existe. workflow_dispatch sobre el mismo archivo, ejecutado a mano contra un par de PRs recientes ya mergeados, atrapa la mayoría de estos errores sin necesitar un pull request en vivo.

¿La misma idea funciona fuera de GitHub Actions?

La forma se traslada igual, solo cambia la sintaxis. GitLab CI expresa la misma regla como un bloque rules de un job que comprueba $CI_MERGE_REQUEST_LABELS en vez de un if de GitHub Actions, y una aprobación obligatoria del merge request puede sustituir al paso de revisión de la excepción. El check que describe este artículo es de GitHub Actions porque esa es la plataforma en la que ya está la mayoría de los equipos que lo leen, pero el requisito de fondo, un gate verificado por máquina en vez de una convención pedida de palabra, es el mismo en cualquier sitio donde CI corra antes de un merge.

¿Funciona igual en un monorepo?

Necesita una pieza más: para qué paquete es la entrada. Changelogs de monorepo cubre por qué un único archivo para todo el repo deja de funcionar en cuanto los paquetes se lanzan de forma independiente; el check de CI hereda ese mismo requisito; un changeset que no nombra un paquete no es evidencia útil de que el changelog correcto se vaya a actualizar, solo de que algún archivo cambió en algún lugar del diff. Las herramientas construidas para esto (Changesets es la habitual en el ecosistema de JavaScript) piden a quien contribuye elegir el paquete afectado y un bump de semver en el mismo momento en que se crea el changeset, así que el check de CI obtiene ambas piezas gratis en vez de inferirlas después.

FAQ

¿El check de CI debería bloquear el merge, o solo avisar? Bloquear. Un aviso es funcionalmente idéntico a pedirlo por favor, que es justo lo que ya falló. La etiqueta de excepción existe precisamente para que un caso genuino de solo-aviso tenga igual un camino legítimo a través del mismo gate estricto.

¿Quién revisa si una etiqueta de excepción se aplicó correctamente? Quien apruebe el pull request, como parte de la revisión que ya está haciendo de todos modos. La etiqueta nunca debería auto-aplicarse sin revisión, o se convierte en el mismo atajo silencioso que el gate estaba pensado para cerrar.

¿Exigir esto en CI reemplaza la necesidad de una pipeline de automatización de changelog? No, la alimenta. Automatización de changelog cubre convertir entradas estructuradas en una página, un feed y un correo; el check de CI es lo que garantiza que esas entradas estructuradas existan para automatizarlas en primer lugar.

¿Cuál es la versión más pequeña de esto que vale la pena construir primero? Un único check que falla si no cambió ningún archivo bajo un directorio de changelog designado, con una etiqueta de excepción. El enrutado por paquete y la inferencia de semver para un monorepo pueden llegar después; el hábito central, una entrada existe o alguien dijo explícitamente que no hace falta, es lo que vale la pena tener desde el primer día.


Las afirmaciones técnicas de este artículo no se han revisado de forma independiente. Si algo está mal, avísanos y lo corregiremos.

Relacionado en changeloop: Herramientas de changelog comparadas, Generador de changelog

changeloop
El equipo que crea un changelog que cierra el círculo. Tus usuarios lo piden, tu equipo lo publica y quien lo pidió se entera.