Dale a tu sitio un endpoint que diga de qué commit salió
Treinta líneas en una ruta de Next.js y la pregunta "¿qué versión está en vivo?" se contesta con un curl y un git rev-parse. Cómo lo armamos en vendear, de dónde sale el commit y por qué la respuesta dice también cómo se publicó.
También en: English
Arreglas algo, lo mergeas, y alguien pregunta si ya está en producción. Si el
sitio se publica solo con cada push a main, nadie corrió el deploy mirando la
pantalla, así que la respuesta suele salir de comparar la hora del último
deploy con la del último commit. Es una aproximación: dos fechas cercanas no
prueban que el build haya salido de ese commit.
En vendear, nuestro sitio de vidrieras con vendedor con IA, esa pregunta hoy se contesta así:
curl -s https://www.vendear.com/api/version
{"commit":"ef66746e2df30970b7968c6e68d0d7066ec92153","branch":"main","source":"vercel-git","env":"production","builtAt":null}
Y se compara contra el repo:
git rev-parse origin/main
# ef66746e2df30970b7968c6e68d0d7066ec92153
Son iguales: lo que está en vivo es main. Esa medición es del 5 de octubre de
2026, y no hay margen de interpretación.
La ruta entera
Es un route handler de Next.js (App Router) en src/app/api/version/route.js:
export const dynamic = "force-dynamic";
export function GET() {
const fromGit = process.env.VERCEL_GIT_COMMIT_SHA || null;
const fromCli = process.env.COMMIT_SHA || null;
return Response.json(
{
commit: fromGit || fromCli,
branch: process.env.VERCEL_GIT_COMMIT_REF || process.env.COMMIT_BRANCH || null,
source: fromGit ? "vercel-git" : fromCli ? "local-cli" : null,
env: process.env.VERCEL_ENV || null,
builtAt: process.env.BUILT_AT || null,
},
{ headers: { "cache-control": "no-store" } }
);
}
No hay más. Lo interesante está en tres decisiones chicas.
1. El commit sale de dos lugares, y la respuesta dice de cuál
Vercel pone VERCEL_GIT_COMMIT_SHA en todo deploy que sale del repositorio
conectado. Es el camino normal y no requiere configurar nada.
Pero a veces se publica a mano desde una máquina (para republicar sin commit
nuevo, o después de un build que falló por algo pasajero), y ahí lo que se sube
es la carpeta de trabajo, no un commit. Vercel construye desde esos archivos,
sin .git, así que adentro del build no hay forma de averiguar el hash: hay
que pasárselo. Nuestro script de deploy lo hace con
vercel --prod --build-env COMMIT_SHA=<hash>, y además define COMMIT_BRANCH
y BUILT_AT.
Los dos dan un hash, pero no significan lo mismo. vercel-git quiere decir "esto
se construyó desde este commit del repo". local-cli quiere decir "esto se
construyó desde el disco de alguien que en ese momento estaba en este commit".
Por eso el campo source va en la respuesta: con el hash solo, los dos casos
se verían iguales.
El script se niega a publicar si la carpeta tiene cambios sin commitear. Si no lo hiciera, el hash estampado describiría un código distinto del que se subió.
Si no hay ninguna de las dos variables, commit y source vuelven null. Un
endpoint de versión que inventa un valor cuando no lo sabe es peor que no
tenerlo.
2. Que no lo guarde ninguna caché
dynamic = "force-dynamic" hace que Next.js resuelva la ruta en cada pedido en
lugar de congelarla en el build, y cache-control: no-store le pide a la CDN y
al navegador que no guarden la respuesta. Sin eso corres el riesgo de que el
endpoint conteste con el commit de un deploy anterior, que es justo el error
que vino a evitar.
3. Un testigo que se lee con un comando
El camino que parecía obvio era leer el commit de los metadatos del deploy con
vercel inspect. El dato se guarda (nuestro script también lo deja ahí con
--meta), pero la salida JSON de la versión de la CLI que usábamos no lo
devolvía. Un chequeo que no se puede leer con un
comando no sirve como chequeo: nadie lo va a correr en un script ni en un CI.
curl más git rev-parse sí se puede automatizar. Si los dos hashes no
coinciden después de un merge, el deploy no tomó tu cambio, sea porque falló,
porque quedó bloqueado o porque sigue construyendo. Y lo sabes antes de que te
lo diga un cliente.
Antes de copiarlo
- El hash queda público. Cualquiera puede ver de qué commit salió el sitio. Sin acceso al repo, eso no le da el código. Si el repo es público, le da un enlace directo al código exacto que corre, lo que a veces es una ventaja y a veces no. Decídelo a propósito.
builtAtvuelvenullen los deploys desde git: sólo lo define el script de deploy manual. Si te sirve la hora del build en los dos caminos, defínela también en tu build; si no, sácala del contrato.- Fuera de Vercel, cambia los nombres de las variables por las que exponga tu proveedor, o haz que tu pipeline escriba el commit en una variable propia. La idea no depende de la plataforma.
Son treinta líneas, y la pregunta "¿qué versión está en vivo?" pasó de investigación a comando. Si algo de esto no te cierra en tu stack, escríbenos.