chore(deps): bump actions/checkout from 6 to 7 in the actions group #357
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
| # Layer-1 PR-gate: AUTH-004 CORS contract + magic-link round-trip | |
| # | |
| # Why: the 2026-05-29 → 2026-05-30 login outage had THREE stacked failures | |
| # (web missing /auth/exchange POST, web sending Accept:application/json, | |
| # api missing access-control-allow-credentials). The worker auth-probe | |
| # catches it 5 min post-deploy; the OpenAPI contract CI catches schema | |
| # drift at PR time. Neither catches it END-TO-END IN A BROWSER before | |
| # merge. This workflow does — see e2e/auth-contract.spec.ts. | |
| # | |
| # What it runs: | |
| # - Three Chromium-driven asserts against PROD api.instanode.dev + | |
| # instanode.dev (override with E2E_API_URL / E2E_WEB_ORIGIN). | |
| # - Bounded under 2 minutes (live tests are ~3s; lion's share is | |
| # npm ci + Chromium download). | |
| # | |
| # Triggers: | |
| # - pull_request to main (any path — auth surface is implicit to every PR). | |
| # - push to main (post-merge canary). | |
| # - workflow_dispatch (manual smoke). | |
| # - repository_dispatch with type `auth-contract-e2e-from-api` (fired by | |
| # the api repo's CI workflow when an api PR opens/pushes — closes the | |
| # cross-repo gap, since an api-side CORS regression would otherwise | |
| # ship without this gate firing). | |
| name: Auth contract E2E (PR gate) | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| api_url: | |
| description: 'Override E2E_API_URL' | |
| required: false | |
| default: 'https://api.instanode.dev' | |
| web_origin: | |
| description: 'Override E2E_WEB_ORIGIN' | |
| required: false | |
| default: 'https://instanode.dev' | |
| run_roundtrip: | |
| description: 'Also run the cookie-exchange ROUND-TRIP spec (needs a NON-prod api + E2E_JWT_SECRET secret)' | |
| type: boolean | |
| required: false | |
| default: false | |
| repository_dispatch: | |
| types: [auth-contract-e2e-from-api] | |
| concurrency: | |
| # Cancel an in-flight run when a new commit lands on the same PR. | |
| # Different PRs / triggers still run in parallel. | |
| group: auth-contract-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| auth-contract-e2e: | |
| name: Auth contract smoke against prod | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| env: | |
| # SECURITY: never interpolate raw repository_dispatch client_payload | |
| # values into `run:` scripts (workflow-injection sink). Pipe through | |
| # env: and let the validate step sanitise. workflow_dispatch inputs | |
| # are gated by repo write permission so are lower-risk but get the | |
| # same treatment for consistency. | |
| RAW_API_URL: ${{ github.event.inputs.api_url || github.event.client_payload.api_url || '' }} | |
| RAW_WEB_ORIGIN: ${{ github.event.inputs.web_origin || github.event.client_payload.web_origin || '' }} | |
| TRIGGER: ${{ github.event_name }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Validate + resolve E2E targets | |
| # Allowlist the only hosts this smoke is permitted to drive. An | |
| # attacker who fires a repository_dispatch with a hostile api_url | |
| # cannot redirect the test to an attacker-controlled origin and | |
| # exfiltrate the GITHUB_TOKEN — the value is rejected here. | |
| # `set -u` would NOT catch the injection; explicit validation does. | |
| env: | |
| DEFAULT_API_URL: https://api.instanode.dev | |
| DEFAULT_WEB_ORIGIN: https://instanode.dev | |
| run: | | |
| set -euo pipefail | |
| api="${RAW_API_URL:-$DEFAULT_API_URL}" | |
| web="${RAW_WEB_ORIGIN:-$DEFAULT_WEB_ORIGIN}" | |
| case "$api" in | |
| https://api.instanode.dev|https://api.instanode.dev/) ;; | |
| *) echo "::error::E2E_API_URL '$api' not in allowlist {https://api.instanode.dev}"; exit 1 ;; | |
| esac | |
| case "$web" in | |
| https://instanode.dev|https://instanode.dev/|https://www.instanode.dev|https://www.instanode.dev/) ;; | |
| *) echo "::error::E2E_WEB_ORIGIN '$web' not in allowlist {https://instanode.dev,https://www.instanode.dev}"; exit 1 ;; | |
| esac | |
| # Strip trailing slash for stable comparison in the spec. | |
| api="${api%/}" | |
| web="${web%/}" | |
| echo "E2E_API_URL=$api" >> "$GITHUB_ENV" | |
| echo "E2E_WEB_ORIGIN=$web" >> "$GITHUB_ENV" | |
| echo "Resolved E2E_API_URL=$api E2E_WEB_ORIGIN=$web trigger=$TRIGGER" | |
| - run: npm ci | |
| # Only Chromium — this is a smoke, not a cross-browser matrix. | |
| - run: npx playwright install --with-deps chromium | |
| - name: Run auth-contract smoke | |
| run: npx playwright test --config=playwright.auth-contract.config.ts | |
| # Upload trace + screenshots on failure so the PR author can replay | |
| # the exact browser session locally (`npx playwright show-trace ...`). | |
| - name: Upload Playwright trace on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: auth-contract-trace-${{ github.run_id }} | |
| path: | | |
| test-results/ | |
| playwright-report-auth-contract/ | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| # ── Cookie-exchange ROUND-TRIP (manual / staging only) ─────────────────── | |
| # | |
| # The Layer-1 smoke above probes the CORS envelope with NO cookie. This job | |
| # runs the full round-trip spec (e2e/auth-roundtrip.spec.ts): provision → | |
| # claim → plant the bridge cookie → cross-origin exchange → Bearer | |
| # /auth/me 200. That needs a NON-prod api it can mint a bridge cookie for | |
| # (E2E_JWT_SECRET = the api's JWT_SECRET) + a provisioning backend. | |
| # | |
| # WHY workflow_dispatch-only (not on every PR): instanode-web CI cannot | |
| # build/boot the api binary (cross-repo), and we never put the prod | |
| # JWT_SECRET in this repo's CI. The AUTHORITATIVE pre-merge round-trip gate | |
| # lives in the api repo's Layer-2 docker-compose workflow (which builds the | |
| # api from PR source + has the stack-local secret). This job is the | |
| # instanode-web-side companion an operator points at a staging/compose api | |
| # on demand. The spec self-skips loudly when the secret is absent or the | |
| # backend returns 503, so a misfire reports as skipped, never a false red. | |
| auth-roundtrip: | |
| name: Cookie-exchange round-trip (staging/compose, on demand) | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.run_roundtrip == 'true' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 8 | |
| env: | |
| # Route dispatch inputs through env: + validate (workflow-injection | |
| # hygiene — same pattern as the smoke job above). The round-trip MUST | |
| # NOT target prod (planting a bridge cookie needs the stack JWT_SECRET). | |
| RAW_API_URL: ${{ github.event.inputs.api_url || '' }} | |
| RAW_WEB_ORIGIN: ${{ github.event.inputs.web_origin || '' }} | |
| # E2E_JWT_SECRET is the api's JWT_SECRET for the TARGET (staging/compose) | |
| # api — provided as a repo/environment secret by the operator who runs | |
| # this. Never the prod secret. | |
| E2E_JWT_SECRET: ${{ secrets.E2E_JWT_SECRET }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Validate + resolve round-trip targets | |
| run: | | |
| set -euo pipefail | |
| api="${RAW_API_URL:-}" | |
| web="${RAW_WEB_ORIGIN:-http://localhost:5173}" | |
| if [ -z "$api" ]; then | |
| echo "::error::run_roundtrip requires a non-empty api_url pointing at a staging/compose api (NOT prod)." | |
| exit 1 | |
| fi | |
| # Hard refuse the prod api — the round-trip would need the prod | |
| # JWT_SECRET, which must never enter this repo's CI. | |
| case "$api" in | |
| https://api.instanode.dev|https://api.instanode.dev/) | |
| echo "::error::round-trip cannot target prod api (api.instanode.dev). Point it at a staging/compose api." | |
| exit 1 ;; | |
| esac | |
| api="${api%/}" | |
| web="${web%/}" | |
| echo "E2E_API_URL=$api" >> "$GITHUB_ENV" | |
| echo "E2E_WEB_ORIGIN=$web" >> "$GITHUB_ENV" | |
| echo "Resolved round-trip E2E_API_URL=$api E2E_WEB_ORIGIN=$web" | |
| - run: npm ci | |
| - run: npx playwright install --with-deps chromium | |
| - name: Run cookie-exchange round-trip | |
| # Spec self-skips (loudly) when E2E_JWT_SECRET is empty or the | |
| # provisioning backend returns 503 — so a missing secret reports as | |
| # skipped, not a hard failure. | |
| run: npx playwright test --config=playwright.auth-roundtrip.config.ts | |
| - name: Upload round-trip trace on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: auth-roundtrip-trace-${{ github.run_id }} | |
| path: | | |
| test-results/ | |
| playwright-report-auth-roundtrip/ | |
| if-no-files-found: ignore | |
| retention-days: 14 |