Skip to main content
← Tillbaka till bloggen
VercelGit HooksCI/CDDeploymentpnpm

Deploya till Vercel från en git pre-push-hook: org-repo, Hobby-plan, inga CI-hemligheter

Vercels Git-integration vägrar koppla ett privat repo som ägs av en GitHub-organisation på Hobby-planen, och CI-vägen kräver repo-hemligheter som du kanske inte får lägga till. Här är det lättare alternativet: en lokal pre-push-hook som deployar exakt den pushade committen med Vercels CLI, medan alla fortsätter pusha under sitt eget namn.

Publicerad 6 oktober 20267 min läsning
Uppdatering till Deploya till Vercel från CI när den som committar inte är teammedlem. Det inlägget deployar från GitHub Actions och är det bättre valet när du kan lägga till repo-hemligheter. Det här är för när du inte kan — och det behöver ingen omskrivning av committar alls.

Repot flyttade in i en GitHub-organisation, Vercel-projektet ligger i ett Hobby-team, och alla i projektet pushar under sitt eget GitHub-namn. Jag ville att varje push skulle deploya — main till produktion, varje annan gren till en preview — utan att ändra vem committarna tillhör.

Min första plan var den inbyggda Git-integrationen. Den misslyckades tre gånger i rad, varje gång av olika skäl:

Error: Failed to link your-org/your-repo. You need to add a Login Connection
to your GitHub account first. (400)

This action must be performed by an organization owner

Error: The repository "your-repo" is private and owned by an organization,
which is not supported on the Hobby plan. Upgrade to Pro to continue. (409)

Tre murar mellan dig och Git-integrationen

  1. Ingen GitHub-inloggning kopplad. Vercel-kontot som äger teamet hade aldrig kopplat GitHub, så länkningen av repot misslyckas med det första felet ovan. Lösningen är Account Settings → Authentication — men tänk efter innan du klickar: ett GitHub-konto kan bara vara kopplat till ett Vercel-konto åt gången. Kopplar du ditt till servicekontot flyttas det bort från ditt personliga Vercel, och Vercel blockerar sedan deployer av dina personliga projekt (”Git author must have access to the project”). Hooken nedan behöver ingen GitHub-koppling alls, så gör det här bara om du satsar på Git-integrationen.
  2. Att installera appen kräver en organisationsägare. Vercels GitHub App måste installeras på organisationen. En vanlig medlem kan bara begära det, och inställningssidan svarar ”This action must be performed by an organization owner”.
  3. Hobby kan inte koppla privata organisationsrepon. När allt ovan är gjort svarar Vercel fortfarande 409. Ingen inställning fixar det här: planen är gränsen.

De två första är behörigheter du kan jaga. Den tredje betyder att Git-integrationen är utesluten tills teamet har Pro. Allt som följer är en nödlösning — så välj en som är ärlig med att den är en nödlösning.

Deploya från maskinen som pushar

Vercels CLI autentiserar med en inloggning eller ett token, inte med commit-författaren, så det kör aldrig medlemskapskontrollen som blockerar integrationen. Mitt förra inlägg kör CLI:t i GitHub Actions. Det kräver VERCEL_TOKEN i repots hemligheter, alltså admin-rättigheter på repot — plus en --amend av författaren i runnern så att dashboarden visar en commit.

En pre-push-hook slipper båda. Den körs på utvecklarens maskin efter git push, deployar committen som just pushades och rör aldrig committen: den riktiga författaren finns kvar på GitHub och i dashboarden. Priset är att den måste finnas på varje maskin — mer om det nedan.

Hooken

Spara den som .git/hooks/pre-push och gör den körbar. Git anropar den med remotens namn och URL, och matar den med en rad per pushad ref på stdin.

.git/hooks/pre-push
#!/bin/bash
# Deploy to Vercel after a successful push. Local only: git never pushes .git/hooks.
# main -> production, any other branch -> preview. Runs detached; log: .git/vercel-deploy.log

case "$2" in
  *your-org/your-repo*) ;;
  *) exit 0 ;;
