GitHub Actions Workflow Lockfiles: por qué fijar uses@SHA no basta
GitHub Actions Workflow Lockfiles: por qué fijar uses@SHA no basta
Fijar una Action de GitHub a un SHA de commit completo inmoviliza el código en ese punto de entrada. Pero ¿qué ocurre si esa Composite Action ejecuta a su vez example-org/upload@v1? El SHA externo no cambia, mientras que la etiqueta interna puede apuntar a otro commit. El YAML del workflow, por sí solo, no revela todo el grafo de dependencias de terceros que se ejecutará.
GitHub presentó los Workflow Lockfiles en vista previa pública. El archivo .github/workflows/actions.lock registra las dependencias directas y transitivas de Actions, sus commits y la identidad del repositorio de origen.
¿Ya tienes todos los uses@SHA fijados? Inspecciona el grafo antes de decidir la migración. El valor añadido no consiste en fijar dos veces el mismo SHA, sino en revisar dependencias transitivas e identidades de repositorio.
| Situación actual del CI | Primera decisión | Motivo |
|---|---|---|
| Pocas Actions directas, todas con SHA completo | Inventariar; posponer una migración total | Puede que el coste de mantenimiento supere el beneficio |
Composite Actions que llaman a @v1 o @main | Probar con un workflow no crítico | Se cubren dependencias transitivas |
| Repositorios que comparten un Reusable Workflow | Revisar responsabilidades del llamador y del repositorio que lo define | El segundo controla sus Actions internas |
| La organización aplica Allowed Actions o reglas de SHA | Revisar la política antes de migrar | Los SHA bloqueados pueden no coincidir con reglas basadas en etiquetas |
Las situaciones pueden darse al mismo tiempo. Esta guía se basa en documentación oficial y código fijado de gh-actions-lock v0.2.0.
Un SHA fijo en la entrada no fija todo el grafo
Imaginemos que ci.yml usa example-org/build con SHA completo, pero el action.yml de ese Action llama a example-org/upload@v1. La Action exterior es estable; la referencia interior continúa siendo mutable.
| Tipo de dependencia | uses@SHA directo | Workflow Lockfile |
|---|---|---|
| Action externa usada directamente | Fija esa referencia | Guarda commit e identidad del repositorio |
| Action invocada dentro de una Composite Action | No fija automáticamente la etiqueta secundaria | Incluye Actions transitivas |
| Reusable Workflow de otro repositorio | Fija la llamada | Registra la llamada; las Actions internas son responsabilidad del repositorio que lo define |
Action propia con uses: $/… | Sigue el commit del repositorio en ejecución | Ya está fijada de forma inherente |
Descargas desde run: o etiquetas de imagen mutables | Fuera del alcance | El Lockfile tampoco las protege por sí solo |
La distinción del Reusable Workflow externo es esencial. Según la documentación conceptual de GitHub, el Lockfile de un repositorio cubre los workflows que ese repositorio define. Las Actions dentro de un Reusable Workflow externo quedan bajo el Lockfile del repositorio que define dicho workflow. Fijar la línea uses del llamador no equivale a haber auditado las Actions internas del repositorio compartido.
Qué hay realmente dentro de actions.lock
workflows relaciona la ruta de cada workflow con sus claves de pin. En dependencies aparecen ref, commit, owner_id, repo_id y, para llamadas internas, uses. El siguiente ejemplo es la fixture oficial completa benchV003 para probar el parser del esquema v0.0.3, obtenida del commit fijo del repositorio de formato. Los SHA repetidos con a/b son valores sintéticos de prueba, no un resultado de un Runner de producción.
version: v0.0.3
workflows:
.github/workflows/ci.yml:
- actions/checkout@v4
- actions/setup-go@v5
- actions/cache@v4
dependencies:
actions/checkout@v4:
hostname: github.com
ref: v4
commit: sha1-11bd71901bbe5b1630ceea73d27597364c9af683
owner_id: 44036562
repo_id: 197814629
actions/setup-go@v5:
hostname: github.com
ref: v5
commit: sha1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
owner_id: 44036562
repo_id: 249058325
actions/cache@v4:
hostname: github.com
ref: v4
commit: sha1-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
owner_id: 44036562
repo_id: 251882839
uses:
- actions/checkout@v4
La fixture incluye hostname: github.com, aunque un Lockfile generado para el host predeterminado github.com puede omitir ese campo opcional. La lista de Pins de workflows y el campo uses dentro de una dependencia muestran relaciones distintas.
El código fijado de state_marshal.go también serializa estos campos:
a := file.Dependencies[k]
entry := &yaml.Node{Kind: yaml.MappingNode}
// se omite el tratamiento de hostname
if a.Ref != "" {
addQuotedField(entry, "ref", a.Ref)
}
if a.Commit != "" {
addQuotedField(entry, "commit", a.Commit)
}
addIntField(entry, "owner_id", a.OwnerID)
addIntField(entry, "repo_id", a.RepoID)
Un commit identifica un objeto Git; los identificadores numéricos de propietario y repositorio ayudan a reconocer qué repositorio se oculta tras un nombre legible. El ejemplo antiguo en la documentación conceptual muestra version: v0.0.2, mientras que la versión v0.2.0 y el README del formato corresponden al esquema v0.0.3. La versión de la CLI y la del esquema son cosas distintas.
Revisar el diff antes de activar una política obligatoria
Empezaría en una rama de pruebas con un workflow no crítico y fácil de revertir. Estos comandos suponen que GitHub CLI está instalado, que la sesión está autenticada y que existe permiso de lectura en los repositorios afectados.
# Instalar la versión fija de la extensión revisada
gh extension install github/gh-actions-lock --pin v0.2.0
gh actions-lock .github/workflows/ci.yml
# Revisar workflow, Actions locales y archivos nuevos
git status --short
git diff
cat .github/workflows/actions.lock
# Verificar solo el workflow piloto
gh actions-lock --verify .github/workflows/ci.yml
La verificación limitada a una ruta está respaldada por el código: root.go acepta parámetros de ruta, y verify.go y los tests mantienen ese alcance. El actions.lock compartido debe tener un formato válido, pero este comando no incorpora automáticamente otros workflows.
La extensión puede modificar más que el archivo de bloqueo: una referencia de versión puede afinarse a una etiqueta concreta y una Action local válida puede pasar de ./actions/build a $/actions/build:
steps:
- - uses: ./actions/build
+ - uses: $/actions/build
$/ vincula la Action al commit del repositorio que se está ejecutando. Una ruta local incorrecta no se corrige mágicamente. Además, un archivo nuevo sin seguimiento no aparece en git diff; revisa también git status --short y el contenido de actions.lock.
La política Allowed Actions puede causar un conflicto adicional. Cuando un workflow utiliza Lockfile, las reglas aplicables pueden comprobar el SHA del commit bloqueado; una entrada anterior como actions/checkout@v6 podría dejar de coincidir. Hay que ajustar la lista de permisos al commit aprobado, no abrir toda la organización a cualquier Action externa.
Hay dos modos de verificación con alcance diferente:
gh actions-lock --verify-local .github/workflows/ci.yml
gh actions-lock --verify .github/workflows/ci.yml
--verify-local comprueba sin red que las referencias estén cubiertas. No valida la identidad real del repositorio remoto. --verify sí realiza verificaciones en línea, por lo que resulta apropiado como condición del PR. Cuando todos los workflows estén incorporados, puede usarse gh actions-lock --verify sin ruta para comprobar todo el repositorio. Durante una prueba limitada, ese comando global puede fallar por workflows aún sin incorporar o Pins obsoletos.
Para diagnosticar, usa --json=valid,findings. El código de salida 1 indica hallazgos bloqueantes; el 2, un error de comando, E/S o red. Las categorías del código fijado incluyen not-pinned, stale, local-action, repo-replaced y unreachable-pin.
Un piloto reversible de cinco pasos:
- Inventariar: enumerar
usesdirectos, Composite Actions anidadas y Reusable Workflows externos. Confirmar quién mantiene los Pins internos en el repositorio del workflow invocado. - Generar en una rama de prueba con la versión fijada; comparar el workflow, los
action.ymllocales yactions.lock. - Contrastar la política Allowed Actions con los SHA bloqueados y revisar todos los cambios mediante un PR ordinario.
- Verificar en dos niveles: cobertura con
--verify-localy confianza en línea con--verify. Paranot-pinned,staleolocal-action, volver a las rutas y al diff; anterepo-replacedounreachable-pin, investigar el historial antes de fusionar. - Conservar la salida: si la política o el parser bloquean CI, deshacer la rama de pruebas. Ampliar el despliegue solo cuando el piloto funcione de forma estable.
Actualizaciones posteriores: sticky pins y release cooldown
El commit elegido en el alta inicial depende de uses y de su resolución. En v0.2.0 el release cooldown no impone siete días por defecto. Primero se consulta un cooldown.default-days positivo del ecosistema github-actions en .github/dependabot.yml; después se recurre al cooldown_days configurado por el usuario. Sin ninguna configuración no se filtran versiones por antigüedad: una etiqueta publicada en los últimos tres días solo genera un aviso fresh-tag que no bloquea. La resolución de configuración y el aviso están en el código fijado. Conviene comprobar los campos ref, commit y el diff generado.
Los Pins existentes son sticky. Volver a ejecutar gh actions-lock no sustituye silenciosamente el commit cuando cambian main o v3 en el origen. Para aceptar una actualización de forma deliberada, utiliza gh actions-lock --relock y revisa el diff. Si una reescritura del historial vuelve inalcanzable el commit previo, --relock --accept-moved implica otra decisión de confianza. No es un arreglo universal para dejar CI en verde.
Dependabot puede mantener el Lockfile de workflows ya incorporados, pero no registra por sí solo un workflow nuevo. La guía de aplicación de GitHub recomienda una prueba pequeña y revisión por CODEOWNERS antes de activar Require lockfile en toda la organización. También hay que comprobar el plan y los permisos. Según el README fijado, GitHub Enterprise Server (GHES) no admite esta vista previa; no debe confundirse con Enterprise Cloud.
El Lockfile indica qué código de Action se pretende ejecutar, no si ese código es seguro. Las descargas en run:, las imágenes sin digest, los paquetes externos y los permisos del token de CI necesitan controles propios. Un piloto útil debería permitir responder: ¿qué Actions internas han quedado fijadas?, ¿quién revisó sus IDs de repositorio? y ¿quién mantiene las dependencias del Reusable Workflow compartido? La diferencia entre detectar un riesgo y bloquear la fusión se aborda también en el análisis de GitHub AI Scan.
Nota de fuentes y fecha: GitHub anunció Workflow Lockfiles Public Preview el 14 de octubre de 2026. Se revisaron los commits fijados d5dca22a06ff8590d50b41612038996800cfb336 (gh-actions-lock v0.2.0) y ac158834765237ee94c36606cfc7d87316aa49ab (formato) el 18 de octubre. El YAML es una fixture oficial con hashes sintéticos. No se ejecutaron una migración CLI, pruebas reales de Runner ni experimentos de cambio de tags. Las capacidades dependen del plan y de los ajustes de cuenta.