Vercel-ге git pre-push hook арқылы деплой жасау: ұйым репозиторийі, Hobby жоспары, CI құпияларынсыз
Vercel-дің Git-интеграциясы Hobby жоспарында GitHub ұйымына тиесілі жеке репозиторийді қабылдамайды, ал CI жолы саған қосуға рұқсат етілмеуі мүмкін репо құпияларын қажет етеді. Міне, жеңілірек нұсқа: Vercel CLI арқылы дәл push жасалған коммитті деплой жасайтын локалды pre-push hook, ал әркім өз атымен push жасай береді.
Репозиторий GitHub ұйымына көшті, Vercel жобасы Hobby командасында тұр, ал жобадағы әркім өз GitHub атымен push жасайды. Мен әр push деплой жасауын қаладым — main өнімге (production), қалған кез келген тармақ превьюге — коммиттердің кімге тиесілі екенін өзгертпей.
Алғашқы жоспарым нативті Git-интеграция еді. Ол қатарынан үш рет сәтсіз аяқталды, әр жолы бөлек себеппен:
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)Сені Git-интеграциядан бөліп тұрған үш қабырға
- GitHub логин байланысы жоқ. Командаға иелік ететін Vercel аккаунты GitHub-ты ешқашан қосқан емес, сондықтан репоны байланыстыру жоғарыдағы бірінші қатемен құлайды. Шешімі — Account Settings → Authentication, бірақ басар алдында ойлан: GitHub аккаунты бір мезгілде тек бір Vercel аккаунтына қосыла алады. Өз аккаунтыңды сервистік аккаунтқа қоссаң, ол өзіңнің жеке Vercel-іңнен ауысып кетеді, содан кейін Vercel жеке жобаларыңның деплойларын бөгейді («Git author must have access to the project»). Төмендегі hook-қа GitHub байланысы мүлде керек емес, сондықтан мұны тек Git-интеграцияны мақсат тұтсаң ғана істе.
- Қосымшаны орнатуға ұйым иесі керек. Vercel GitHub App ұйымға орнатылуы тиіс. Жай мүше оны тек сұрай алады, ал баптаулар беті «This action must be performed by an organization owner» деп жауап береді.
- Hobby ұйымның жеке реполарын қоса алмайды. Жоғарыдағының бәрін істесең де, Vercel бәрібір 409 қайтарады. Мұны ешқандай баптау түзетпейді: шек — жоспардың өзінде.
Алғашқы екеуі — сұрап алуға болатын рұқсаттар. Үшіншісі команда Pro-ға көшкенше Git-интеграцияның күн тәртібінен шығатынын білдіреді. Одан кейінгінің бәрі — айналып өту жолы, сондықтан айналып өту жолы екенін адал мойындайтынын таңда.
Push жасайтын машинадан деплой жаса
Vercel CLI коммит авторымен емес, логинмен немесе токенмен аутентификацияланады, сондықтан интеграцияны бөгейтін мүшелік тексерісін мүлде жүргізбейді. Алдыңғы жазбам CLI-ды GitHub Actions-та жүргізеді. Ол үшін репо құпияларында VERCEL_TOKEN болуы керек, яғни репоға админ құқығы қажет — және дашбордта коммит көрінуі үшін раннерде авторды --amend ету керек.
pre-push hook екеуін де айналып өтеді. Ол әзірлеушінің машинасында git push-тан кейін жүреді, жаңа ғана push жасалған коммитті деплой жасайды және коммитке мүлде тиіспейді: нағыз автор GitHub-та да, дашбордта да қалады. Бағасы — ол әр машинада бөлек тұрады; бұл жайлы төменде толығырақ.
Hook
Оны .git/hooks/pre-push деп сақта да, орындалатын файл қыл. Git оны remote атымен және URL-мен шақырады, ал stdin арқылы әр push жасалатын ref үшін бір жолдан береді.
#!/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Негізгі жүкті төрт бөлшек көтереді:
- Remote-тің атын емес, URL-ін салыстыр. Git URL-ді
$2ретінде береді. Басқа кез келген remote-ке push кезінде hook бірден шығады, сондықтан mirror мен fork ешқашан деплой жасалмайды. - Push-ты күт, содан кейін оның жеткенін тексер. Hook push аяқталғанға дейін жүреді, әрі қабылданбаған push-та да жүреді. Сондықтан ол фонға бөлініп шығады,
git pushпроцесінің аяқталуын күтеді жәнеgit ls-remoteremote-те сенің SHA-ңды көрсетсе ғана деплой жасайды. - Жұмыс ағашыңды емес, push жасалған коммитті деплой жаса. Push жасалған SHA-дағы бір реттік
git worktreeкоммит жасалмаған өзгерістер мен аяқталмаған файлдардың ешқашан шықпауын қамтамасыз етеді. Ол сенің.vercelбайланысыңды қайта қолданады. main— өнім (production), қалғанының бәрі — превью. Push жасалған ref-ке қарай таңдалатын бір--prodжалауы.
Дашбордта хешті емес, коммитті көрсет
Алғашқы hook-деплойларым дашбордта коммит хабарының орнына AowEm2XzF тәрізді кездейсоқ жол болып көрінді. Деплойларды API арқылы қайта оқығанда себебі анықталды: хабар көрсеткен деплойдың метадеректерінде githubOrg, githubRepo және githubDeployment болған. CLI оларды қалыпты checkout-тан GitHub remote-ін танығанда ғана қосады — ал бөлінген worktree-ден маған gitCommit* кілттері ғана келді, басқа ештеңе жоқ.
Шешімі — жоғарыдағы hook істегендей, оларды remote URL-ден алып, -m арқылы өзің беру. Сонда хабар, тармақ және автор сол кілттерден алынады. Бұл — өз жобамда байқағаным; Vercel бұл кілттерді құжаттамайды, сондықтан алғашқы деплойдан кейін дашбордыңды тексер де, оларды өзгеруі мүмкін нәрсе деп қара.
Аутентификация: токенді қолдан, логинді бөліспе
Hook vercel deploy шақырады, сондықтан машинаға командаға иелік ететін аккаунттың тіркелгі деректері керек. CLI VERCEL_TOKEN-ді орта айнымалыларынан өзі оқиды:
# A token created for this purpose; the CLI reads it automatically
export VERCEL_TOKEN="..."vercel login-нен гөрі токенді таңда. Токенді жеке-дара қайтарып алуға болады; ал ортақ логинді бәрі үшін ауыстырмай, бір адамнан ғана қайтарып алу мүмкін емес. Vercel автоматтандыру үшін құжаттайтыны да — токендер.
Командамен бөлісу
Скрипт пен бір пәрменмен орнататын орнатқышты репоға коммит жаса, сонда ешкім файлдарды қолмен көшірмейді:
pnpm add -g vercel
vercel link --project my-project --scope <team-slug>
bash scripts/vercel-deploy/install.sh # copies the hook into .git/hooksHook-тың өзі .git/hooks ішінде тұрады, оны git ешқашан push жасамайды — орнатқыш сол үшін бар. Скриптті қайта қолданар алдында өзгертілетін бір жол: жоғарыдағы case ішіндегі репо URL үлгісі.
CI жолымен салыстырғанда
Бұл — алдыңғы жазбаның барлық жағынан жақсырақ нұсқасы емес. Ол қамтуды қарапайымдылыққа айырбастайды:
| GitHub Actions (алдыңғы жазба) | pre-push hook (осы жазба) | |
|---|---|---|
| Репоға админ құқығы керек (құпиялар) | Иә | Жоқ |
| Барлығының push-тарын деплой жасайды | Иә | Тек hook бар машиналардан |
| Раннерде авторды қайта жазады | Иә (amend, ешқашан push жасалмайды) | Жоқ |
| Баптау | Репоға бір рет | Әр әзірлеушіге бір рет |
| GitHub веб-интерфейсінен, боттардан, басқа машиналардан push | Деплой жасалады | Деплой жасалмайды |
| Қателер қайда көрінеді | Actions логы | Локалды лог файлы |
Шек қайда
Hook — айналып өту жолы, рұқсат емес. Оны қабылдар алдында үш нәрсе туралы өзіңе адал бол:
- Hobby — коммерциялық емес қолдануға арналған. Егер бұл компанияның өнімі болса, шынайы мәселе — жоспар, ал hook оны тек жасырады. Шешімі — Pro; hook біреу шотты бекіткенше уақыт ұтып береді.
- Орын (seat) ақысын төлемеу үшін логинді бөліспе. Команданың деплой-токенін ұстайтын бір аккаунт — дұрыс. Дашбордқа қатынас алу үшін бірнеше адамның бір адам болып кіруі — тіркелгі деректерін бөлісу.
- Ол тек өз машинаңнан push жасағаныңды ғана деплой жасайды. Hook-сыз әріптесің push жасап, өнімді ескірген күйде қалдыруына ештеңе кедергі болмайды — деплойға кім жауап беретінін шеш.
Қорытынды
- Hobby-дегі ұйымның жеке репозиторийі үшін Vercel-дің Git-интеграциясы күн тәртібінен шығады: ешбір рұқсат жоспар шегін түзетпейді.
- CLI коммит авторын емес, токенді аутентификациялайды, сондықтан мүшелік тексерісі оған қатысты емес.
pre-pushhook әзірлеушінің машинасынан деплой жасайды, репо құпияларын қажет етпейді және коммитті ешқашан қайта жазбайды.- Деплой алдында push-ты күт те, оның жеткенін
git ls-remoteарқылы тексер. - Push жасалған SHA-ның worktree-сін деплой жаса және дашбордта коммит көрінуі үшін
github*метадеректерін өзің бер. - Ортақ логинді емес, токенді қолдан — ал жоба коммерциялық болса, Hobby-ді уақытша шешім деп қара.
- GitHub логині бір мезгілде тек бір Vercel аккаунтына қосылады — өзіңдікін сервистік аккаунтқа көшірме; hook-қа ол керек емес.
Құпияларды қоса алатын болсаң, CI жолы әлі де жақсырақ әдепкі таңдау. Hook — қоса алмайтын күнге арналған: бірнеше ондаған жол shell, әр әзірлеушіге бір рет баптау, ал әр коммит әлі де нағыз авторын сақтайды.
Қате байқадыңыз ба?
Осы мақалада қате факт, олқы аударма немесе жалған естілетін нәрсе бар ма? Маған жазыңыз — өз тіліңізде.