Skip to content

chore(deps): bump actions/checkout from 6 to 7 in the actions group #47

chore(deps): bump actions/checkout from 6 to 7 in the actions group

chore(deps): bump actions/checkout from 6 to 7 in the actions group #47

Workflow file for this run

# Real-backend UI SMOKE on the PR path (Wave 2 — close the "UI PRs merge having
# touched the real api only via a cookieless CORS preflight" gap).
#
# Design ref: docs/ci/01-CI-INTEGRATION-DESIGN.md (Wave 2): "A real-backend UI
# smoke must gate the PR (or post-merge-pre-deploy), not run only on a 30-min
# schedule." This runs a SMALL, tagged subset (@pr-smoke) of the Wave 3
# real-backend UI journeys against PROD via a minted, cohort-scoped, reaped
# account — so EVERY web PR exercises the actual dashboard against the real api
# and catches the login-broke class (a backend field/enum/status rename that
# compiles + passes mocked Playwright but breaks the real /app) BEFORE merge.
#
# The @pr-smoke subset (3 journeys, ~40s):
# #1 auth round-trip — /app renders authed (not /login); /auth/me data loads.
# #2 provision render — a seeded resource lists + its detail/metrics render.
# #3 deploy row — a created deploy's row + detail logs + make-permanent.
# The FULL journey suite stays on the 30-min schedule (e2e-prod.yml).
#
# WHY this is safe to run against prod (same invariants as e2e-prod.yml):
# - The mint endpoint creates an is_test_cohort=true account; the live worker
# skip-guards neuter billing/churn/email/quota for it (no charge, no quota
# burn, no "we miss you" email, no churn of a real customer).
# - The account + every resource it creates is reaped: this job DELETEs the
# minted account AND runs the per-run ledger reaper (npm run reap:live) in an
# `if: always()` teardown; the reaper exits non-zero on any leak (rule 24).
# - cohort.ts assertSafeApiTarget() only allows a prod target for a sanctioned
# minted run; a stray invocation can never hammer prod.
#
# FORK / SECRET-LESS SAFETY (why this never hard-fails a PR that can't reach
# secrets): GitHub does NOT expose repo secrets to pull_request runs from forks.
# The gate step below no-ops the job cleanly (::notice::) when E2E_ACCOUNT_TOKEN
# is empty, so a fork PR (or a PR opened before the secret exists) goes GREEN
# without running — it is "required only when secrets are available". For
# same-repo branch PRs (where secrets ARE present) it runs for real and gates the
# merge. (Same posture as e2e-prod.yml's gate.)
name: E2E PR smoke (real-backend UI, minted account)
on:
pull_request:
# Only when the dashboard surface or the live-UI harness changes — a docs-only
# or marketing-copy PR doesn't need to spend a prod mint+reap cycle. (The
# full schedule still covers everything every 30 min.)
paths:
- 'src/**'
- 'e2e/**'
- 'playwright.live.config.ts'
- 'vite.config.ts'
- 'package.json'
- '.github/workflows/e2e-pr-smoke.yml'
concurrency:
# One PR-smoke run per branch; cancel superseded runs (a new push obsoletes the
# old). Distinct group from e2e-prod so a scheduled prod run isn't cancelled.
group: e2e-pr-smoke-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
pr-smoke:
name: UI real-backend smoke (@pr-smoke) via minted account + reap
runs-on: ubuntu-latest
timeout-minutes: 15
env:
E2E_API_URL: https://api.instanode.dev
E2E_LIVE_RUN_ID: ${{ github.run_id }}
E2E_ACCOUNT_TOKEN: ${{ secrets.E2E_ACCOUNT_TOKEN }}
steps:
- name: Gate on configured mint token (no-op cleanly on fork / secret-less PR)
run: |
set -euo pipefail
if [ -z "${E2E_ACCOUNT_TOKEN:-}" ]; then
echo "::notice::secrets.E2E_ACCOUNT_TOKEN not available (fork PR or unconfigured) — skipping UI PR smoke (no-op, GREEN)."
echo "RUN=0" >> "$GITHUB_ENV"
else
echo "RUN=1" >> "$GITHUB_ENV"
fi
- uses: actions/checkout@v7
if: env.RUN == '1'
- uses: actions/setup-node@v6
if: env.RUN == '1'
with:
node-version: '22'
cache: 'npm'
- name: Install deps
if: env.RUN == '1'
run: npm ci
- name: Install Chromium
if: env.RUN == '1'
run: npx playwright install --with-deps chromium
- name: Mint ephemeral cohort account (pro — deploy headroom for #3)
id: mint
if: env.RUN == '1'
run: |
set -euo pipefail
resp="$(curl -sS -w '\n%{http_code}' \
-X POST "${E2E_API_URL}/internal/e2e/account" \
-H "X-E2E-Token: ${E2E_ACCOUNT_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"tier":"pro","with_resources":true}')"
code="$(printf '%s' "$resp" | tail -n1)"
body="$(printf '%s' "$resp" | sed '$d')"
if [ "$code" != "200" ]; then
echo "::error::mint endpoint returned HTTP $code (expected 200). Body: $body"
exit 1
fi
jwt="$(printf '%s' "$body" | jq -r '.session_jwt // empty')"
team="$(printf '%s' "$body" | jq -r '.team_id // empty')"
email="$(printf '%s' "$body" | jq -r '.email // empty')"
tier="$(printf '%s' "$body" | jq -r '.tier // empty')"
if [ -z "$jwt" ] || [ -z "$team" ]; then
echo "::error::mint response missing session_jwt or team_id. Body: $body"
exit 1
fi
echo "::add-mask::$jwt"
echo "::add-mask::$team"
{
echo "MINTED_SESSION_JWT=$jwt"
echo "MINTED_TEAM_ID=$team"
echo "MINTED_EMAIL=$email"
echo "MINTED_TIER=$tier"
} >> "$GITHUB_ENV"
echo "minted=1" >> "$GITHUB_OUTPUT"
echo "Minted cohort account (tier=$tier) for the PR UI smoke — session + team_id masked."
- name: Run @pr-smoke UI journeys against prod
if: env.RUN == '1' && steps.mint.outputs.minted == '1'
env:
E2E_LIVE: '1'
# The minted PRO account drives the authed UI legs (cohort.ts
# mintedSession / factory mintUser). The factory mints per-test, but
# exporting the workflow-minted session here makes assertSafeApiTarget
# treat the prod target as sanctioned even for the anon login leg.
E2E_SESSION_JWT: ${{ env.MINTED_SESSION_JWT }}
E2E_TEAM_ID: ${{ env.MINTED_TEAM_ID }}
E2E_ACCOUNT_EMAIL: ${{ env.MINTED_EMAIL }}
E2E_ACCOUNT_TIER: ${{ env.MINTED_TIER }}
# Fingerprint bypass for any anon provision the smoke touches.
E2E_TEST_TOKEN: ${{ secrets.E2E_TEST_TOKEN }}
run: npm run test:e2e:live:pr-smoke
- name: Reap the workflow-minted account (teardown)
if: always() && env.RUN == '1' && env.MINTED_TEAM_ID != ''
run: |
set -euo pipefail
code="$(curl -sS -o /dev/null -w '%{http_code}' \
-X DELETE "${E2E_API_URL}/internal/e2e/account/${MINTED_TEAM_ID}" \
-H "X-E2E-Token: ${E2E_ACCOUNT_TOKEN}")"
case "$code" in
200|202|204|404|410) echo "Reaped workflow-minted account (HTTP $code)." ;;
*) echo "::error::DELETE minted account returned HTTP $code — possible leak."; exit 1 ;;
esac
- name: Reap per-test cohort resources from ledger (teardown)
# The factory mints a fresh account PER test and reaps it inline + via the
# ledger; this sweep is the no-leak backstop. Exits non-zero on any leak,
# failing the job loudly (rule 24).
if: always() && env.RUN == '1'
run: npm run reap:live
- name: Upload trace on failure
if: failure() && env.RUN == '1'
uses: actions/upload-artifact@v7
with:
name: e2e-pr-smoke-trace-${{ github.run_id }}
path: |
test-results/
playwright-report-live/
e2e/.cleanup-ledger.json
if-no-files-found: ignore
retention-days: 7
# Wave 5 — record the PR-smoke outcome in NR (suite=pr-smoke). Gated on
# RUN == '1' so a fork/secret-less no-op run does NOT report a misleading
# pass (the suite did not actually execute). When it DID run, if: always()
# captures both the journey failure and a leak-reaper failure. No-ops
# without the NR secret. The pr-smoke-failing-main NR alert reads this.
- name: Emit PR-smoke result to New Relic
if: always() && env.RUN == '1'
uses: ./.github/actions/nr-ci-event
with:
license-key: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
account-id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
result: ${{ job.status == 'success' && 'pass' || 'fail' }}
suite: pr-smoke
pr-number: ${{ github.event.pull_request.number }}
failed-step: ${{ job.status != 'success' && 'real-backend UI @pr-smoke journeys / mint / reap' || '' }}
repo: ${{ github.repository }}
workflow: ${{ github.workflow }}
branch: ${{ github.ref_name }}
commit-sha: ${{ github.sha }}
log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
event-name: ${{ github.event_name }}
actor: ${{ github.actor }}