Skip to content

QA plan: Prod environment manual gate (#28)

Story: Prod environment — manual-approval gate (GitHub Environment) (#28)
Capability: Environments and deployment promotion (#26)
ADR: RAG001 — Prod gated by GitHub Environment protection rule

TypeCoverage
Pipeline behaviorProd deploy job does NOT run automatically on merge to main; it waits for a named reviewer to approve
Pipeline behaviorProd deploy job runs from the same artifact (Lambda zip, SPA bundle) produced by the QA deploy job — no rebuild
Artifact integrityPipeline step diffs the SHA256 of the Lambda zip and SPA bundle between QA and Prod jobs; they must be byte-identical
SecurityProd GitHub Environment has at least one required reviewer
SecurityProd OIDC role trust policy names repo:ArunskiOrg/rag-sample-app:environment:prod; no wildcard in sub
SecurityTerraform state backend for Prod is separate from QA; QA OIDC role cannot assume the Prod OIDC role or write to Prod state
Integration (manual)After Prod approval and apply: GET /healthz on Prod API base URL returns 200
  • Story #27 (QA auto-deploy) complete and passing
  • Prod GitHub Environment created with a required reviewer
  • Prod OIDC role exists in AWS with the prod environment-scoped trust policy
  • gh api repos/ArunskiOrg/rag-sample-app/environments/prod confirms at least one required-reviewer rule
  • Artifact hash diff step passes (QA zip SHA == Prod zip SHA)
  • OIDC trust policy parsed in CI: Prod trust policy references environment:prod with no wildcard
  • Prod state bucket policy parsed: denies s3:PutObject from the QA OIDC role ARN
  • Manual smoke: /healthz 200 in Prod after approved deploy

CI (GitHub Actions) for gate and artifact checks. Prod AWS env for deployment validation.

GitHub Actions, gh CLI, sha256sum in pipeline, Terraform, AWS CLI.

Terminal window
# Given the Prod GitHub Environment is configured
# When its protection rules are fetched
gh api repos/ArunskiOrg/rag-sample-app/environments/prod \
| python3 -c "import sys,json; e=json.load(sys.stdin); rules=e.get('protection_rules',[]); has_reviewer=any(r['type']=='required_reviewers' for r in rules); assert has_reviewer, 'Prod env missing required reviewer'"
# Then at least one required-reviewer rule is present

Artifact hash check:

Terminal window
# Given the Lambda zip was uploaded as a CI artifact by the QA job
# When the hash is compared between QA artifact and Prod artifact
diff <(sha256sum qa-artifacts/api.zip | awk '{print $1}') <(sha256sum prod-artifacts/api.zip | awk '{print $1}')
# Then diff exits 0 (files are identical)
  • An emergency hotfix may pressure the operator to bypass the manual gate. Procedure: create a hotfix branch, promote through QA fast-track (same pipeline), then approve Prod deploy immediately. Do not bypass the environment gate.
  • Prod state bucket policy must be refreshed when a new QA OIDC role ARN is created (e.g., on Terraform destroy/recreate). Add the bucket policy assertion to the Prod bootstrap runbook.

RAG001: Prod is gated by a GitHub Environment manual-approval rule; artifacts are byte-identical between QA and Prod. Security per-feature requirements (#26–#28): Prod OIDC role environment-scoped, Prod state unwritable from QA role. All exit criteria map directly.