Módulo 6: Rollback And Safety Nets

4. Branch protection como control

Descripción

Todo lo que construiste hasta ahora asume algo que ningún archivo YAML garantiza por sí solo: que un cambio a main siempre pasa primero por un Pull Request, y que ci.yml corre antes de que ese PR pueda fusionarse. Sin nada más, eso es solo una convención — cualquier persona con permiso de escritura sobre el repositorio podría, técnicamente, hacer git push directo a main, saltándose ci.yml por completo. Esta lección enseña el control que convierte esa convención en una regla imposible de saltarse: branch protection, configurado directamente en GitHub, no en ningún workflow.

Conexión con el módulo

Las lecciones 2 y 3 asumieron, sin decirlo explícitamente, que todo cambio pasa por un Pull Request revisado. Esta lección hace esa suposición explícita y la convierte en un control real: branch protection es la pieza que obliga a que sea así, siempre, sin depender de que cada persona del equipo recuerde seguir el proceso. La lección 5 retoma el incidente de Claude Code con esta pieza ya en la mano, evaluando si branch protection —combinada con lo que ya construiste— hubiera cambiado algo.


Analogía: la puerta con control de acceso a la sala de servidores

Piensa en una sala de servidores real. Cualquier empleado de la empresa podría, en teoría, caminar hasta la puerta —nada en el edificio se lo impide físicamente—. Lo que hace que esa sala sea segura no es que todos los empleados sean confiables (aunque lo sean): es que la puerta tiene una cerradura con control de acceso, que solo abre para tarjetas autorizadas, y que registra quién entró y cuándo. La confianza no depende de la buena voluntad de cada persona — depende de un mecanismo que hace la mala decisión, o el error, físicamente imposible sin pasar por el control.

main, sin branch protection, es la sala de servidores sin cerradura: ci.yml puede ser el mejor pipeline del mundo, pero si alguien puede hacer git push directo a main sin pasar por él, el pipeline entero es una recomendación, no un control. Branch protection es la cerradura: convierte "se supone que todo pasa por un PR" en "no hay forma técnica de que no pase por un PR".


Qué es, con precisión

Branch protection es un conjunto de reglas que GitHub aplica sobre una rama específica de un repositorio —típicamente main—, configuradas en Settings → Branches, no en ningún archivo del repositorio. Las dos reglas más relevantes para el pipeline que construiste en esta guía:

  • Require a pull request before merging — nadie puede hacer push directo a la rama protegida. Todo cambio tiene que llegar a través de un Pull Request, sin excepción, sin importar el permiso que tenga esa persona sobre el repositorio (salvo, opcionalmente, administradores, que pueden eximirse explícitamente — una decisión de configuración, no un valor por defecto).
  • Require status checks to pass before merging — el botón "Merge" de un Pull Request queda deshabilitado hasta que los checks que elijas —en este caso, el job terraform-checks de ci.yml— terminen en verde. Si ci.yml falla, GitHub no permite fusionar, sin importar que la persona que abrió el PR quiera hacerlo de todas formas.

Juntas, estas dos reglas son la pieza que le faltaba a todo lo que construiste: ci.yml puede calcular el plan más completo del mundo, pero sin branch protection, nada obliga a que ese cálculo ocurra antes de que el cambio llegue a main.


El camino de clics exacto (representativo)

Esto es configuración de repositorio, no un workflow — no existe ninguna forma de que act la ejecute, sea cual sea el YAML que escribas. Lo que sigue es el camino real, documentado, tal como se ve hoy en GitHub.com:

   1. Repositorio → Settings → Branches
                        │
                        ▼
   2. "Branch protection rules" → Add rule
                        │
                        ▼
   3. Branch name pattern: main
                        │
                        ▼
   4. ☑ Require a pull request before merging
        └─ opcional: exigir un número mínimo de aprobaciones (ej. 1)
                        │
                        ▼
   5. ☑ Require status checks to pass before merging
        └─ buscar y seleccionar: terraform-checks (el job de ci.yml)
        └─ opcional: ☑ Require branches to be up to date before merging
                        │
                        ▼
   6. Create (o Save changes)

