Skip to main content
← Tilbake til bloggen
VercelGit HooksCI/CDDeploymentpnpm

Deploy til Vercel fra en git pre-push-hook: org-repo, Hobby-plan, ingen CI-hemmeligheter

Vercels Git-integrasjon avviser et privat repo som eies av en GitHub-organisasjon på Hobby-planen, og CI-veien krever repo-hemmeligheter du kanskje ikke har lov til å legge til. Her er det lettere alternativet: en lokal pre-push-hook som deployer akkurat den pushede committen med Vercel-CLI-en, mens alle fortsetter å pushe under sitt eget navn.

Publisert 6. oktober 20267 min lesing
Oppdatering til Deploy til Vercel fra CI når den som committer ikke er teammedlem. Det innlegget deployer fra GitHub Actions og er det beste valget når du kan legge til repo-hemmeligheter. Dette er for når du ikke kan det — og det trenger ingen omskriving av committer i det hele tatt.

Repoet flyttet inn i en GitHub-organisasjon, Vercel-prosjektet ligger i et Hobby-team, og alle på prosjektet pusher under sitt eget GitHub-navn. Jeg ville at hver push skulle deploye — main til produksjon, alle andre grener til en preview — uten å endre hvem committene tilhører.

Min første plan var den innebygde Git-integrasjonen. Den feilet tre ganger på rad, hver gang av en annen grunn:

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 vegger mellom deg og Git-integrasjonen

  1. Ingen GitHub-innlogging koblet til. Vercel-kontoen som eier teamet hadde aldri koblet til GitHub, så kobling av repoet feiler med den første feilen ovenfor. Løsningen er Account Settings → Authentication — men tenk deg om før du klikker: en GitHub-konto kan bare være koblet til én Vercel-konto om gangen. Kobler du din til tjenestekontoen, flyttes den bort fra din personlige Vercel, og Vercel blokkerer deretter deployer av de personlige prosjektene dine («Git author must have access to the project»). Hooken nedenfor trenger ingen GitHub-kobling i det hele tatt, så gjør dette bare hvis du går for Git-integrasjonen.
  2. Å installere appen krever en organisasjonseier. Vercels GitHub App må installeres på organisasjonen. Et vanlig medlem kan bare be om det, og innstillingssiden svarer «This action must be performed by an organization owner».
  3. Hobby kan ikke koble til private organisasjonsrepoer. Når alt ovenfor er gjort, svarer Vercel fortsatt 409. Ingen innstilling fikser dette: planen er grensen.

De to første er tillatelser du kan jakte på. Den tredje betyr at Git-integrasjonen er utelukket til teamet er på Pro. Alt etter dette er en workaround — så velg en som er ærlig på at den er det.

Deploy fra maskinen som pusher

Vercel-CLI-en autentiserer med en innlogging eller et token, ikke med commit-forfatteren, så den kjører aldri medlemskapssjekken som blokkerer integrasjonen. Mitt forrige innlegg kjører CLI-en i GitHub Actions. Det krever VERCEL_TOKEN i repoets hemmeligheter, altså admin-rettigheter på repoet — pluss en --amend av forfatteren i runneren slik at dashbordet viser en commit.

En pre-push-hook slipper begge deler. Den kjører på utviklerens maskin etter git push, deployer committen som nettopp ble pushet, og rører aldri committen: den ekte forfatteren blir stående på GitHub og i dashbordet. Prisen er at den finnes per maskin — mer om det nedenfor.

Hooken

Lagre den som .git/hooks/pre-push og gjør den kjørbar. Git kaller den med remotens navn og URL, og mater den med én linje per pushet 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

Fire detaljer bærer hele vekten:

  • Match remotens URL, ikke navnet. Git sender URL-en som $2. Ved push til en hvilken som helst annen remote avslutter hooken umiddelbart, så et speil eller en fork deployer aldri.
  • Vent på pushen, og sjekk så at den landet. Hooken kjører før pushen er ferdig, og en avvist push kjører den også. Så den kobler seg løs, venter til git push-prosessen er ferdig, og deployer bare hvis git ls-remote viser SHA-en din på remoten.
  • Deploy den pushede committen, ikke arbeidstreet ditt. Et midlertidig git worktree på den pushede SHA-en gjør at endringer som ikke er committet og halvferdige filer aldri går ut. Det gjenbruker .vercel-koblingen din.
  • main er produksjon, alt annet er en preview. Ett enkelt --prod-flagg, valgt ut fra den pushede refen.

