Skip to content

Implement canary deployment strategy with separate workers #5

Implement canary deployment strategy with separate workers

Implement canary deployment strategy with separate workers #5

Workflow file for this run

name: Canary
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
statuses: write # Required for creating commit statuses
jobs:
preview:
name: Deploy and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
# 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@v4
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
- name: Build
run: pnpm build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
- name: Create GitHub Deployment
id: deployment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.payload.pull_request.head.sha,
environment: 'canary',
description: `Canary deployment for PR #${{ github.event.pull_request.number }}`,
transient_environment: true,
production_environment: false,
required_contexts: [],
auto_merge: false,
payload: JSON.stringify({
pr_number: ${{ github.event.pull_request.number }},
branch: '${{ github.head_ref }}',
deployed_at: new Date().toISOString()
})
});
core.setOutput('id', deployment.data.id);
// Set initial status
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: deployment.data.id,
state: 'in_progress',
description: 'Deploying canary environment',
auto_inactive: true // Auto-mark previous deployments as inactive
});
- name: Deploy Canary Version
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: packages/mcp-cloudflare
command: versions upload --preview-alias="pr-${{ github.event.pull_request.number }}" --message="PR #${{ github.event.pull_request.number }} - ${{ github.head_ref }}"
packageManager: pnpm
- name: Get Canary URL
id: canary_url
run: |
# Preview alias URL format: <alias>-<worker-name>.<subdomain>.workers.dev
CANARY_URL="https://pr-${{ github.event.pull_request.number }}-sentry-mcp.getsentry.workers.dev"
echo "url=$CANARY_URL" >> $GITHUB_OUTPUT
echo "Canary URL: $CANARY_URL"
- name: Update Deployment Status
if: always()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const deploySuccess = '${{ steps.deploy.outcome }}' === 'success';
const canaryUrl = '${{ steps.canary_url.outputs.url }}';
// Update GitHub deployment status
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.id }},
state: deploySuccess ? 'success' : 'failure',
environment_url: deploySuccess ? canaryUrl : undefined,
description: deploySuccess ? 'Canary deployed successfully' : 'Deployment failed',
auto_inactive: false
});
// Also create a commit status for visibility
try {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: deploySuccess ? 'success' : 'failure',
target_url: deploySuccess ? canaryUrl : `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
description: deploySuccess ? 'Canary environment ready' : 'Deployment failed',
context: 'Canary URL'
});
} catch (error) {
console.error('Failed to create commit status:', error);
}
- name: Wait for deployment to propagate
if: success()
run: |
echo "Waiting for deployment to propagate..."
sleep 30
- name: Run Smoke Tests
id: smoke_tests
if: success()
working-directory: packages/smoke-tests
env:
PREVIEW_URL: ${{ steps.canary_url.outputs.url }}
run: |
echo "Running smoke tests against $PREVIEW_URL"
pnpm test:ci
- name: Publish Test Report
uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857
if: always() && steps.smoke_tests.outcome != 'skipped'
with:
report_paths: "**/tests.junit.xml"
check_name: "Smoke Test Results"
fail_on_failure: false
- name: Update Commit Status with Test Results
if: always() && steps.deploy.outcome == 'success'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const testsSuccess = '${{ steps.smoke_tests.outcome }}' === 'success';
let state = testsSuccess ? 'success' : 'failure';
let description = testsSuccess ? 'Passed' : 'Failed';
try {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: state,
target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
description: description,
context: 'Smoke Tests'
});
} catch (error) {
console.error('Failed to create commit status:', error);
}