Vercel prod deploy #349
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Vercel prod deploy | |
| # Auto-deploy to Vercel Production on every push to main (any merged PR). | |
| # Same VERCEL_TOKEN as the staging workflow. The openbench-monitoring | |
| # control plane that originally drove this was retired 2026-07-10; the | |
| # workflow itself stays as the sole prod deploy path. | |
| # | |
| # Required GitHub secrets (Settings → Secrets and variables → Actions): | |
| # VERCEL_TOKEN — vercel.com/account/tokens, scope = mobula-labs | |
| # VERCEL_ORG_ID — already set for staging-deploy.yml | |
| # VERCEL_PROJECT_ID — already set for staging-deploy.yml | |
| on: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| concurrency: | |
| group: vercel-prod | |
| cancel-in-progress: false | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| - name: Enable pnpm via corepack | |
| run: | | |
| corepack enable | |
| corepack prepare pnpm@10 --activate | |
| pnpm --version | |
| - name: Install Vercel CLI | |
| run: npm install -g vercel@latest | |
| - name: Pull Vercel env (Production) | |
| run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | |
| - name: Build | |
| run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| - name: Deploy to Production | |
| id: deploy | |
| run: | | |
| url=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}) | |
| echo "url=$url" >> "$GITHUB_OUTPUT" | |
| echo "Production deployed at $url" | |
| # Each deployment starts with an empty ISR cache; until the | |
| # materialize worker's next sweep (≤5 min) every page hit is a | |
| # cold render. Warming here closes that window so a deploy never | |
| # ships a slow first impression. | |
| - name: Warm bench pages | |
| run: | | |
| base="https://openchainbench.com" | |
| curl -sf --max-time 30 "$base/sitemap.xml" \ | |
| | grep -o 'https://[^<]*/benchmarks/[a-z0-9-]*' \ | |
| | sort -u \ | |
| | xargs -P 4 -I{} curl -s -o /dev/null -w "%{http_code} %{time_total}s {}\n" --max-time 120 {} \ | |
| || echo "warm-up failed (non-blocking)" | |
| # Warm /api/citable explicitly: the aggregator cache is independent | |
| # from per-bench caches, and the bench-page warm-up only populates | |
| # the latter. Without this step, /api/citable's first hit after | |
| # deploy runs 26 parallel Prom queries on a cold function instance, | |
| # which has historically produced mostly-placeholder snapshots that | |
| # cached for 60s and shipped status=insufficient to LLM agents. | |
| - name: Warm citable aggregate | |
| run: | | |
| base="https://openchainbench.com" | |
| # /api/mcp is the docs path under /mcp, the live JSON-RPC | |
| # endpoint is /api/mcp/mcp (Streamable HTTP). Warm the real | |
| # endpoint so the first agent request after deploy is hot. | |
| for ep in /api/citable /api/llm-context /api/mcp/mcp; do | |
| code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 120 "$base$ep" || echo "000") | |
| echo "$code $base$ep" | |
| done | |
| # Indexability smoke test runs AFTER the warm-up steps so cold | |
| # renders cannot false-fail. If any sitemap URL returns non-200 or | |
| # carries a noindex hint (X-Robots-Tag header OR <meta name=robots>), | |
| # the next step auto-rolls back the prod alias to the previous good | |
| # deploy. This catches the class of bug that bled SEO on 2026-06-25 | |
| # (bench page 404 + noindex while still in the sitemap). | |
| - name: Sitemap indexability smoke | |
| id: smoke | |
| continue-on-error: true | |
| run: node scripts/sitemap-smoke.mjs https://openchainbench.com | |
| - name: Auto-rollback on smoke failure | |
| if: steps.smoke.outcome == 'failure' | |
| run: | | |
| echo "::error::Sitemap smoke failed. Rolling back prod alias to the previous deploy." | |
| vercel rollback --token=${{ secrets.VERCEL_TOKEN }} --scope=mobula-labs --yes | |
| # Re-fail the job so the rollback is visible in the workflow | |
| # status (a red X on the commit + an email). | |
| exit 1 | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { | |
| if [ "${{ steps.smoke.outcome }}" = "failure" ]; then | |
| echo "## Vercel prod deploy ✗ rolled back" | |
| echo "" | |
| echo "**Reason:** sitemap smoke test failed. Prod alias was reverted to the previous deploy." | |
| else | |
| echo "## Vercel prod deploy ✓" | |
| echo "" | |
| echo "**Live:** https://openchainbench.com" | |
| fi | |
| echo "" | |
| echo "**Deploy URL:** ${{ steps.deploy.outputs.url }}" | |
| echo "" | |
| echo "Commit: \`${{ github.sha }}\`" | |
| } >> "$GITHUB_STEP_SUMMARY" |