
Claude Code 上線完即核實:自動 ping Vercel preview + smoke test + Slack 通知
Push 唔代表部署成功。教 Claude Code 一套上線後嘅例行核實:poll Vercel API 等 build ready、ping 關鍵路徑、跑 Playwright smoke、Slack 報生死。一個 /ship 搞掂。
情境
你 indie 做緊個 SaaS,禮拜五夜晚 push 完一個 pricing page 嘅改動,落街食飯。返到屋企打開 laptop —— Slack 三個用戶 ping 你:「點解 /pricing 出 500?」
睇 Vercel dashboard,build 綠色。睇 GitHub,合併咗。但個 page 真係 500。原因:你改咗個 env var name,本地 .env.local 有,Vercel project setting 冇加。Build 過得,runtime 死。
呢類錯,push 成功 ≠ 部署成功 ≠ 用戶見到嘅嘢正常。Vercel 個綠剔淨係話 build pipeline 行得,唔保證個 page render 到。
呢篇拆 4 步,教 Claude Code 喺你打完 git push 之後,自動行一套上線後嘅例行核實:等 deploy ready、ping 關鍵路徑、跑 Playwright smoke、Slack 報生死。出事第一個知嘅係你,唔係你啲用戶。
跟住做
1. Poll Vercel API 等 deploy ready
Push 完即跑 smoke test 會撞「deploy 仲未起好」。要先 poll Vercel API 等 state 變 READY。
.claude/scripts/wait-deploy.sh:
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ID="${VERCEL_PROJECT_ID:?missing}"
TOKEN="${VERCEL_TOKEN:?missing}"
SHA="$(git rev-parse HEAD)"
for i in {1..40}; do
RES=$(curl -s "https://api.vercel.com/v6/deployments?projectId=$PROJECT_ID&limit=5" \
-H "Authorization: Bearer $TOKEN")
URL=$(echo "$RES" | jq -r ".deployments[] | select(.meta.githubCommitSha==\"$SHA\") | .url" | head -1)
STATE=$(echo "$RES" | jq -r ".deployments[] | select(.meta.githubCommitSha==\"$SHA\") | .state" | head -1)
if [ "$STATE" = "READY" ]; then
echo "https://$URL"
exit 0
fi
if [ "$STATE" = "ERROR" ] || [ "$STATE" = "CANCELED" ]; then
echo "deploy failed: $STATE" >&2
exit 1
fi
sleep 10
done
echo "timeout waiting for deploy" >&2
exit 1
呢段每 10 秒 poll 一次,最多等 400 秒(一般 build 兩三分鐘)。Token 喺 Vercel account settings 開,權限限定唯讀 deployments 已經夠。最後吐返個 preview URL 畀下游用。
2. Critical path smoke check
有咗 preview URL,第一輪驗證係HTTP 層:關鍵 page 回 200,response body 含預期關鍵字。唔使任何 browser、又快又慳。
.claude/scripts/smoke-paths.sh:
#!/usr/bin/env bash
BASE="$1"
PATHS=(
"/|首頁"
"/pricing|月費"
"/api/health|ok"
)
FAIL=0
for entry in "${PATHS[@]}"; do
IFS='|' read -r path expect <<< "$entry"
RES=$(curl -s -w "\n%{http_code}" "$BASE$path")
CODE=$(echo "$RES" | tail -1)
BODY=$(echo "$RES" | sed '$d')
if [ "$CODE" != "200" ]; then
echo "FAIL $path → HTTP $CODE"; FAIL=1; continue
fi
if ! echo "$BODY" | grep -q "$expect"; then
echo "FAIL $path → missing 「$expect」"; FAIL=1; continue
fi
echo "OK $path"
done
exit $FAIL
呢層捉到 90% 嘅部署 regression:env var 漏、route 拆錯、SSR 拋 error。三秒搞掂。每條 path 配個關鍵字,連「回咗 200 但 render 咗一版 fallback 空白頁」嘅暗病都捉到。
3. Playwright scenario test
HTTP 層過到,唔代表用戶 flow 行得到。第三步係用 headless browser 跑 3 至 5 條最關鍵嘅 flow。
tests/smoke.spec.ts:
import { test, expect } from '@playwright/test'
const BASE = process.env.PREVIEW_URL!
test('landing → CTA → signup form 開到', async ({ page }) => {
await page.goto(BASE)
await page.getByRole('link', { name: /開始試用/ }).click()
await expect(page.getByRole('heading', { name: /註冊/ })).toBeVisible()
})
test('pricing page 三個 plan 都 render', async ({ page }) => {
await page.goto(`${BASE}/pricing`)
await expect(page.getByText('Starter')).toBeVisible()
await expect(page.getByText('Pro')).toBeVisible()
await expect(page.getByText('Team')).toBeVisible()
})
test('search 打字有結果', async ({ page }) => {
await page.goto(BASE)
await page.getByPlaceholder(/搵嘢/).fill('claude')
await expect(page.locator('[data-testid="search-result"]').first()).toBeVisible({ timeout: 5000 })
})
跑:PREVIEW_URL=$URL npx playwright test --project=chromium tests/smoke.spec.ts。
呢個唔係 full E2E,係 smoke —— 揀 3 至 5 條「呢條死咗即等於成個 site 死咗」嘅 flow。多咗會慢、會 flaky、會成日無端端報錯。
4. Slack / Discord 通知
最後一步:將上面三輪結果灌入 webhook,過到報綠、唔過報紅,再連埋 preview URL 同 commit message。
.claude/commands/ship.md:
---
description: Push + verify deploy + Slack 通知
---
請執行:
1. `git push origin HEAD`
2. 跑 `.claude/scripts/wait-deploy.sh`,拎到 PREVIEW_URL
3. 跑 `.claude/scripts/smoke-paths.sh "$PREVIEW_URL"`
4. 跑 `PREVIEW_URL=$PREVIEW_URL npx playwright test tests/smoke.spec.ts`
5. 整合結果:
- 全綠 → POST Slack webhook:✅ shipped [commit subject] → [PREVIEW_URL]
- 任一 fail → POST Slack webhook:🔴 deploy verify FAIL → [fail 嘅 step] + [PREVIEW_URL]
6. Return 一句總結
SLACK_WEBHOOK_URL 入 .env.local(同 Claude session env 共用)。打 /ship 一鍵搞掂:push、等、驗、報,五分鐘冇你事,行完先 ping 你。
變化
變化 1:Netlify 版本
Netlify API 同 Vercel 邏輯一樣,poll 嘅 endpoint 換做 https://api.netlify.com/api/v1/sites/$SITE_ID/deploys,state 認 ready、error。Cloudflare Pages 用 /accounts/.../pages/projects/$PROJECT/deployments,state 認 success。Step 2 至 4 完全唔使改 —— preview URL 出咗嚟之後嘅核實邏輯同邊個平台都無關,呢套例行核實嘅威力就喺度。
變化 2:三段 deploy(preview / staging / prod)
一個人開發,一段 deploy 夠用。Team / SaaS 通常分三段:feature branch 開 preview、merge develop 出 staging、merge main 出 prod。
/ship slash command 加參數:
/ship preview—— 只跑 smoke paths,平日修修補補用/ship staging—— smoke + Playwright + Slack 通知#dev/ship prod—— 上面全部 + 額外 Lighthouse 跑分 + Slack 通知#general+ 跑 sentry release create
喺 CLAUDE.md 寫明每段嘅關卡(譬如 prod 必須 staging 綠咗 24 小時先肯出),Claude 就唔會跳過 staging 直接出 prod。
變化 3:E-commerce checkout flow
賣嘢嘅 site,最關鍵嘅唔係首頁而係 checkout。Playwright smoke 必須跑足 add-to-cart → checkout → payment confirm(用 Stripe test mode key):
test('checkout flow 行到尾', async ({ page }) => {
await page.goto(`${BASE}/products/test-sku`)
await page.getByRole('button', { name: /加入購物車/ }).click()
await page.goto(`${BASE}/checkout`)
await page.getByLabel('Email').fill('smoke@example.com')
// Stripe test card
const card = page.frameLocator('iframe[name^="__privateStripeFrame"]')
await card.getByPlaceholder('Card number').fill('4242 4242 4242 4242')
await page.getByRole('button', { name: /落單/ }).click()
await expect(page.getByText(/多謝惠顧/)).toBeVisible({ timeout: 15000 })
})
呢條 flow 一綠,等於話收錢條 pipeline 通晒。Black Friday 前一晚 push,跑完先放心瞓。
拆解:點解 work,同邊度會仆街
跟到上面就已經用得。下面呢段係畀**想由「跑一次 OK」做到「自動化值得信、出事真係捉到」**嘅人——初學者可以跳過,唔影響你跟住做。
驗證腳本最危險嘅地方係:佢自己靜靜雞死咗,但報你綠。一個成日報綠嘅 smoke test,等於冇 test,仲衰過冇——因為你會信佢。呢套流程實際會喺呢幾個位仆街,你要預咗:
1. 撈錯個 deployment(SHA 對唔上)
Step 1 用 githubCommitSha 對你個 HEAD。但 Vercel 可能仲未收到 webhook、或者揀咗你之前一個 commit 嘅 deployment。jq 揀唔到就 URL 同 STATE 係空,STATE != READY 永遠唔成立,loop 一路行到 timeout。
- 會出事:等足 400 秒先 timeout,你以為 build 慢,其實係根本對唔到單;又或者撈到上一個舊 deploy,驗咗個舊版報你綠。
- 點救:loop 入面分開兩件事——「搵唔到呢個 SHA 嘅 deployment」同「搵到但未 READY」要有唔同 log。等夠幾轉都搵唔到 SHA 就明講「Vercel 未見到呢個 commit」早啲收手,唔好靜靜等到 timeout。
2. Smoke 用 grep 認關鍵字,太鬆會永遠綠
Step 2 靠 grep -q "$expect" 認 body。問題係好多 SPA / SSR 嘅 error page、login redirect、甚至 Vercel 自己嘅 404,都係回 HTTP 200,body 入面又啱啱好撞到你個關鍵字(譬如成版都有個 nav 寫住「月費」)。
- 會出事:page 其實壞咗,但因為關鍵字喺 layout 度照出,smoke 報綠,你完全唔知。
- 點救:關鍵字揀只會喺「正常 render」先出嘅嘢(譬如真實 plan 價錢、訂單號格式),唔好揀 header / footer 嘅字。
/api/health嗰類就明確 check JSON 入面個 field,唔好淨係 grep 個ok字。
3. Playwright 對住 preview URL,但 preview 有 protection / 認證牆
Step 3 直接 page.goto(BASE)。但 Vercel preview deployment 預設可能有 Deployment Protection(Vercel Authentication),未登入會俾 401 / 重定向去登入頁;你條 selector 點都揀唔到,test 報 fail。
- 會出事:明明個 app 冇事,但 Playwright 成片紅,你以為自己 ship 爛咗,嘥半個鐘揾錯方向。
- 點救:要驗 protected preview,就用 Vercel 嘅 protection bypass token(行內叫 automation bypass),喺 request header 或 query 帶住佢;或者改驗一個冇開 protection 嘅 branch / 環境。揀邊條路冇所謂,但一定要喺 prompt 同 CLAUDE.md 寫死,因為 Claude 唔會自動知你個 preview 鎖咗。
4. Flaky test 報紅,你慢慢就會 disable 佢
Step 3 啲 test 靠 toBeVisible、靠 timeout。網絡慢少少、cold start、Stripe iframe 慢 load,都會間中 fail。一個十次有一次無端紅嘅 smoke,比你想像中毒——因為人類對「成日嘈」嘅 alert 會自動麻木。
- 會出事:你開始見到紅都唔 click 入去睇,最後索性
/ship跳過 Playwright;真係出事嗰次,你都當佢又 flaky。 - 點救:寧願少幾條、條條穩,都好過多而 flaky。用
getByRole/getByText呢類語意 selector 唔好靠 CSS class;有 timeout 嘅就俾夠位(cold start 真係慢)。間中紅嗰條,要即刻 debug 或者剷走,唔好留住污染你對綠燈嘅信任。
5. Slack 報咗綠,但 webhook 其實 POST 失敗 Step 4 POST 去 webhook。但 webhook URL 過期、Slack channel 改咗名、rate limit,POST 會靜靜失敗。最毒嘅 case 係:smoke 真係紅,但連「報紅」嗰個 POST 都失敗——你乜都收唔到,以為冇 push 過。
- 會出事:你靠「冇消息即好消息」,但其實係通知 pipeline 自己死咗,出事你一無所知。
- 點救:check webhook POST 嘅 HTTP response code,POST 唔到 2xx 就要喺 terminal 大聲報錯、non-zero exit,唔好當成功。
/ship跑完一定要喺你眼前留返一句明確總結,唔好淨係靠 Slack——Slack 收唔到嗰次,正正係你最需要知嗰次。
呢幾個位,就係「跑一次 smoke OK」同「半夜出事真係 ping 醒你」之間嘅距離。一套你信得過嘅驗證,前提係佢識報紅;一套只識報綠嘅驗證,係安慰劑。
一個心態
部署唔係「
git push完就算」,係「用戶真係見到嘅嘢同你 staging 一樣」先算。中間嗰段差距,就係靠呢套例行核實補返。
最後提醒:
- ✅ 先做 step 1 至 2(poll + curl smoke)。15 分鐘工夫,已經捉到八成 regression。
- ⚠️ Playwright smoke 要狠手揀。多過 5 條會 flaky、會煩,最後畀你自己 disable 咗。三條最關鍵嘅夠。
- 🎯 Slack 通知一定要連 preview URL。出事嗰陣你撳一下就跳到出事嗰版,唔使再揾。
下次禮拜五夜晚 push,落街食飯前打 /ship 一句。返到屋企 Slack 已經話畀你聽生定死 —— 而唔係等你啲用戶話畀你聽。
文中工具 · 連結
- Claude Code CLI· 付費
開發者用 — terminal 入面同 Claude pair coding
- Vercel· 免費 / 付費 plan
Next.js 部署平台 — 呢個 site 都用緊
- Slack· 免費 / 付費 plan
Team chat — 多嘢 channel 嗰種
睇完想同 Claude 一齊行一次?
撳一撳,就將成段 tutor 指示(連埋成篇文嘅內容)抄入剪貼簿。 貼入 Claude.ai 或 Claude Desktop,佢會用廣東話帶你一步一步行, 每步問你填關鍵位,最後畀返一個專為你情況寫嘅 prompt 帶走。
- 創作者 · 35 分鐘
Claude Code 同 git 點同步:branch 命名 / commit 文 / PR description 自動跟團隊規矩
Claude 預設嘅 commit / PR 寫法「OK 但唔似你個團隊」。教你用 CLAUDE.md + slash command + hooks 令 Claude 自動跟 conventional commits、branch naming、PR template,仲教 rebase 救命招。
- 創作者 · 45 分鐘
Claude Code 唔使開 terminal 都跑得:headless + cron 起夜貓自動化 workflow
Claude Code 唔淨係 interactive。Headless mode 可以入 cron、launchd、GitHub Actions、npm script,凌晨自動跑 code health、dep 審查、PR review。教 4 個真實設定 + 3 個部署變化。
- 創作者 · 25 分鐘
Claude Code Hooks:自動排版 / 自動 test / 自動 block 危險指令
成日唔記得跑 prettier,commit 就 lint fail。或者擔心 Claude 突然跑 `rm -rf /`。Hooks 解決:PreToolUse 阻破壞性指令,PostToolUse 自動排版。教你 setup 5 個必備 hook + 邊個事件對邊類任務。