Repository navigation
Protected staging E2E #843
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: Protected staging E2E | |
| on: | |
| workflow_run: | |
| workflows: ["Quality"] | |
| types: [completed] | |
| workflow_dispatch: | |
| permissions: | |
| # Manual dispatch queries workflow runs for the selected main commit. | |
| 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: | |
| if: >- | |
| ( | |
| (github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main') || | |
| (github.event_name == 'workflow_dispatch' && 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.event_name == 'workflow_run' && github.event.workflow_run.head_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.event_name == 'workflow_run' && github.event.workflow_run.head_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 | |
| ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| - name: Verify selected commit passed the quality workflow | |
| if: github.event_name == 'workflow_dispatch' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| shell: bash | |
| run: | | |
| successful_runs="$(gh api \ | |
| "repos/${GITHUB_REPOSITORY}/actions/workflows/deploy.yml/runs?head_sha=${GITHUB_SHA}&event=push&status=success&per_page=1" \ | |
| --jq '.workflow_runs | length')" | |
| if [ "$successful_runs" -ne 1 ]; then | |
| echo "::error::The selected main commit has no successful push run of the Quality workflow" | |
| exit 1 | |
| fi | |
| - 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 |