vercel-deploy について
既存のVercelプロジェクトをローカルディレクトリから本番環境へデプロイする、段階的なスキルです。明示的なプロジェクト情報を要求して各段階を検証し、失敗時は停止します。プロジェクトの新規作成や暗黙の状態には依存しません。
主な機能
- 01ディレクトリとプロジェクト情報の明示
- 02Vercel APIによるプロジェクト連携の確認
- 03CLIによる非対話型デプロイ
- 04デプロイがREADYまたはERRORになるまで確認
- 05本番ドメインの応答検証
活用シーン
- 01ローカルのチェックアウトから既存プロジェクトをデプロイ
- 02設定済みドメインへの本番デプロイを検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fellowship-dev/ dogfooded-skills --skill vercel-deployインストール後、Claude Code で/vercel-deployを入力して読み込まれたか確認します。
詳細
目的
ローカルディレクトリから既存の Vercel プロジェクトを本番環境にデプロイします。ハングせず、新しいプロジェクトを作成せず、暗黙の状態を前提としないように設計されています。
必須シークレット
環境変数(crew のシークレットストアから)として利用できる必要があります。
VERCEL_TOKEN— デプロイ権限を持つアクセストークン(実際のシークレットはこれだけです)
必須入力
すべての入力は、スキルの呼び出し時またはタスクの説明内で KEY=value ペアとして渡されます。引数から解析してください。これらを環境変数から読み取らないでください。
DEPLOY_DIR— デプロイ可能なディレクトリのパス(例:./ui、リポジトリのルート)PROD_DOMAIN— 想定される本番ドメイン(例:pylot.fellowship.dev)VERCEL_ORG_ID— 組織/チーム ID(team_で始まります)VERCEL_PROJECT_ID— プロジェクト ID(prj_で始まります)
これらの値はシークレットではなく、リポジトリのプレイブック(GET /admin/playbooks/<repo>)に記載されています。オペレーターはプレイブックを読み取り、このスキルを呼び出す際に値を渡します。このスキルの CLI フローを通じてのみデプロイしてください。Vercel の Git 自動デプロイ連携は、上記のプレイブック由来の入力と「禁止事項」に記載されたチェックを迂回するため、決して利用しないでください。
任意入力
DEPLOY_BRANCH— デプロイ元の Git ブランチ。指定がない場合はデフォルトでmainです。デプロイ前に必ずチェックアウトしてプルします。DEPLOY_AUTHOR— 検証済み Vercel チームメンバーの Git 名(例:maxfindel)。DEPLOY_EMAILとともに指定された場合、ステージ 00 でデプロイ前にコミットの作成者を修正します。Vercel がコミット作成者のチームメンバーシップを強制する場合にのみ必要です。DEPLOY_EMAIL— 作成者に一致する Git メールアドレス。DEPLOY_AUTHORを設定する場合は必須です。
禁止事項
- Vercel プロジェクトを新規作成しないでください。 存在しない場合は停止し、失敗を報告してください。
- プロジェクトが自動的にリンクされると想定しないでください。 常に
.vercel/project.jsonを明示的に確認または書き込んでください。 - Git リポジトリを Vercel プロジェクトに接続しないでください。 すべてのデプロイは CLI によるプッシュであり、Git をトリガーとするものではありません。
--tokenと--yesを指定せずにvercelを実行しないでください。 指定しないと CLI が永久にハングします。vercel linkを実行しないでください。 対話的な入力を求められます。.vercel/project.jsonを直接書き込んでください。vercel project createまたは同等のコマンドを実行しないでください。 そのための手順は別にあります。
ステージの概要
| ステージ | 名前 | 目的 |
|---|---|---|
| 00 | author-fix | コミット作成者を修正します(DEPLOY_AUTHOR が設定されている場合。任意で、それ以外はスキップ) |
| 01 | preflight | シークレットとツールを確認し、デプロイのコンテキストを算出します |
| 02 | link | .vercel/project.json が存在することを確認し、API 経由でプロジェクトを検証します |
| 03 | deploy | 対話なしで vercel deploy --prod を実行します |
| 04 | poll | デプロイが READY または ERROR になるまで Vercel API をポーリングします |
| 05 | verify | 本番ドメインに到達できることを確認し、結果を出力します |
ステージ 00 — 作成者の修正(条件付き)
DEPLOY_AUTHOR が指定されていない場合は、このステージをスキップしてください。 すべての Vercel プロジェクトでコミット作成者のチームメンバーシップが必須というわけではありません。
Vercel がこれを強制する場合(TEAM_ACCESS_REQUIRED/seatBlock)、ボットアカウント(例:fry-lobster)によって CLI デプロイがブロックされます。チームメンバーを作成者とする空のコミットで修正します。
# Only run if DEPLOY_AUTHOR is set in the skill params
if [ -n "$DEPLOY_AUTHOR" ] && [ -n "$DEPLOY_EMAIL" ]; then
HEAD_AUTHOR=$(git log -1 --format='%an')
if [ "$HEAD_AUTHOR" != "$DEPLOY_AUTHOR" ]; then
echo "[vercel-deploy] HEAD author '$HEAD_AUTHOR' is not a Vercel team member."
echo "[vercel-deploy] Creating empty commit with author '$DEPLOY_AUTHOR' to unblock deploy."
git commit --allow-empty \
--author="$DEPLOY_AUTHOR <$DEPLOY_EMAIL>" \
-m "chore: vercel deploy author fix (empty commit)"
git push origin HEAD
fi
else
echo "[vercel-deploy] Stage 00 skipped — no DEPLOY_AUTHOR configured"
fi
ステージ 01 — 事前確認
すべてのシークレットと入力が存在し、ツールがインストールされていることを確認してから、デプロイブランチをチェックアウトします。
# Verify required secret (from crew secret store — the only real secret)
[ -z "$VERCEL_TOKEN" ] && { echo "[vercel-deploy] MISSING secret: VERCEL_TOKEN"; exit 1; }
# Verify required inputs (parsed from skill args / task description — from the repo playbook)
[ -z "$DEPLOY_DIR" ] && { echo "[vercel-deploy] MISSING input: DEPLOY_DIR — check the repo playbook"; exit 1; }
[ -z "$PROD_DOMAIN" ] && { echo "[vercel-deploy] MISSING input: PROD_DOMAIN — check the repo playbook"; exit 1; }
[ -z "$VERCEL_ORG_ID" ] && { echo "[vercel-deploy] MISSING input: VERCEL_ORG_ID — check the repo playbook"; exit 1; }
[ -z "$VERCEL_PROJECT_ID" ] && { echo "[vercel-deploy] MISSING input: VERCEL_PROJECT_ID — check the repo playbook"; exit 1; }
# Optional: warn if author-fix params are incomplete (both or neither)
if [ -n "$DEPLOY_AUTHOR" ] && [ -z "$DEPLOY_EMAIL" ]; then
echo "[vercel-deploy] WARNING: DEPLOY_AUTHOR set but DEPLOY_EMAIL missing — Stage 00 will be skipped"
fi
# Verify vercel CLI is available
command -v vercel >/dev/null 2>&1 || command -v npx >/dev/null 2>&1 || {
echo "[vercel-deploy] vercel CLI not available — install with: npm i -g vercel"
exit 1
}
# Verify deploy directory exists
[ -d "$DEPLOY_DIR" ] || { echo "[vercel-deploy] DEPLOY_DIR not found: $DEPLOY_DIR"; exit 1; }
# Checkout and pull deploy branch
DEPLOY_BRANCH="${DEPLOY_BRANCH:-main}"
git checkout "$DEPLOY_BRANCH" && git pull origin "$DEPLOY_BRANCH" || {
echo "[vercel-deploy] Failed to checkout/pull $DEPLOY_BRANCH"
exit 1
}
# Save state for downstream stages
cat > /tmp/vercel-deploy-ctx.env <<EOF
DEPLOY_BRANCH=$DEPLOY_BRANCH
DEPLOY_DIR=$DEPLOY_DIR
PROD_DOMAIN=$PROD_DOMAIN
EOF
echo "[vercel-deploy] Stage 01 complete — preflight passed, branch: $DEPLOY_BRANCH"
ステージ 02 — リンク
.vercel/project.json を直接書き込み(vercel link は使用しないでください)、API 経由でプロジェクトが存在することを確認します。
source /tmp/vercel-deploy-ctx.env
# Write project link file directly
mkdir -p "$DEPLOY_DIR/.vercel"
cat > "$DEPLOY_DIR/.vercel/project.json" <<EOF
{
"orgId": "$VERCEL_ORG_ID",
"projectId": "$VERCEL_PROJECT_ID"
}
EOF
# Verify project exists via Vercel API
PROJECT_JSON=$(curl -sf \
-H "Authorization: Bearer $VERCEL_TOKEN" \
"https://api.vercel.com/v9/projects/$VERCEL_PROJECT_ID?teamId=$VERCEL_ORG_ID")
[ -z "$PROJECT_JSON" ] && {
echo "[vercel-deploy] Stage 02 failed: project $VERCEL_PROJECT_ID not found — STOP, do not create"
exit 1
}
PROJECT_NAME=$(echo "$PROJECT_JSON" | python3 -c \
"import sys,json; d=json.load(sys.stdin); print(d.get('name','unknown'))" 2>/dev/null || echo "unknown")
echo "PROJECT_NAME=$PROJECT_NAME" >> /tmp/vercel-deploy-ctx.env
echo "[vercel-deploy] Stage 02 complete — linked to project '$PROJECT_NAME' ($VERCEL_PROJECT_ID)"
ステージ 03 — デプロイ
対話なしで vercel deploy --prod を実行します。--token と --yes は必ず指定してください。指定しないと CLI がハングします。
source /tmp/vercel-deploy-ctx.env
cd "$DEPLOY_DIR"
DEPLOY_OUTPUT=$(npx vercel deploy --prod --token="$VERCEL_TOKEN" --yes 2>&1)
DEPLOY_EXIT=$?
echo "$DEPLOY_OUTPUT"
if [ $DEPLOY_EXIT -ne 0 ]; then
echo "[vercel-deploy] Stage 03 failed: vercel deploy exited $DEPLOY_EXIT"
exit 1
fi
# Extract deployment URL (last https:// line in output)
DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -E '^https://' | tail -1)
[ -z "$DEPLOY_URL" ] && {
echo "[vercel-deploy] Stage 03 failed: no deployment URL found in vercel output"
exit 1
}
echo "DEPLOY_URL=$DEPLOY_URL" >> /tmp/vercel-deploy-ctx.env
echo "[vercel-deploy] Stage 03 complete — deployment URL: $DEPLOY_URL"
ステージ 04 — ポーリング
デプロイが READY または ERROR になるまで Vercel API をポーリングします。120 秒後にタイムアウトします。
source /tmp/vercel-deploy-ctx.env
MAX_WAIT=120
ELAPSED=0
DEPLOY_HOST=$(echo "$DEPLOY_URL" | sed 's|https://||')
while [ $ELAPSED -lt $MAX_WAIT ]; do
DEPLOY_STATE=$(curl -sf \
-H "Authorization: Bearer $VERCEL_TOKEN" \
"https://api.vercel.com/v13/deployments?url=${DEPLOY_HOST}&teamId=$VERCEL_ORG_ID&limit=1" \
| python3 -c \
"import sys,json; d=json.load(sys.stdin); deps=d.get('deployments',[]); print(deps[0].get('state','UNKNOWN') if deps else 'NOT_FOUND')" \
2>/dev/null || echo "API_ERROR")
echo "[vercel-deploy] Deploy state: $DEPLOY_STATE (${ELAPSED}s elapsed)"
case "$DEPLOY_STATE" in
READY)
echo "[vercel-deploy] Stage 04 complete — deployment READY"
break
;;
ERROR|CANCELED)
echo "[vercel-deploy] Stage 04 failed: deployment reached terminal state $DEPLOY_STATE"
exit 1
;;
*)
sleep 10
ELAPSED=$((ELAPSED + 10))
;;
esac
done
[ $ELAPSED -ge $MAX_WAIT ] && {
echo "[vercel-deploy] Stage 04 failed: timed out after ${MAX_WAIT}s — last state: $DEPLOY_STATE"
exit 1
}
ステージ 05 — 検証
本番ドメインにアクセスできること(HTTP 200、301、または 302)を確認してから、結果を出力します。
source /tmp/vercel-deploy-ctx.env
VERIFY_URL="https://$PROD_DOMAIN"
HTTP_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" --max-time 15 "$VERIFY_URL" 2>/dev/null || echo "000")
echo "[vercel-deploy] $VERIFY_URL → HTTP $HTTP_STATUS"
case "$HTTP_STATUS" in
200|301|302)
echo "[vercel-deploy] Stage 05 complete — production domain verified"
;;
*)
echo "[vercel-deploy] Stage 05 failed: $VERIFY_URL returned HTTP $HTTP_STATUS"
exit 1
;;
esac
検証コマンドの実行後、対応する解決済みマーカーを最後のアシスタント行として出力してください(Bash から出力せず、コードフェンスにも入れないでください)。
- 検証成功:[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy succeeded" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=success
- 失敗:[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy failed at stage 05: $PROD_DOMAIN returned HTTP $HTTP_STATUS" status=failed
実行モデル
- ステージは順番どおりに実行してください。次に進む前に、各ステージのチェックポイントを通過する必要があります。
- ステージをスキップしないでください。 すべてのチェックポイントを検証します。
- 速やかに失敗を報告してください。 チェックポイントが失敗した場合は停止し、
status=failedを報告してください。 - ステージ間では、
/tmp/vercel-deploy-ctx.envを介して状態を渡します。
重要なルール
- 順次実行のみです。ステージ N のチェックポイントを通過してから、ステージ N+1 を開始してください。
- スキップは禁止です。すべてのステージを実行し、すべてのチェックポイントを検証してください。
- 速やかに失敗を報告してください。チェックポイントが失敗した場合は停止し、報告してください。
vercelまたはnpx vercelの呼び出しには、必ず--token="$VERCEL_TOKEN" --yesを含めてください。
エラー処理
いずれかのステージが失敗した場合:
- 失敗したステージと正確なエラーを出力してください
- 利用可能な場合は、Vercel API のレスポンス本文を含めてください
[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy failed at stage <NN>: <reason>" status=failedを出力してください- 自動的に再試行しないでください
結果
成功した場合、次を出力してください:
[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy succeeded" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=success