esac

repo_root="$(git rev-parse --show-toplevel)"
gh_path="${2#*github.com[:/]}"; gh_path="${gh_path%.git}"
gh_org="${gh_path%%/*}"; gh_repo="${gh_path#*/}"
log="$repo_root/.git/vercel-deploy.log"
push_pid=$PPID

[ -d "$repo_root/.vercel" ] || { echo "vercel-deploy: run 'vercel link' first, skipping deploy" >&2; exit 0; }

while read -r _ local_sha remote_ref _; do
  [ "$local_sha" = "0000000000000000000000000000000000000000" ] && continue
  branch="${remote_ref#refs/heads/}"
  if [ "$branch" = "main" ]; then flag="--prod"; else flag=""; fi

  (
    # wait for the push itself to finish, then deploy exactly the pushed commit
    while kill -0 "$push_pid" 2>/dev/null; do sleep 1; done
    # a rejected or failed push must not deploy: only continue if the commit reached the remote
    landed="$(git ls-remote "$2" "$remote_ref" 2>/dev/null | cut -f1)"
    if [ "$landed" != "$local_sha" ]; then
      echo "=== $(date '+%F %T') $branch @ ${local_sha:0:7} push did not land, deploy skipped" >> "$log"
      exit 0
    fi
    tmp="$(mktemp -d)"
    cd "$repo_root" || exit 1
    git worktree add --detach "$tmp" "$local_sha" >/dev/null 2>&1 || exit 1
    cp -R "$repo_root/.vercel" "$tmp/.vercel"
    cd "$tmp" || exit 1
    {
      echo "=== $(date '+%F %T') $branch @ ${local_sha:0:7} ${flag:-preview}"
      vercel deploy $flag --yes \
        --build-env ENABLE_EXPERIMENTAL_COREPACK=1 \
        -m "githubDeployment=1" \
        -m "githubOrg=$gh_org" -m "githubRepo=$gh_repo" \
        -m "githubCommitOrg=$gh_org" -m "githubCommitRepo=$gh_repo" \
        -m "githubCommitMessage=$(git -C "$repo_root" log -1 --format=%s "$local_sha")" \
        -m "githubCommitSha=$local_sha" \
        -m "githubCommitRef=$branch" \
        -m "githubCommitAuthorName=$(git -C "$repo_root" log -1 --format=%an "$local_sha")" \
        -m "githubCommitAuthorEmail=$(git -C "$repo_root" log -1 --format=%ae "$local_sha")" \
        2>&1 | tail -5
    } >> "$log"
    cd "$repo_root" && git worktree remove --force "$tmp"
  ) >/dev/null 2>&1 &
  disown
done

exit 0

Fyra detaljer bär hela tyngden:

  • Matcha remotens URL, inte dess namn. Git skickar URL:en som $2. Vid en push till vilken annan remote som helst avslutas hooken direkt, så en spegel eller en fork deployar aldrig.
  • Vänta på pushen och kontrollera sedan att den landade. Hooken körs innan pushen är klar, och en avvisad push kör den också. Så den kopplar loss sig, väntar på att git push-processen avslutas och deployar bara om git ls-remote visar din SHA på remoten.
  • Deploya den pushade committen, inte din arbetskatalog. Ett tillfälligt git worktree på den pushade SHA:n gör att ocommittade ändringar och halvfärdiga filer aldrig går ut. Det återanvänder din .vercel-länk.
  • main är produktion, allt annat är en preview. En enda --prod-flagga, vald utifrån den pushade refen.

Låt dashboarden visa committen, inte en hash

Mina första hook-deployer dök upp i dashboarden som en slumpmässig sträng i stil med AowEm2XzF i stället för commit-meddelandet. När jag läste tillbaka deployerna via API:t framgick varför: den som visade ett meddelande hade githubOrg, githubRepo och githubDeployment i sin metadata. CLI:t lägger bara till dem när det känner igen en GitHub-remote från en vanlig checkout — från ett fristående worktree fick jag gitCommit*-nycklar och inget annat.

