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.
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
- 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.
- Å 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».
- 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.
#!/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 0Fire 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 hvisgit ls-remoteviser SHA-en din på remoten. - Deploy den pushede committen, ikke arbeidstreet ditt. Et midlertidig
git worktreepå den pushede SHA-en gjør at endringer som ikke er committet og halvferdige filer aldri går ut. Det gjenbruker.vercel-koblingen din. mainer 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/hooksSelve 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) | Ja | Nei |
| Deployer det alle pusher | Ja | Bare fra maskiner med hooken |
| Skriver om forfatteren i runneren | Ja (amend, aldri pushet) | Nei |
| Oppsett | Én gang per repo | Én gang per utvikler |
| Push fra GitHubs nettgrensesnitt, boter, andre maskiner | Deployes | Deployes ikke |
| Hvor feil dukker opp | Actions-loggen | En 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-remotefø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.