Skip to main content
Volver al blog
VercelGitHub ActionsCI/CDDeploymentMonorepo

Desplegar en Vercel desde CI cuando quien commitea no es del equipo

Mueve un repo a una organización de GitHub y Vercel empieza a bloquear tus despliegues en silencio: el commit «no se pudo asociar a un miembro del equipo». Así despliego desde GitHub Actions con la CLI de Vercel: mis commits siguen contando en mi propio gráfico de GitHub, el panel sigue mostrando quién hizo push, y no pago un asiento por cada colaborador.

Publicado 10 de agosto de 20268 min de lectura

Moví landee a una organización de GitHub para que mis commits contaran en mi propio gráfico de contribuciones. En el siguiente push, producción dejó de desplegarse.

El panel de Vercel decía solo esto — en cada push. La integración nativa de Git se había convertido en silencio en una barrera:

Deployment Blocked
The deployment was blocked because the commit could not be
matched to a team member.

Por qué Vercel bloquea el despliegue

La integración nativa de Git de Vercel es un despliegue por webhook: GitHub envía un evento de push, Vercel lee el autor del commit, comprueba si es miembro del equipo de Vercel y bloquea el despliegue si no lo es. Es un control de seguridad: evita que un colaborador cualquiera, o el pull request de un fork, despliegue a producción de forma automática.

Tiene sentido, hasta que quien escribe el código no es un miembro de pago. Mi cuenta de GitHub firma los commits, pero no es miembro del equipo de Vercel. Así que para la integración cada despliegue es de «alguien que no es miembro», y cada despliegue se bloquea.

Despliega con la CLI, no con la integración de Git

La CLI es la salida. vercel deploy se autentica con un token, no con el autor del commit — nunca ejecuta la comprobación de pertenencia. Así que desconecta la integración nativa de Git y despliega desde GitHub Actions: push → se ejecuta el workflow → vercel deploy con un token → Vercel compila y publica.

.github/workflows/deploy-production.yml
name: Deploy Production (Vercel CLI)

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
    steps:
      - uses: actions/checkout@v7
      - run: npm install -g vercel@latest
      - run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
      - run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}

Dos cosas hacen que este sea el único camino de despliegue, para que la CLI y el webhook no compitan: vercel.json desactiva el auto-despliegue nativo, y pulsas Disconnect en el repo de Git dentro del panel de Vercel.

vercel.json
{
  "git": { "deploymentEnabled": false },
  "installCommand": "pnpm install --frozen-lockfile"
}

Conserva tu gráfico de contribuciones de GitHub

Todo el sentido de mover el repo a la organización era que los commits contaran en mi gráfico — así que el commit en GitHub debe seguir firmado por mí. Aquí ya ocurre: el despliegue por CLI corre sobre un checkout nuevo dentro del runner, y nada de eso se vuelve a hacer push a GitHub.

Así que commiteo como yo mismo, hago push y los cuadros verdes se rellenan. El token que autoriza el despliegue pertenece al dueño de la cuenta, no a mí — que es justo el propósito de un despliegue en CI.

Que el panel muestre quién hizo push

Un despliegue por CLI a secas aparece en la lista de Vercel como un hash pelado — sin mensaje, sin rama, sin autor. Para tener una fila con estilo git de verdad (mensaje + rama + avatar), Vercel tiene que reconocer el despliegue como un commit de git — y ese reconocimiento es justo lo que vuelve a lanzar la comprobación de autor.

El truco: dentro del runner, reescribe el autor de HEAD a un miembro del equipo y luego despliega. El amend nunca se vuelve a hacer push, así que en GitHub el commit sigue siendo tuyo. Vercel lee el autor-miembro desde .git — el despliegue queda reconocido y sin bloquear — y pones a quien realmente hizo push en el mensaje del commit para que la fila siga diciendo quién lo publicó.

