Guía9 min

git verify-tag para coding agents: ningún release sin firma válida

Resumen

git verify-tag valida la firma GPG de un tag anotado sin mover HEAD ni tocar el working tree. Solo opera sobre tag objects: un tag liviano o sin firma falla con exit distinto de cero. Con -v, --raw y --format controlas el detalle del reporte. Cero construir releases sobre tags no verificados, cero confundir un tag liviano con un release firmado y cero volcar el reporte crudo al contexto del agente. Git 2.50.1.

GitHub
Escudo de verificación sobre un tag de release: verify-tag valida la firma GPG del tag anotado sin mover HEAD

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

git verify-tag valida la firma GPG creada por git tag en los tag objects que le pasas, sin mover HEAD, sin tocar el índice y sin tocar el working tree. El man (git-verify-tag(1); git-scm.com/docs/git-verify-tag; man local Git 2.54.0, 2026-04-19; binario Git 2.50.1 / Apple Git-155): Validates the gpg signature created by git tag in the tag objects listed on the command line. SYNOPSIS: git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>....

Es la compuerta de release del plumbing: antes de que un agente construya, despliegue o empaquete a partir de un tag que otro agente (o humano) dejó en el repo, verifica que ese tag lo firmó quien dice haberlo firmado. Lectura pura, cero efectos.

No es verify-commit. verify-commit valida la firma de un commit (git commit -S); verify-tag valida la firma de un tag anotado (git tag -s). Son complementos: el commit dice quién escribió el cambio, el tag dice quién autorizó el release. Verificar uno no verifica el otro.

Tampoco es tag. tag crea y lista; verify-tag solo valida y responde con exit code. Ni es describe: describe encuentra el tag anotado más cercano para nombrar un commit; verify-tag comprueba que ese tag sea auténtico. Ni es show: show imprime el objeto para inspeccionarlo; verify-tag lo valida. En todos rige la misma higiene: cero volcar la salida cruda al contexto del LLM.

Contrato: git verify-tag <tag>. Exit code 0 = firma válida. Solo tag objects: liviano o sin firma = fallo. Cero construir sobre tags no verificados. Cero confundir "existe el tag" con "el tag es auténtico".

La regla de oro: solo tag objects

verify-tag solo sabe validar tag objects, es decir, tags anotados (y firmados). Todo lo demás falla con exit distinto de cero. Comportamiento verificado en local (Git 2.50.1):

Lo que le pasasResultadoExit
Tag anotado firmado, firma válidaFirma válida0
Tag anotado sin firmarerror: no signature found1
Tag liviano (apunta directo a un commit)error: <tag>: cannot verify a non-tag object of type commit.1
Ref que no existeerror: tag '<tag>' not found.1

Tres consecuencias prácticas para un agente:

  1. Un tag liviano nunca es un release verificable. Si tu flujo de release acepta tags livianos, la compuerta de firma no existe. Estandariza git tag -s (o -u <key-id>) para releases y deja los livianos para marcadores locales desechables.
  2. no signature found no es "tag malo", es "tag sin firmar". Distingue el caso: un tag anotado sin firma puede ser legítimo en repos que no firman; la política (exigir firma o no) la pone tu compuerta, no git.
  3. Verifica el nombre exacto que vas a consumir. Un typo no da un falso positivo: da tag not found y exit 1. El contrato es fail-closed.

Opciones: -v, --raw, --format

Flujo de verificación de un tag: del nombre del tag a la firma GPG y al exit code como contrato

El SYNOPSIS completo es git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>.... Nótese el ...: acepta varios tags en una invocación y los valida uno por uno. Si cualquiera falla, el exit es distinto de cero.

  • -v, --verbose: imprime el contenido del tag object antes de validarlo. Útil para depurar a mano (ves tagger, fecha y mensaje junto al resultado). Para un agente, úsalo solo en diagnóstico: en el camino feliz, el exit code basta.
  • --raw: imprime el estado GPG crudo a standard error en vez de la salida legible. Ojo al detalle: va a stderr, no a stdout. Si tu harness captura solo stdout, el reporte crudo se pierde (o contamina logs). Y nunca lo pegues al prompt: es GPG crudo, ruido caro para el contexto.
  • --format=<format>: formatea la salida con placeholders del tag. Sirve para reportes máquina-legibles (tagger + fecha en una línea) sin parsear texto humano.

Regla de contexto para agentes: el exit code es el contrato; el texto es solo para humanos depurando. Un if git verify-tag "$TAG" decide; nada del stdout/stderr necesita entrar al prompt.

Receta: compuerta de release para un agente

Decisión de release: solo el tag anotado con firma válida avanza a build y deploy

Así se ve la compuerta mínima antes de construir un release desde un tag:

TAG="v2.4.0"

git fetch origin "refs/tags/${TAG}:refs/tags/${TAG}"

if git verify-tag "$TAG" 2>/dev/null; then
  echo "firma válida, construyendo release"
else
  echo "tag no verificado, alto" >&2
  exit 1
fi

Notas de la receta:

  • El fetch trae el tag exacto del remoto; verificar un tag local desactualizado es verificar el release equivocado.
  • 2>/dev/null descarta el reporte humano; la decisión vive en el exit code.
  • El exit 1 es fail-closed: sin firma válida no hay build, no hay deploy, no hay excepción silenciosa.
  • Si tu política exige además que el commit esté firmado, encadena verify-commit sobre el SHA al que apunta el tag. Tag válido + commit válido = procedencia completa.

Checklist

  • Los releases usan tags anotados firmados (git tag -s), nunca livianos.
  • La compuerta corre git verify-tag <tag> y decide por exit code, no por parsear texto.
  • El tag se trae del remoto (fetch refs/tags/...) antes de verificar.
  • --raw y -v solo en diagnóstico humano; jamás al contexto del LLM.
  • no signature found y non-tag object se tratan como fallo fail-closed, no como warnings.
  • Si la política lo exige, se verifica también el commit con verify-commit.

FAQ

¿verify-tag verifica también el commit al que apunta el tag? No. Valida la firma del tag object. El commit apuntado puede no estar firmado. Para procedencia completa, verifica ambos: tag con verify-tag y commit con verify-commit.

¿Un tag liviano firmado existe? No hay tal cosa: la firma vive en el tag object y el tag liviano no crea objeto, es solo una ref a un commit. Por eso verify-tag responde cannot verify a non-tag object of type commit. Si necesitas firmarlo, conviértelo en anotado.

¿Por qué --raw va a stderr? Diseño de git: la salida legible y la cruda son reportes para humanos, no datos. Tu harness debe decidir por exit code; si necesitas el detalle máquina-legible, usa --format.

¿Puedo verificar varios tags a la vez? Sí: git verify-tag v1 v2 v3 los valida en orden. Basta un fallo para exit distinto de cero. Para reportes por tag individual, prefiera un loop que registre cuál falló.

¿Firma válida significa firmante confiable? No. Significa que el tag lo firmó alguien con esa key. La confianza (¿esa key pertenece a quien debe autorizar releases?) la pone tu política: restringe qué key-ids acepta la compuerta y rota las comprometidas.