Desde ese momento, cualquier intento de git push directo a main —sin pasar por un Pull Request— es rechazado por GitHub, con un mensaje explícito indicando que la rama está protegida. Y cualquier Pull Request cuyo check terraform-checks no haya terminado en verde muestra el botón de fusión deshabilitado, con una etiqueta indicando qué check falta o falló.

Por qué el nombre del check tiene que coincidir exactamente: GitHub identifica un status check por el nombre del job, no del workflow completo — en ci.yml, ese nombre es terraform-checks (el identificador que declaraste bajo jobs: desde el Módulo 3). Si renombraras ese job sin actualizar la regla de branch protection, GitHub seguiría esperando un check que ya no existe, y ningún PR podría fusionarse nunca — un error de configuración real y común, nombrado explícitamente en la sección de errores de esta lección.


Por qué esto es exactamente lo que le faltaba al pipeline

Vale la pena conectar esto, con precisión, con cada pieza que ya construiste:

Sin branch protectionCon branch protection
ci.yml corre en cada PR, pero nada impide fusionar un PR con ci.yml en rojoEl botón de fusión queda deshabilitado hasta que terraform-checks termine en verde
Alguien puede hacer git push directo a main, sin PR, sin revisiónGitHub rechaza cualquier push directo a la rama protegida
apply.yml (disparado por push a main) podría correr sobre un cambio que nadie revisóapply.yml solo puede dispararse por un push que, gracias a branch protection, siempre llegó a través de un PR fusionado

Este último punto merece subrayarse: apply.yml en sí mismo no verifica que el push que lo disparó vino de un PR revisado —el disparador on: push: branches: [main] (Módulo 5, lección 2) reacciona a cualquier push a esa rama, sin distinguir su origen—. Es branch protection, no apply.yml, la que garantiza que la única forma de producir ese push es fusionando un Pull Request que ya pasó por ci.yml. Sin branch protection, todo el diseño cuidadoso de ci.yml/apply.yml sería, técnicamente, saltable.


Errores comunes

Pensar que branch protection es un archivo del repositorio, como ci.yml (conceptual, el error central de esta lección). Qué pasa: alguien busca un archivo .github/branch-protection.yml o similar dentro de andes-cargo-infra/. Cómo detectarlo: si buscas branch protection dentro del código del repositorio en vez de en Settings de GitHub.com. Cómo corregirlo: branch protection vive en la configuración del repositorio en GitHub, gestionada vía la interfaz web (o la API de GitHub, fuera del alcance de esta guía) — nunca como un archivo versionado dentro del propio repositorio.

Configurar el nombre del check incorrectamente (de configuración, muy común en la práctica real). Qué pasa: alguien escribe el nombre del workflow (ci) en vez del nombre del job (terraform-checks) al seleccionar el status check requerido, y GitHub nunca encuentra un check con ese nombre exacto — el PR queda bloqueado para siempre, sin ningún check "pendiente" que algún día se resuelva. Cómo detectarlo: si el botón de fusión sigue deshabilitado incluso después de que ci.yml terminó en verde, revisa el nombre exacto configurado en Settings → Branches contra el jobs: de ci.yml. Cómo corregirlo: el nombre requerido tiene que coincidir con el identificador del job (terraform-checks), no con el nombre del archivo ni con el name: del workflow completo.

Asumir que branch protection reemplaza al guardrail de la lección 7 (de alcance, revisita la lección 1). Qué pasa: alguien piensa que, con branch protection activa, ya no hace falta ningún control adicional sobre el contenido del plan. Cómo corregirlo: branch protection garantiza el proceso (PR revisado, checks en verde) — no mira si el plan mismo es peligroso. Un PR perfectamente revisado y aprobado, con ci.yml en verde, todavía podría contener un cambio que destruye Shipments, si nadie se dio cuenta al leer el plan. El guardrail de la lección 7 es la capa que sí mira el contenido; branch protection es la capa que garantiza que ese contenido pasó por revisión antes de llegar a main. Son complementarias, no sustitutas.


Ejercicios