# In the runner only — this amend is NEVER pushed back, so on GitHub the
# commit stays authored by the real pusher (your contribution graph is intact).
MSG="$(git log -1 --pretty=%s)"
git -c user.name="CI Bot" -c user.email="ci@your-org.dev" \
  commit --amend --author="CI Bot <ci@your-org.dev>" \
  -m "[$GITHUB_ACTOR] $MSG"

# Vercel now reads a team-member author from .git → recognized AND not blocked.
vercel deploy --prod --token="$VERCEL_TOKEN"

Una cuenta de servicio por la que despliegan todos

Generalízalo: una cuenta de servicio es dueña del equipo de Vercel y guarda el token de despliegue. Todos commitean como ellos mismos; la CI reescribe el autor a la cuenta de servicio y despliega con su token. La fila del panel dice [tú] tu mensaje de commit, y GitHub conserva tu autoría real.

Es la versión aburrida y honesta de lo que buscan muchos equipos. Pagas por los asientos de quienes necesitan acceso al panel — no uno por colaborador. Un token de CI que despliega el código de todos es el modelo que Vercel documenta; no compras un asiento a alguien solo porque su código se publique.

Los detalles que muerden

Un par de cosas que me costaron una tarde, para que a ti no te la cuesten:

  • pnpm en Vercel. La imagen de build puede fijar un pnpm antiguo sin importar tu campo packageManager. Pasa --build-env ENABLE_EXPERIMENTAL_COREPACK=1 a vercel deploy para que corepack use la versión que realmente fijaste.
  • Commits verificados. La firma va aparte: configura la firma SSH de commits y registra la clave en GitHub, y tus commits (los reales) obtienen la insignia verde Verified. El amend desechable del runner no necesita firma.
  • Ejecuciones legibles. Divide el único paso grande de shell en pasos con nombre (re-author → deploy → summary) para que la interfaz de Actions muestre la etapa de un vistazo.
  • Tests. El despliegue compila en Vercel pero nunca ejecuta tus tests — añade un workflow de CI aparte para eso, y de paso activa Dependabot.

El despliegue por CLI lo autoriza un token, no quien escribió el commit. Ese es todo el desbloqueo — y es una forma documentada y de primera clase de publicar.

Dónde está la línea

Seamos honestos con el límite, porque es fácil autoconvencerse de saltárselo. Desplegar el código de colaboradores con un token de CI es legítimo y está pensado así. Crear cuentas falsas para esquivar la facturación por asiento no lo es — eso es multi-cuenta, y va contra los términos de cualquier plataforma.

Lo que hacesLegítimoPasada de línea
Desplegar el código de colaboradores con un token de CI✓ documentado
Pagar un asiento por quien gestiona el panel
Commitear como tú; la identidad de servicio solo en CI✓ estadística honesta
Crear cuentas falsas/de servicio para no pagar asientos✗ multi-cuenta
Compartir un login para que muchos gestionen sin asiento✗ compartir credenciales

Conclusiones

  • La integración nativa de Git de Vercel bloquea los despliegues cuyo autor de commit no es miembro del equipo — una barrera de seguridad, no un bug.
  • vercel deploy con un token nunca comprueba el autor; desconecta la integración nativa y despliega desde CI.
  • Commitea como tú mismo y nunca hagas push del amend del runner, y tu gráfico de GitHub queda intacto.
  • Reescribir el autor de HEAD a un miembro del equipo en el runner da una fila de panel de verdad sin bloqueo — pon a quien hizo push en el mensaje.
  • Pagas por asientos del panel, no por colaborador: un token de CI que despliega el código de todos es el modelo previsto.
  • Crear cuentas falsas para esquivar la facturación es multi-cuenta. Ahí está la línea.

Ninguna de estas piezas es exótica — la CLI, un token, un git amend que nunca sale del runner. El giro es darse cuenta de que el muro de «no es miembro» no te dice que compres otro asiento; te dice que despliegues como se supone que hace la CI. Desconecta el webhook, entrega el token al runner y deja que quien escribió el código se lleve el crédito.

¿Ves un error?

¿Un dato incorrecto, una traducción torpe, algo que suena falso en este artículo? Escríbeme — en tu propio idioma.