Få dashbordet til å vise committen, ikke en hash

De første hook-deployene mine dukket opp i dashbordet som en tilfeldig streng som AowEm2XzF i stedet for commit-meldingen. Å lese deployene tilbake via API-et viste hvorfor: den som viste en melding hadde githubOrg, githubRepo og githubDeployment i metadataene sine. CLI-en legger dem bare til når den gjenkjenner en GitHub-remote fra en vanlig checkout — fra et frakoblet worktree fikk jeg gitCommit*-nøkler og ingenting mer.

Løsningen er å sende dem selv med -m, utledet fra remotens URL, slik hooken ovenfor gjør. Melding, gren og forfatter kommer da fra de nøklene. Dette er det jeg observerte i mitt eget prosjekt; Vercel dokumenterer ikke disse nøklene, så sjekk dashbordet etter første deploy og regn med at de kan endre seg.

Autentisering: bruk et token, ikke del en innlogging

Hooken kaller vercel deploy, så maskinen trenger legitimasjon for kontoen som eier teamet. CLI-en leser selv VERCEL_TOKEN fra miljøet:

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

Foretrekk et token framfor vercel login. Et token kan trekkes tilbake for seg selv; en delt innlogging kan ikke tas fra én person uten at den endres for alle. Et token er også det Vercel dokumenterer for automatisering.

Dele det med teamet

Committ skriptet og en installer med én kommando til repoet, så ingen kopierer filer for hånd:

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

Selve hooken ligger i .git/hooks, som git aldri pusher — derfor finnes installeren. Én linje å endre før du gjenbruker skriptet: repo-URL-mønsteret i case øverst.

Hvordan det står seg mot CI-veien

Dette er ikke en versjon av forrige innlegg som er bedre på alle punkter. Det bytter rekkevidde mot enkelhet:

GitHub Actions (forrige innlegg)pre-push-hook (dette innlegget)
Krever admin på repoet (hemmeligheter)JaNei
Deployer det alle pusherJaBare fra maskiner med hooken
Skriver om forfatteren i runnerenJa (amend, aldri pushet)Nei
OppsettÉn gang per repoÉn gang per utvikler
Push fra GitHubs nettgrensesnitt, boter, andre maskinerDeployesDeployes ikke
Hvor feil dukker oppActions-loggenEn lokal loggfil

Hvor grensen går

En hook er en workaround, ikke en fribillett. Før du tar den i bruk, vær ærlig mot deg selv om tre ting:

  • Hobby er for ikke-kommersiell bruk. Hvis dette er et selskaps produkt, er planen det egentlige problemet, og hooken skjuler det bare. Løsningen er Pro; hooken kjøper tid til noen godkjenner fakturaen.
  • Ikke del en innlogging for å slippe å betale for plasser. Én konto som holder teamets deploy-token er greit. Flere personer som logger inn som samme person for å få dashbord-tilgang er deling av påloggingsdata.
  • Den deployer bare det du pusher fra maskinen din. Ingenting hindrer en kollega uten hooken i å pushe og la produksjonen henge etter — bestem hvem som eier deployene.

Oppsummering

  • Vercels Git-integrasjon er utelukket for et privat organisasjonsrepo på Hobby: ingen tillatelse fikser en plangrense.
  • CLI-en autentiserer et token, ikke commit-forfatteren, så medlemskapssjekken gjelder ikke for den.
  • En pre-push-hook deployer fra utviklerens maskin, trenger ingen repo-hemmeligheter og skriver aldri om en commit.
  • Vent på pushen og verifiser at den landet med git ls-remote før du deployer.
  • Deploy et worktree av den pushede SHA-en, og send github*-metadataene selv slik at dashbordet viser committen.
  • Bruk et token, ikke en delt innlogging — og se på Hobby som en nødløsning hvis prosjektet er kommersielt.
  • En GitHub-innlogging kobles til én Vercel-konto om gangen — ikke flytt din over til tjenestekontoen; hooken trenger den ikke.

CI-veien er fortsatt det beste standardvalget når du kan legge til hemmeligheter. Hooken er for dagen du ikke kan det: noen titalls linjer shell, satt opp én gang per utvikler, og hver commit bærer fortsatt sin ekte forfatter.

Fant du en feil?

Et feil faktum, en skjev oversettelse, noe som virker usant i denne artikkelen? Skriv til meg — på ditt eget språk.