Ejercicio 1 — Explica por qué act no puede ejecutar esta lección, en tus propias palabras. A un colega que pregunta "¿por qué no lo probamos con act, como todo lo demás?".

Ver solución

Una respuesta completa suena, más o menos, así: "act simula la ejecución de workflows —archivos YAML dentro de .github/workflows/—, corriendo sus jobs en contenedores Docker locales. Branch protection no es un workflow: es una configuración que vive en los servidores de GitHub.com, aplicada sobre cómo se puede interactuar con una rama del repositorio (quién puede hacer push, qué checks son obligatorios antes de fusionar). No hay ningún YAML que act pueda leer o ejecutar para esto — la única forma de verla en acción es tener un repositorio real en GitHub.com, con esta configuración activada, e intentar fusionar (o hacer push directo) contra ella."

Ejercicio 2 — Diagnostica un PR bloqueado. Un colega configura branch protection, pero después de que ci.yml termina en verde, el botón de fusión sigue deshabilitado, mostrando "Expected — Waiting for status to be reported". ¿Qué error de configuración es más probable, según esta lección?

Ver solución

Lo más probable es que el nombre del status check requerido, configurado en Settings → Branches, no coincida exactamente con el identificador del job dentro de ci.yml (terraform-checks) — quizás se escribió el nombre del workflow (ci) en su lugar, o se escribió con una mayúscula distinta, o un espacio de más. GitHub sigue "esperando" un check con el nombre exacto que configuraste, y como ningún job produce ese nombre exacto, el estado nunca se resuelve, sin importar cuántas veces corra ci.yml con éxito.

Ejercicio 3 — Diseña la política de Andes Cargo. Basándote en todo lo que aprendiste en este módulo hasta ahora, escribe, en dos o tres frases, qué configuración exacta de branch protection recomendarías para el repositorio real de Andes Cargo, y por qué.

Ver solución

Una recomendación razonable: activar "Require a pull request before merging" con al menos una aprobación requerida (para que la revisión humana del plan, establecida desde el Módulo 3, sea obligatoria y no solo una buena práctica); activar "Require status checks to pass before merging", exigiendo específicamente el check terraform-checks; y considerar "Require branches to be up to date before merging" para que ningún PR se fusione con un plan calculado sobre una versión vieja de main — exactamente el mismo tipo de riesgo de inconsistencia que el Módulo 5 (lección 5) ya nombró para el state, ahora aplicado a la rama misma.


Resumen y siguiente paso

En esta lección aprendiste branch protection: la configuración de repositorio —no un workflow— que convierte "se supone que todo pasa por un PR revisado" en una regla técnica imposible de saltar, con dos reglas centrales (PR obligatorio, checks obligatorios antes de fusionar) y el camino de clics exacto para activarlas. Confirmaste, con precisión, por qué esta capa es necesaria incluso con ci.yml/apply.yml ya construidos: sin ella, nada impide que un push directo a main dispare apply.yml sobre un cambio que nadie revisó.

Antes de avanzar deberías poder: explicar la diferencia entre las dos reglas centrales de branch protection; diagnosticar un PR bloqueado por un nombre de check mal configurado; y articular por qué branch protection y el guardrail de la lección 7 son capas complementarias, no intercambiables.

La lección 5 usa esta pieza —junto con todo lo demás del módulo— para responder, con la profundidad que el Módulo 1 dejó pendiente, la pregunta central de esta guía: ¿un pipeline hubiera detenido el incidente de Claude Code?

Recursos

  1. GitHub Docs — About protected branches — documentación oficial completa de branch protection, incluidas ambas reglas de esta lección.
  2. GitHub Docs — Managing a branch protection rule — el camino de clics exacto, paso a paso, con capturas oficiales.
  3. Módulo 5 de esta guía (02-from-plan-to-apply-the-merge-trigger.md) — el disparador on: push: branches: [main] que branch protection termina de asegurar.
  4. Módulo 3 de esta guía (02-the-hashicorp-github-pattern.md) — el patrón plan en PR / apply en merge que branch protection convierte en una regla técnica, no solo una convención.