Skip to content

Protected staging E2E #852

Protected staging E2E

Protected staging E2E #852

Workflow file for this run

name: Protected staging E2E
# Daily, and on demand from main. It used to follow every push to main, and
# about half of each run's billed minutes went to waiting for the staging
# sync. A frontend merge is checked live by the production canary; before a
# backend production deploy, dispatch this from main once staging runs the
# merge: `gh workflow run staging-e2e.yml --ref main`.
on:
schedule:
- cron: "11 7 * * *"
workflow_dispatch:
permissions:
# Reads the Quality runs for the main commit under test.
actions: read
contents: read
# Waits for the staging-sync commit status set by the VPS sync.
statuses: read
concurrency:
group: protected-staging-e2e-main
cancel-in-progress: false
jobs:
# Staging is one shared backend that the VPS sync (docs/staging.md) moves to
# each new main commit and marks with a staging-sync commit status. Test a
# commit only once staging runs it, and skip a commit that main has already
# moved past: staging runs the newer backend, and the newer commit's run
# tests it.
staging-sync-gate:
# A scheduled run's ref is main; a dispatch must choose main.
if: github.ref == 'refs/heads/main' && vars.STAGING_E2E_ENABLED == 'true'
runs-on: ubuntu-latest
timeout-minutes: 25
outputs:
current: ${{ steps.wait.outputs.current }}
steps:
- name: Wait for staging to sync this commit
id: wait
env:
GH_TOKEN: ${{ github.token }}
SHA: ${{ github.sha }}
shell: bash
run: |
for _ in $(seq 1 40); do
main="$(gh api "repos/${GITHUB_REPOSITORY}/commits/main" --jq .sha)"
if [ "$main" != "$SHA" ]; then
echo "::notice::main moved on to ${main}; staging runs that backend, and its own run tests it. Skipping ${SHA}."
echo "current=false" >> "$GITHUB_OUTPUT"
exit 0
fi
state="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${SHA}/statuses" \
--jq '[.[] | select(.context == "staging-sync")][0].state // "missing"')"
case "$state" in
success)
echo "Staging is synced with ${SHA}."
echo "current=true" >> "$GITHUB_OUTPUT"
exit 0 ;;
failure|error)
echo "::error::Staging sync failed for ${SHA}. See the open 'Staging is out of sync with main' issue."
exit 1 ;;
esac
sleep 30
done
echo "::error::No staging-sync status for ${SHA} after 20 minutes. Is debatelab-staging-sync.timer running on the VPS? See docs/staging.md."
exit 1
staging-e2e:
needs: staging-sync-gate
if: needs.staging-sync-gate.outputs.current == 'true'
runs-on: ubuntu-latest
# Bounds how long the VPS sync holds off for this run (ops/staging-sync/run.sh).
timeout-minutes: 30
environment: staging
steps:
# "Re-run failed jobs" skips the gate, so check again on every attempt.
- name: Confirm staging still runs this commit
env:
GH_TOKEN: ${{ github.token }}
SHA: ${{ github.sha }}
shell: bash
run: |
main="$(gh api "repos/${GITHUB_REPOSITORY}/commits/main" --jq .sha)"
state="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${SHA}/statuses" \
--jq '[.[] | select(.context == "staging-sync")][0].state // "missing"')"
if [ "$main" != "$SHA" ] || [ "$state" != "success" ]; then
echo "::error::Staging no longer runs ${SHA} (main is ${main}, staging-sync is ${state}). Rerun the newest main commit's run instead."
exit 1
fi
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# A commit merged just before the schedule can reach staging while its
# Quality run is still queued or running, so wait for it to finish.
- name: Verify the commit passed the quality workflow
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
for _ in $(seq 1 30); do
read -r status conclusion < <(gh api \
"repos/${GITHUB_REPOSITORY}/actions/workflows/deploy.yml/runs?head_sha=${GITHUB_SHA}&event=push&per_page=1" \
--jq '.workflow_runs[0] // {} | "\(.status // "missing") \(.conclusion // "none")"') || true
case "$status" in
completed)
if [ "$conclusion" = success ]; then echo "Quality passed for ${GITHUB_SHA}."; exit 0; fi
echo "::error::Quality ended in ${conclusion} for main commit ${GITHUB_SHA}"
exit 1 ;;
missing)
echo "::error::Main commit ${GITHUB_SHA} has no push run of the Quality workflow"
exit 1 ;;
esac
sleep 20
done
echo "::error::Quality did not finish for ${GITHUB_SHA} within 10 minutes"
exit 1
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Install Chromium
run: npx playwright install --with-deps chromium
- name: Verify protected variables are present
shell: bash
env:
STAGING_SUPABASE_URL: ${{ secrets.STAGING_SUPABASE_URL }}
STAGING_SUPABASE_ANON_KEY: ${{ secrets.STAGING_SUPABASE_ANON_KEY }}
STAGING_DEMO_CODE: ${{ secrets.STAGING_DEMO_CODE }}
run: |
missing=0
for name in STAGING_SUPABASE_URL STAGING_SUPABASE_ANON_KEY STAGING_DEMO_CODE; do
if [ -z "${!name}" ]; then
echo "::error::$name is not configured in the staging environment"
missing=1
fi
done
exit "$missing"
- name: Protected staging browser tests
env:
CI: 1
PORT: 18080
PLAYWRIGHT_BASE_URL: http://127.0.0.1:18080
STAGING_SUPABASE_URL: ${{ secrets.STAGING_SUPABASE_URL }}
STAGING_SUPABASE_ANON_KEY: ${{ secrets.STAGING_SUPABASE_ANON_KEY }}
STAGING_DEMO_CODE: ${{ secrets.STAGING_DEMO_CODE }}
run: npm run test:e2e:staging