Lösningen är att skicka dem själv med -m, härledda från remotens URL, som hooken ovan gör. Meddelande, gren och författare kommer sedan från de nycklarna. Det här är vad jag observerade i mitt eget projekt; Vercel dokumenterar inte dessa nycklar, så kolla din dashboard efter första deployen och räkna med att de kan ändras.

Autentisering: använd ett token, dela inte en inloggning

Hooken anropar vercel deploy, så maskinen behöver inloggningsuppgifter för kontot som äger teamet. CLI:t läser VERCEL_TOKEN från miljön på egen hand:

# A token created for this purpose; the CLI reads it automatically
export VERCEL_TOKEN="..."

Föredra ett token framför vercel login. Ett token kan återkallas separat; en delad inloggning kan inte tas tillbaka från en person utan att den ändras för alla. Tokens är också det Vercel dokumenterar för automatisering.

Dela hooken med teamet

Committa skriptet och en installerare på ett enda kommando till repot, så att ingen behöver kopiera filer för hand:

pnpm add -g vercel
vercel link --project my-project --scope <team-slug>
bash scripts/vercel-deploy/install.sh   # copies the hook into .git/hooks

Själva hooken ligger i .git/hooks, som git aldrig pushar — därför finns installeraren. En rad att ändra innan du återanvänder skriptet: repo-URL-mönstret i case-satsen högst upp.

Så står den sig mot CI-vägen

Det här är inte en strikt bättre version av förra inlägget. Det byter räckvidd mot enkelhet:

GitHub Actions (förra inlägget)pre-push-hook (det här inlägget)
Kräver admin på repot (hemligheter)JaNej
Deployar allas pusharJaBara från maskiner med hooken
Skriver om författaren i runnernJa (amend, pushas aldrig)Nej
InstallationEn gång per repoEn gång per utvecklare
Pushar från GitHubs webbgränssnitt, bottar, andra maskinerDeployasDeployas inte
Var fel dyker uppActions-loggenEn lokal loggfil

Var gränsen går

En hook är en nödlösning, inte ett frikort. Innan du tar den i bruk, var ärlig mot dig själv om tre saker:

  • Hobby är för icke-kommersiellt bruk. Om det här är ett företags produkt är planen det verkliga problemet och hooken döljer det bara. Lösningen är Pro; hooken köper tid tills någon godkänner fakturan.
  • Dela inte en inloggning för att slippa betala för platser. Ett konto som håller teamets deploy-token är okej. Flera personer som loggar in som samma person för att få dashboard-åtkomst är delade inloggningar.
  • Den deployar bara det du pushar från din maskin. Inget hindrar en kollega utan hooken från att pusha och låta produktionen halka efter — bestäm vem som äger deployerna.

Slutsatser

  • Vercels Git-integration är utesluten för ett privat organisationsrepo på Hobby: ingen behörighet fixar en plangräns.
  • CLI:t autentiserar ett token, inte commit-författaren, så medlemskapskontrollen gäller inte för det.
  • En pre-push-hook deployar från utvecklarens maskin, behöver inga repo-hemligheter och skriver aldrig om en commit.
  • Vänta på pushen och verifiera med git ls-remote att den landade innan du deployar.
  • Deploya ett worktree på den pushade SHA:n, och skicka github*-metadatan själv så att dashboarden visar committen.
  • Använd ett token, inte en delad inloggning — och se Hobby som ett provisorium om projektet är kommersiellt.
  • En GitHub-inloggning kopplas till ett Vercel-konto åt gången — flytta inte din till servicekontot; hooken behöver den inte.

CI-vägen är fortfarande det bättre standardvalet när du kan lägga till hemligheter. Hooken är för den dag du inte kan: några dussin rader shell, uppsatta en gång per utvecklare, och varje commit bär fortfarande sin riktiga författare.

Hittade du ett fel?

Ett felaktigt faktum, en skev översättning, något som känns falskt i den här artikeln? Skriv till mig — på ditt eget språk.