·4 min de lectura·Mounaji Studio

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ó.

engineeringdeploywebvendear

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

De dónde sale el commit, en orden: si existe VERCEL_GIT_COMMIT_SHA, source es vercel-git; si no, y existe COMMIT_SHA, source es local-cli; si no hay ninguna, commit y source son null

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.
  • builtAt vuelve null en 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.

Comentarios y correcciones

Chat with us