feat(e2e): split CI env vars into staging/production secrets; manual prod opt-in #200

Merged
nrupard merged 1 commit from feat/pms-271-e2e-staging-prod-secrets into main 2026-06-13 16:42:46 +02:00
Owner

Summary

Splits the Playwright E2E suite's CI configuration into per-environment Forgejo Actions secrets and adds a manual production opt-in. Closes PMS-271.

Previously the suite held one flat set of E2E_* secrets, so CI could only ever target a single deployment. Forgejo has no GitHub-style environments, so the secret names themselves must encode the environment.

Changes

  • Environment-prefixed secrets. CI now expects E2E_STAGING_* / E2E_PRODUCTION_* pairs. The job env: block maps the selected environment onto the plain E2E_* names per var via ${{ inputs.environment == 'production' && secrets.E2E_PRODUCTION_<NAME> || secrets.E2E_STAGING_<NAME> }}. Because the mapping lands on the plain names, e2e/lib/env.ts, e2e/scripts/wait-for-deploy.mjs, and e2e/scripts/health-check.mjs are unchanged, and local .env still uses the plain names.
  • Manual production opt-in. workflow_dispatch gains an environment choice input (staging default / production). Automatic push / pull_request runs resolve to staging (empty inputs context); production runs only on a human dispatch that selects it, since the suite is write-heavy and shares one rate-limited account.
  • Re-gated pre-flight checks. The version-sync gate (wait-for-deploy.mjs) runs on push and on a staging dispatch; the reachability check (health-check.mjs) runs on pull_request and on a production dispatch. A production dispatch does NOT run the SHA-polling gate (the dispatched commit is unlikely to be what prod serves, so it would time out).
  • Docs. e2e/README.md and e2e/.env.example explain the prefixed-secret convention, the per-run mapping, the unchanged plain local .env, production-is-manual-dispatch-only, and a per-secret rotation-source record.

Operator follow-up (not covered by this code change)

  • Provision the E2E_STAGING_<NAME> / E2E_PRODUCTION_<NAME> secret pairs in the Forgejo Actions secret store (migrate current values into the staging set, then populate production).
  • Delete the old bare E2E_* secrets once the workflow is cut over, so a stale value cannot silently win.
  • Record the rotation source for every secret per the README's provisioning step (HUMAN TASK in the AC).

Verification

  • python3 -c "import yaml; yaml.safe_load(open('.forgejo/workflows/e2e.yml'))" parses clean.
  • Diff touches only .forgejo/workflows/e2e.yml, e2e/README.md, e2e/.env.example; the suite code and gate scripts are untouched.
## Summary Splits the Playwright E2E suite's CI configuration into per-environment Forgejo Actions secrets and adds a manual production opt-in. Closes PMS-271. Previously the suite held one flat set of `E2E_*` secrets, so CI could only ever target a single deployment. Forgejo has no GitHub-style environments, so the secret names themselves must encode the environment. ## Changes - **Environment-prefixed secrets.** CI now expects `E2E_STAGING_*` / `E2E_PRODUCTION_*` pairs. The job `env:` block maps the selected environment onto the plain `E2E_*` names per var via `${{ inputs.environment == 'production' && secrets.E2E_PRODUCTION_<NAME> || secrets.E2E_STAGING_<NAME> }}`. Because the mapping lands on the plain names, `e2e/lib/env.ts`, `e2e/scripts/wait-for-deploy.mjs`, and `e2e/scripts/health-check.mjs` are unchanged, and local `.env` still uses the plain names. - **Manual production opt-in.** `workflow_dispatch` gains an `environment` choice input (`staging` default / `production`). Automatic `push` / `pull_request` runs resolve to staging (empty `inputs` context); production runs only on a human dispatch that selects it, since the suite is write-heavy and shares one rate-limited account. - **Re-gated pre-flight checks.** The version-sync gate (`wait-for-deploy.mjs`) runs on `push` and on a staging dispatch; the reachability check (`health-check.mjs`) runs on `pull_request` and on a production dispatch. A production dispatch does NOT run the SHA-polling gate (the dispatched commit is unlikely to be what prod serves, so it would time out). - **Docs.** `e2e/README.md` and `e2e/.env.example` explain the prefixed-secret convention, the per-run mapping, the unchanged plain local `.env`, production-is-manual-dispatch-only, and a per-secret rotation-source record. ## Operator follow-up (not covered by this code change) - Provision the `E2E_STAGING_<NAME>` / `E2E_PRODUCTION_<NAME>` secret pairs in the Forgejo Actions secret store (migrate current values into the staging set, then populate production). - Delete the old bare `E2E_*` secrets once the workflow is cut over, so a stale value cannot silently win. - Record the rotation source for every secret per the README's provisioning step (HUMAN TASK in the AC). ## Verification - `python3 -c "import yaml; yaml.safe_load(open('.forgejo/workflows/e2e.yml'))"` parses clean. - Diff touches only `.forgejo/workflows/e2e.yml`, `e2e/README.md`, `e2e/.env.example`; the suite code and gate scripts are untouched.
feat(e2e): split CI env vars into staging/production secrets
Some checks failed
E2E / Playwright against staging (pull_request) Failing after 30s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt + clippy + compile + tests (pull_request) Successful in 2m37s
7788f76905
The Playwright E2E suite held one flat set of `E2E_*` Forgejo Actions secrets, so CI could only ever target a single deployment. Forgejo has no GitHub-style environments, so the secret names themselves must encode the environment.

CI now holds staging and production config side by side as environment-prefixed secrets (`E2E_STAGING_*` / `E2E_PRODUCTION_*`). The job `env:` block maps the selected environment onto the plain `E2E_*` names per var via a Forgejo expression, so `e2e/lib/env.ts` and both gate scripts stay environment-agnostic and unchanged, and local `.env` keeps using the plain names.

`workflow_dispatch` gains an `environment` choice input (`staging` default / `production`). Automatic `push` and `pull_request` runs resolve to staging (empty `inputs` context); production runs only when a human dispatches and selects it, since the suite is write-heavy and shares one rate-limited account.

Re-gate the pre-flight checks so a production dispatch does not assert a SHA prod is not serving: the version-sync gate (`wait-for-deploy.mjs`) runs on push and on a staging dispatch; the reachability check (`health-check.mjs`) runs on pull_request and on a production dispatch.

Docs (`e2e/README.md`, `e2e/.env.example`) explain the prefixed-secret convention, the per-run mapping, the unchanged plain local `.env`, production manual-dispatch-only, and a per-secret rotation-source record.

Operator follow-up (not code): provision the `E2E_STAGING_*` / `E2E_PRODUCTION_*` secret pairs and delete the old bare `E2E_*` secrets once cut over.

#PMS-271

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch feat/pms-271-e2e-staging-prod-secrets 2026-06-13 16:42:46 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!200
No description provided.