Loading...
Loading...
Use when deploying an existing Vercel project to production non-interactively.
npx skill4agent add fellowship-dev/dogfooded-skills vercel-deployVERCEL_TOKENKEY=valueDEPLOY_DIR./uiPROD_DOMAINpylot.fellowship.devVERCEL_ORG_IDteam_VERCEL_PROJECT_IDprj_GET /admin/playbooks/<repo>DEPLOY_BRANCHmainDEPLOY_AUTHORmaxfindelDEPLOY_EMAILDEPLOY_EMAILDEPLOY_AUTHOR.vercel/project.jsonvercel--token--yesvercel link.vercel/project.jsonvercel project create| Stage | Name | Purpose |
|---|---|---|
| 00 | author-fix | Fix commit author if DEPLOY_AUTHOR is set (optional, skipped otherwise) |
| 01 | preflight | Verify secrets, tools, compute deploy context |
| 02 | link | Ensure |
| 03 | deploy | Run |
| 04 | poll | Poll Vercel API until deployment reaches READY or ERROR |
| 05 | verify | Confirm production domain is reachable, emit outcome |
DEPLOY_AUTHORTEAM_ACCESS_REQUIREDseatBlockfry-lobster# 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# 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".vercel/project.jsonvercel linksource /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)"vercel deploy --prod--token--yessource /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"READYERRORsource /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
}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 "[pylot] outcome=\"vercel-deploy succeeded\" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=done"
;;
*)
echo "[vercel-deploy] Stage 05 failed: $VERIFY_URL returned HTTP $HTTP_STATUS"
echo "[pylot] outcome=\"vercel-deploy failed at stage 05: $PROD_DOMAIN returned HTTP $HTTP_STATUS\" status=failed"
exit 1
;;
esacstatus=failed/tmp/vercel-deploy-ctx.envvercelnpx vercel--token="$VERCEL_TOKEN" --yes[pylot] outcome="vercel-deploy failed at stage <NN>: <reason>" status=failed[pylot] outcome="vercel-deploy succeeded" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=done