| Ámbito | Quién lo ve | Cuándo usarlo |
|---|---|---|
| Repositorio | Cualquier job de cualquier workflow del repo | Tokens sin consecuencias (Codecov, un badge) |
| Environment | Solo los jobs que declaran environment: | Credenciales de despliegue |
| Organización | Los repositorios que la política permita | Registro de paquetes compartido |
La distinción práctica, dicha sin rodeos: un secret de repositorio con permisos de despliegue a producción está al alcance de cualquier workflow que alguien añada al repositorio. El mismo valor guardado en un environment protegido solo se entrega a un job que además ha tenido que pasar por sus reglas.
| Regla | Qué hace |
|---|---|
| Required reviewers | El job se queda en Waiting hasta que alguien aprueba |
| Wait timer | Retraso obligatorio antes de arrancar |
| Deployment branches | Solo ciertas ramas pueden desplegar a este environment |
| Historial | Quién desplegó qué y cuándo, por environment |
La tercera es la más olvidada y la más útil: sin ella, una rama cualquiera puede pedir el
environment production y, con él, sus secretos.
jobs:
deploy-production:
environment: production # ← aquí se para y espera
runs-on: ubuntu-latest
Esa línea es el gate entero. Y no hay un if que la sustituya, porque un
if lo evalúa el propio workflow — y quien puede editar el workflow puede quitar el
if. El environment lo evalúa GitHub antes de entregar los secretos, fuera del
alcance del YAML.
ref.El rollback merece un workflow propio y no un if dentro del de despliegue, por una
razón de diseño de emergencia: el día que lo necesitas no quieres editar YAML. Quieres un
botón, un desplegable con la versión y un actions/checkout con
ref: ${{ inputs.version }}. Todo lo que haya que redactar bajo presión saldrá mal.
| Build-time | Runtime | |
|---|---|---|
| Cuándo se resuelve | Al compilar | Al ejecutar |
| Cambiarla exige | Rebuild y redeploy | Reiniciar el proceso |
| Puede ser secreta | No | Sí |
| Ejemplos | URL pública de la API, feature flags estáticos | Credenciales de BD, API keys |
La regla de oro: si va al bundle del frontend, no puede ser un secreto. Da igual cómo se llame la variable, ni que estuviera guardada en GitHub Secrets, ni que el repositorio sea privado — si el valor termina dentro del JavaScript que descarga el navegador, es público y lo puede leer cualquiera con las herramientas de desarrollo abiertas. Es el error de gestión de secretos más común y también el más caro, porque el secreto queda publicado sin que nadie haga nada visiblemente malo.
AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY como secrets de repositorio:
credenciales de larga vida que no caducan solas, en un capítulo que dedica catorce páginas a
explicar por qué los secretos son peligrosos. GitHub Actions puede autenticarse contra AWS
emitiendo un token de identidad de corta duración por ejecución, sin almacenar ninguna clave
permanente — ¿por qué una guía de este año no lo menciona ni una vez? ¿Y qué quedaría de su
sección sobre rotación de secretos si esas credenciales sencillamente no existieran?
La segunda: el workflow «profesional» de la guía incluye un job de Security Scan
que usa snyk/actions/node@master. ¿Es coherente fijar precisamente la action de
seguridad a una rama que se mueve, en el mismo fichero donde todas las demás van fijadas a
@v4?
Crea los dos environments: staging sin aprobación y production con
required reviewers. Pon environment: production en un job que solo haga
echo, lánzalo y mira el job quedarse en Waiting con tu nombre en el botón.
Y luego lo que de verdad enseña la lección: intenta que ese mismo job lea el secreto de
production sin declarar el environment. Que no puedas es todo el argumento —
y es la respuesta que darías si alguien te pregunta en qué se diferencia de un if.