Repository navigation
Promote each staging deployment's tested Simulation Entrypoint revision - #894
Conversation
The main deployment's beta job and the Stage 12 comparison path's staging deployment now pass promote_entrypoint: true, as production deployments do. Each staging deployment therefore moves the staging service's stable URL to the revision it tested, runs the same stable-URL checks as production, restores the previous revision if they fail, and then deletes the staging service's older revisions except the promoted one and the one it replaced. The new rule sim.deploy.staging-promotes-tested-revision requires it: a test reads every workflow, finds each job that runs the reusable deployment for staging, and fails unless it passes promote_entrypoint: true. Its proof fixtures are the beta job before and after this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…build The beta environment does not restrict which branch a run may come from, so a manual run of either deployment workflow from a branch other than main now moves the staging stable URL to that branch's build until the next deployment from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The prod deployment needs the whole beta deployment, which now includes the revision clean-up that runs after a staging promotion, so a failed staging clean-up also stops the production deployment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d3dd12e to
e9e8de9
Compare
…e staging clean-up The repository README already says the prod deployment starts only after beta's revision clean-up succeeds; the entrypoint's DEPLOYMENT.md listed the beta jobs prod waits for without the clean-up job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review at Verdict: right direction, and the rollback path holds. But it lets any branch replace the staging stable URL, which I'd close before merging.
Checked and fine: What I ran: fresh clone, 🤖 Generated with Claude Code |
Merges v2plan/1j-139-staging-promotion (#894) at 4f34bf0. The staging economy check runs after the staging deployment promotes the revision it tested, which #894 adds. Main has since split tests/test_deployment_assets.py, so #894's test changes move into main's structure: its path constants go into test_deployment_assets_support.py, and its staging promotion check and rule proof stay in test_deployment_assets.py, which the rule registry names, using the support module's workflow_jobs. The changelog keeps both sides' entries, and the generated rules document and AGENTS.md are rendered again from the merged registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main split the deployment asset tests into smaller files and replaced changelog_entry.yaml with changelog fragments. The staging promotion tests and their paths move to the promotion test file and its support module, the registry points at their new node IDs, the changelog entry becomes changelog.d/893.changed.md, and the rules documents are re-rendered from the merged registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The staging promotion rule now requires each staging deployment to pass
promote_entrypoint: ${{ github.ref == 'refs/heads/main' }}. A literal
true counts as a violation, because a manual run from another branch
would then move the staging stable URL to an unmerged build, and the
clean-up would delete the older revisions that rollback needs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The beta deployment and the Stage 12 comparison path's staging
deployment pass promote_entrypoint: ${{ github.ref == 'refs/heads/main' }}.
A manual run from another branch still deploys and tests its candidate
revision, but leaves the staging stable URL and the older staging
revisions unchanged. The rule statement, deployment guide, README and
changelog fragment say so, and the rules documents are re-rendered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A manual run from another branch still deploys its Modal gateway and executor to the staging Modal environment and publishes its routing, as every staging deployment does; only the staging service's Cloud Run traffic and its older revisions stay as they were. The deployment guide, rule statement and changelog fragment now say so, say that the check stops an accidental branch run rather than a branch that edits its workflow, and note that the prod environment refuses a branch run. The manual-run paragraph moves after the clean-up, so the stable-URL check again directly follows the traffic change it verifies. The tests share one constant for the required expression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deployment guide named only the gateway and executor and said they last until the next deployment from main. A branch run of the main deployment workflow also deploys the Stage 12 workers, the Stage 12 workflow deploys only those, and routes and versions that main doesn't deploy can stay after main's next deployment. It also says that a branch restriction on the beta environment would refuse every branch run's candidate deployment and tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. Point 1 is fixed in 62ca535: both staging jobs now pass |
Fixes #893
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Merge order: the staging stable traffic has been moved
This PR had to wait until the staging service's stable traffic was moved off Cloud Run's sample container (revision
policyengine-simulation-entry-staging-00001-cuh). The promotion's stable-URL check reads the revision header from/health, and the sample container returns an HTML page with no such header. A check that still reached it would fail, restore the sample container and fail thebetadeployment, which stops the production deployment.Done on 2026-10-30: stable traffic now goes to
policyengine-simulation-entry-staging-00099-vin, built frommainat4b09f76d, whosebetadeployment and authenticated tests passed. The stable-URL smoke check passed against it.The first promotion deletes about 97 old staging revisions. After a successful promotion, the existing clean-up job (
cleanup_entrypoint_revisions) removes tags from older revisions and deletes every staging revision except the promoted one and the one it replaced. The staging service has 99 revisions today, so about 97 are deleted, the sample container among them. This is expected and accepted.Summary
Staging promotes each tested build from
main. Thebetajob insimulation-deploy.ymland the staging job of the Stage 12 comparison path (deploy-stage12-staginginstage12-deploy.yml) now passpromote_entrypoint: ${{ github.ref == 'refs/heads/main' }}. A deployment frommain, including the release commit's, therefore runs the samepromote_entrypointjob as production:It then runs the same revision clean-up. The staging stable URL then serves the revision that passed the staging tests.
A manual run from another branch doesn't promote. Both workflows accept
workflow_dispatchfrom any branch, and thebetaenvironment has no deployment-branch policy. Such a run deploys and tests its candidate revision, but leaves the staging service's Cloud Run traffic where it was and deletes no revisions, so the last revision deployed frommainstays available tosimulation-rollback.yml.mainreplaces the apps it deploys, but routes and versions it doesn't deploy can stay.betawould enforce it, and that would also stop branch runs from deploying to staging at all.New rule
sim.deploy.staging-promotes-tested-revision(enforced, mechanical):test_deployment_assets_promotion.pyreads every workflow, finds each job that calls the reusable deployment workflow withdeployment_environment: staging, and fails unless it passes exactlypromote_entrypoint: ${{ github.ref == 'refs/heads/main' }}. Bothfalseand a literaltrueare violations.rules/fixtures/sim.deploy.staging-promotes-tested-revision/: the bad fixture is thebetajob as it was before this change. The proof test also rejects a literaltrue, and evaluates the required expression: true forrefs/heads/main, false for another branch and for a tag.docs/generated/rules.mdand the Rules block inAGENTS.mdare re-rendered.rules/, so it requests review fromcore-developers.Documentation:
README.mdand the entrypoint'sREADME.mdandDEPLOYMENT.mdnow describe staging promotion, the revision clean-up and what a branch run does.beta's value ofSIMULATION_ENTRYPOINT_ENFORCE_PERMISSIONSnow applies to every caller of the staging stable URL.prodnow also waits forbeta's revision clean-up, so a failed staging clean-up stops the production deployment. A branch run skips both, and theprodenvironment, which accepts onlymain, refuses it.Changelog fragment
changelog.d/893.changed.md.mainis merged into this branch rather than rebased onto it, because other open branches are built on these commits.Environment variables and secrets
The change only sets an input. The behavior that changes is that the reusable workflow's
promote_entrypointandcleanup_entrypoint_revisionsjobs now also run forbeta, on deployments frommain. Their promotion result now also feedsfinalize_routinganddeployment_ready, as it already does forprod. Every value below reaches the job through the caller'ssecrets: inheritand the job'senvironment: ${{ inputs.release_environment }}, which isbetahere. Values were checked by name through the GitHub API on 2026-10-30, after mergingmain; no secret value was read. Mergingmainchanged neither job's variables.SIMULATION_ENTRYPOINT_GCP_PROJECT_IDpromote_entrypoint(jobenv:PROJECT_ID),cleanup_entrypoint_revisions(jobenv:)betaenvironment variablevars.in jobenv:policyengine-simulation-entrySIMULATION_ENTRYPOINT_GCP_REGIONbetaenvironment variable (falls back tous-central1)vars.in jobenv:us-central1SIMULATION_ENTRYPOINT_GCP_WIF_PROVIDER,SIMULATION_ENTRYPOINT_GCP_DEPLOY_SERVICE_ACCOUNTgoogle-github-actions/authstep in both jobsbetaenvironment secretssecrets: inherit, then stepwith:sim-entry-gh-deployeris the only service account in the project that this repository's GitHub workload identity pool may impersonate (roles/iam.workloadIdentityUserforattribute.repository/PolicyEngine/policyengine-sim-api), and it holdsroles/run.adminat project level, which covers traffic changes and revision deletion.beta'sdeploy_entrypointjob already authenticates with these secrets.SIMULATION_ENTRYPOINT_AUTH_ISSUER,SIMULATION_ENTRYPOINT_AUTH_AUDIENCE,SIMULATION_ENTRYPOINT_TEST_AUTH_CLIENT_ID,SIMULATION_ENTRYPOINT_TEST_AUTH_CLIENT_SECRETpromote_entrypointbetaenvironment secretssecrets: inherit, then stepenv:beta'sauthenticated_testjob against the candidate URLSIMULATION_ENTRYPOINT_ENFORCE_PERMISSIONSbetaenvironment variablevars.in stepenv:beta(same as today for the candidate), so the tests run in log-only modeSIMULATION_ENTRYPOINT_CUSTOM_URLpromote_entrypoint(jobenv:CUSTOM_URL)betaenvironment variablevars.in jobenv:beta, so the custom-hostname steps are skipped (production sets it)MODAL_TOKEN_ID,MODAL_TOKEN_SECRETfinalize_routing(already ran forbeta; it now also counts the promotion result)secrets: inherit, then stepenv:release-sentinel-betabeta'ssentinel_expectedjobactions/download-artifactbeta; unchangedNo new variable or secret is introduced, and the deploy image, Cloud Run runtime configuration and Modal secrets are unchanged. Promotion only moves traffic to a revision that was already deployed with its own runtime environment.
Testing
After merging
main(4b09f76d) and restricting promotion tomain, all local:mainsplittest_deployment_assets.pyinto smaller files, so the two new tests now sit intest_deployment_assets_promotion.pyand the registry names them there.true,test_staging_deployments_promote_their_tested_entrypoint_revisionandtest_deployment_uses_gcloud_workflow_without_terraformfailed as expected. After the workflow change, they andtest_rule_proof_staging_promotes_tested_revisionpass, each run by node ID.ruff@0.15.22 check .andruff@0.15.22 format --check(CI's commands): clean.python scripts/run_quality_guards.py --base-ref origin/main: every guard passed, includingrules-registry,ci-scriptsandfile-length.python -m scripts.rules guard --base-ref origin/main: passed..github/scripts/check-changelog-fragment.shagainstmain: passed.Earlier, on
8d259a5a: the same tests failed first and then passed; the entrypoint project's tests, the rules tool's tests, ruff and the quality guards passed; documentation-only commits were followed by the quality guards andtest_deployment_assets.py.Not run locally: the deployment itself. The promotion and clean-up behavior is first exercised by the post-merge
betadeployment.Review
${{ github.ref == 'refs/heads/main' }}, and the rule checks for that expression. The documentation says what a branch run still does.latestpointer and the Stage 12 manifest's default version move only to a newer version, so after a branch run with a newer policyengine.py version, unversioned staging requests keep reaching that branch's apps oncemaindeploys again; that predates this PR.4f34bf0f.After merge (operator checks)
betadeployment: "Promote and verify Cloud Run entrypoint" and "Remove superseded Cloud Run revisions" succeed.curl -sSi <staging stable URL>/healthshowsx-policyengine-simulation-revision: policyengine-simulation-entry-staging-<number>-<suffix>.🤖 Generated with Claude Code