Skip to content

Deploy to Cloudflare #719

Deploy to Cloudflare

Deploy to Cloudflare #719

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
# Upload one production Worker version. A version includes code, assets,
# bindings and compatibility settings; the upload does not move traffic.
- 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: Capture active production version
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: Upload production version without moving traffic
id: upload
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 }}
WRANGLER_OUTPUT_FILE_PATH: ${{ runner.temp }}/wrangler-upload.jsonl
run: |
test -s dist/client/index.html
test -s dist/sentry_mcp/wrangler.json
pnpm exec wrangler versions upload --experimental-auto-create=false \
--config dist/sentry_mcp/wrangler.json \
--assets dist/client \
--message "toolkit-mcp:$GITHUB_RUN_ID:$GITHUB_RUN_ATTEMPT:$TESTED_SHA:upload"
- name: Identify uploaded production version
id: uploaded
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TESTED_SHA: ${{ github.event.workflow_run.head_sha }}
WRANGLER_OUTPUT_FILE_PATH: ${{ runner.temp }}/wrangler-upload.jsonl
run: node scripts/cloudflare-deployment.mjs uploaded
- name: Require tested revision on main before staging
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 before candidate staging; refusing deployment.' >&2
exit 1
fi
- name: Stage exact candidate at zero percent
id: stage
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 stage
- name: Wait for staged candidate to propagate
run: sleep 30
- name: Smoke test exact production Worker version
id: candidate_smoke_tests
env:
PREVIEW_URL: https://mcp.sentry.dev
CLOUDFLARE_WORKER_NAME: sentry-mcp
CLOUDFLARE_VERSION_OVERRIDE: ${{ steps.uploaded.outputs.candidate_version }}
EXPECTED_VERSION_ID: ${{ steps.uploaded.outputs.candidate_version }}
run: |
cd packages/smoke-tests
pnpm test:ci
- name: Publish Candidate Smoke Test Report
uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857
if: always() && steps.candidate_smoke_tests.outcome != 'skipped'
with:
report_paths: "packages/smoke-tests/tests.junit.xml"
check_name: "Candidate Smoke Test Results"
fail_on_failure: false
- name: Require tested revision on main before promotion
if: steps.candidate_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 candidate testing; refusing promotion.' >&2
exit 1
fi
- name: Promote tested version to production
id: promote
if: steps.candidate_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 promote
- name: Verify production deployment ownership
id: verify_production
if: steps.promote.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
EXPECTED_VERSION_ID: ${{ steps.uploaded.outputs.candidate_version }}
run: |
echo "Running smoke tests on production..."
cd packages/smoke-tests
pnpm test:ci
- name: Recover captured previous version if owned transition fails
if: failure() && steps.uploaded.outcome == 'success' && steps.stage.outcome != 'skipped'
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
# Report publication must not turn a verified promotion into a rollback.
- 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