Skip to content

Deploy to Cloudflare #699

Deploy to Cloudflare

Deploy to Cloudflare #699

Workflow file for this run

name: Deploy to Cloudflare
permissions:
contents: read
deployments: write
checks: write
on:
workflow_run:
workflows: ["Test"]
types:
- completed
branches: [main]
concurrency:
group: mcp-production-deploy
cancel-in-progress: false
jobs:
deploy:
name: Deploy to Cloudflare
runs-on: ubuntu-latest
environment: production
if: >-
${{ github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_repository.id == github.event.repository.id &&
github.event.workflow_run.head_branch == 'main' }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false
- name: Require tested revision on main
env:
GH_TOKEN: ${{ github.token }}
EXPECTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
current_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/main" --jq '.object.sha')"
if [[ "$current_sha" != "$EXPECTED_SHA" ]]; then
echo 'The tested revision is no longer at main; refusing deployment.' >&2
exit 1
fi
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22"
# pnpm/action-setup@v4
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
name: Install pnpm
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV"
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
# === BUILD AND DEPLOY CANARY WORKER ===
- name: Build
run: pnpm --filter '@sentry/mcp-cloudflare...' run build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: production
- name: Deploy to Canary Worker
id: deploy_canary
working-directory: packages/mcp-cloudflare
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: pnpm exec wrangler deploy --config wrangler.canary.jsonc
- name: Wait for Canary to Propagate
if: success()
run: |
echo "Waiting 30 seconds for canary deployment to propagate..."
sleep 30
# === SMOKE TEST CANARY ===
- name: Run Smoke Tests on Canary
id: canary_smoke_tests
if: success()
env:
PREVIEW_URL: https://sentry-mcp-canary.getsentry.workers.dev
run: |
echo "Running smoke tests against canary worker..."
cd packages/smoke-tests
pnpm test:ci
- name: Publish Canary Smoke Test Report
uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857
if: always() && steps.canary_smoke_tests.outcome != 'skipped'
with:
report_paths: "packages/smoke-tests/tests.junit.xml"
check_name: "Canary Smoke Test Results"
fail_on_failure: false
# === DEPLOY PRODUCTION WORKER (only if canary tests pass) ===
- name: Require tested revision on main before production
if: steps.canary_smoke_tests.outcome == 'success'
env:
GH_TOKEN: ${{ github.token }}
EXPECTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
current_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/main" --jq '.object.sha')"
if [[ "$current_sha" != "$EXPECTED_SHA" ]]; then
echo 'Main advanced during canary testing; refusing production deployment.' >&2
exit 1
fi
- name: Capture active production version
if: steps.canary_smoke_tests.outcome == 'success'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: node scripts/cloudflare-deployment.mjs capture
- name: Deploy to Production Worker
id: deploy_production
if: steps.canary_smoke_tests.outcome == 'success'
working-directory: packages/mcp-cloudflare
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: pnpm exec wrangler deploy --message "toolkit-mcp:$GITHUB_RUN_ID:$GITHUB_RUN_ATTEMPT:$TESTED_SHA"
- name: Verify production deployment ownership
id: verify_production
if: steps.deploy_production.outcome == 'success'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: node scripts/cloudflare-deployment.mjs verify
- name: Wait for Production to Propagate
if: steps.verify_production.outcome == 'success'
run: |
echo "Waiting 30 seconds for production deployment to propagate..."
sleep 30
# === SMOKE TEST PRODUCTION ===
- name: Run Smoke Tests on Production
id: production_smoke_tests
if: steps.verify_production.outcome == 'success'
env:
PREVIEW_URL: https://mcp.sentry.dev
run: |
echo "Running smoke tests on production..."
cd packages/smoke-tests
pnpm test:ci
- name: Publish Production Smoke Test Report
uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857
if: always() && steps.production_smoke_tests.outcome != 'skipped'
with:
report_paths: "packages/smoke-tests/tests.junit.xml"
check_name: "Production Smoke Test Results"
fail_on_failure: false
- name: Recover captured previous version after smoke failure
if: failure() && steps.production_smoke_tests.outcome == 'failure' && steps.verify_production.outcome == 'success'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
run: node scripts/cloudflare-deployment.mjs recover
- name: Fail Job if Production Smoke Tests Failed
if: failure() && steps.production_smoke_tests.outcome == 'failure'
run: |
echo 'Production smoke tests failed. Inspect the deployment before changing traffic.' >&2
exit 1