Repository navigation
Start production deploy runs only for main CI runs - #261
Merged
Merged
Conversation
Require the deploy workflow to filter its workflow_run trigger to main, to declare the calibration-diagnostics-production concurrency group on the deploy job instead of the workflow, and to keep requiring a successful push run on main in every job that holds that group. These fail against the current workflow: the trigger has no branch filter, the group is declared at workflow level, and no job declares it. Refs #260 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deploy workflow ran after every CI run, pull requests included, and declared its concurrency group at workflow level. Each new run joined the group as soon as GitHub created it, before the deploy job's condition skipped it, and replaced the pending run. On 2026-10-21 a pull-request CI run's deploy run cancelled the pending deploy of #248. Filter the workflow_run trigger to main and declare the concurrency group on the deploy job, so a run whose job is skipped does not take the pending position. The job keeps its condition requiring a successful push run on main, which rejects a pull request from a fork whose branch is named main; that run still passes the branch filter. Fixes #260 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The check that each job in the production concurrency group requires a
successful main push compared the job's `if:` with exactly three
`&&`-joined clauses. It rejected conditions that are just as strict:
the `${{ }}` expression wrapper, parenthesised or reordered clauses, and
an extra required clause such as a repository check.
Move the comparison into a helper and test it directly. Three cases fail
with the exact comparison: the expression wrapper, reordered and grouped
clauses, and an extra required clause. The cases for a missing clause,
an `||` alternative and a negation already pass and must keep passing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check now passes when a job's `if:` is a conjunction that includes
the three required clauses, whatever the whitespace, clause order,
grouping parentheses, `${{ }}` wrapper or extra `&&`-joined clauses. It
still fails when a clause is missing, and it fails on any `||` or `!`
(other than `!=`), because either could let the job run without them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #260
What changes
.github/workflows/deploy.yml("Deploy production"):workflow_runtrigger now listsbranches: [main], so only CI runs whose head branch ismaincreate a deploy run. Pull-request CI runs from branches in this repository no longer create one.calibration-diagnostics-productionconcurrency group (stillcancel-in-progress: false) moves from the workflow to thedeployjob. A workflow-level group admits a run as soon as GitHub creates it, before the job'sif:is evaluated, so a run that would skip could replace a pending deploy ofmain. That is how Refuse a country with no configured release instead of serving the US one #248's pending deploy (run 39680251409) was cancelled on 2026-10-21; Pull-request CI runs can cancel a pending production deploy of main #260 lists the runs involved.if:stays the same: a successful CI run, triggered bypush, onmain.A newer
maindeploy still replaces an older pendingmaindeploy. That is intended, because the newer commit contains the older one.frontend/HOSTED_RUNTIME.mddescribes the trigger filter and where the concurrency group is declared.Pull requests from a fork whose branch is named
mainGitHub matches the
workflow_runbranchesfilter against the triggering run'shead_branch. For a pull request from a fork, that is the fork's own branch name; fork pull-request runs inPolicyEngine/policyengine-usshow this. A fork pull request from a branch namedmaintherefore still passes the filter and creates a deploy run. Itsworkflow_run.eventispull_request, so the job'sif:skips it, and the job never deploys. Because the concurrency group now belongs to the job, the skipped job should not enter the group or replace a pending deploy. GitHub's documentation does not say outright whether a job skipped byif:takes part in job-level concurrency. See "Not verified" below. This repository has had no fork pull-request runs in its last 100 pull-request runs.The same applies to a CI run on
mainthat fails or is cancelled, for example when a newer push cancels it under CI'scancel-in-progress: true. It still creates a deploy run, but the job skips and no longer occupies the group.Tests
The workflow tests are in
frontend/tests/test_deploy_workflow.py.test_deployment_waits_for_successful_main_ci: the trigger has nobranches: [main].test_production_concurrency_group_belongs_to_the_deploy_job_not_the_workflow: the workflow declaresconcurrency, and thedeployjob does not.test_every_job_in_the_production_group_requires_a_successful_main_push: no job declares the group, so no job can be checked for the three requiredif:clauses (success,push,main, all joined by&&).test_deploy_workflow.py: 20 passed. Withtest_telemetry_service_workflow.pyandtest_deploy_script.py: 35 passed.actionlint1.7.12 ondeploy.yml: no findings. CI does not runactionlint; this was a local check.workflow_runworkflows run from the default branch's copy of the file, so this pull request's own CI does not exercise the new trigger. The first real run happens when this pull request's merge commit passes CI onmain. Merging this pull request deploys production.Environment variables and secrets
Moving the concurrency group does not change
environment: Production,permissionsor any step'senv:on thedeployjob, and the workflow-levelenv:block is unchanged. GitHub resolves environment secrets and variables for a job from itsenvironment:key, which is stillProduction. The Production environment has no protection rules or deployment branch policy. Providers below are fromgh secret list/gh variable list(repository, Production environment, and the organization secrets shared with this repository). The last production deploy, run 39684343767 (2026-10-21), passedRequire deployment credentials,google-github-actions/auth@v2andCheck collector read secret existswith this same wiring.MODAL_ENVIRONMENT,VERCEL_CLI_VERSION,VERCEL_ORG_ID,VERCEL_PROJECT_IDenv:(all steps);deploy-dashboard.shdeploy.ymlenv:is unchangedHF_RELEASE_READ_TOKENRequire deployment credentials;Build unaliased Vercel production candidate(passed to Vercel asHF_TOKEN)env:is unchangedMODAL_TOKEN_ID,MODAL_TOKEN_SECRETRequire deployment credentials;Deploy Modal backend;Resolve Modal backend URLenv:is unchangedMICROCOSM_MODAL_KEY,MICROCOSM_MODAL_SECRETVerify Modal backend metadata; Vercel build (--env)environment: Productionis unchangedVERCEL_TOKENPromote verified Vercel deploymentVERCEL_AUTOMATION_BYPASS_SECRETMICROCOSM_TELEMETRY_COLLECTOR_URL--env)TELEMETRY_GCP_WIF_PROVIDER,TELEMETRY_GCP_DEPLOY_SERVICE_ACCOUNTgoogle-github-actions/auth@v2inputsid-token: writeTELEMETRY_GCP_PROJECT_ID→PROJECT_ID,TELEMETRY_READ_SECRET_NAME→READ_SECRET_NAMECheck collector read secret existsSOURCE_COMMIT,CALIBRATION_MODAL_APP_NAME,MICROCOSM_CALCULATION_URL,MICROCOSM_BACKEND_SOURCE_COMMITgithub.event.workflow_run.head_shaand earlier step outputs in the same jobworkflow_runpayload is unchanged by the branch filterGITHUB_OUTPUT, OIDC token request variablesdeploy-dashboard.sh;google-github-actions/auth@v2id-token: write)Modal runtime:
backend/modal_app.pyattaches no Modal secrets. It setsCALIBRATION_SOURCE_COMMIT,CALIBRATION_MODAL_APP_NAMEand the source-tree hash in the image from the checked-out commit, so this change does not affect them. Vercel runtime: the frontend's server variables come from the--envarguments indeploy-dashboard.sh deploy-frontend, listed above. This change does not modify Vercel project settings.Not verified
if:stays out of its job-level concurrency group. GitHub's workflow syntax reference describes job-level concurrency and the pending-run replacement, but it does not say whether a skipped job enters the group. This matters only for runs that pass the branch filter but skip: a fork pull request from a branch namedmain, and a failed or cancelledmainCI run. The branch filter alone removes every same-repository pull-request run, which caused the 2026-10-21 cancellation. It was not tested, because testing would mean dispatching or merging. If needed, the job'sif:could also requiregithub.event.workflow_run.head_repository.full_name == github.repository, but that would not change whether the skipped run enters the group.Other workflows
No other workflow in this repository uses
workflow_run.publish-calibration-tree.ymlhas a workflow-level concurrency group and a job-levelif:onvars.CALIBRATION_TREE_BUILD_ENABLED, but it runs only onworkflow_dispatch. Its comments describe the replacement of pending dispatches as intended.telemetry-service.ymluses separate groups for pull requests and dispatches.ci.ymlcancels in-progress runs for the same ref. None has this pattern.Review (2026-10-22)
The change is correct.
branches: [main]filter matches the triggering CI run's head branch. Another repository with the identical trigger (openops-cloud/openops) created deploy runs only for its 29mainCI runs, out of 178.environment: Production, permissions, the timeout and everyenv:block are unchanged.Not stated by GitHub's documentation: whether a job skipped by its
if:stays out of a job-level group. This matters only for failedmainCI runs and for a fork pull request from a branch namedmain, which the workflow-level group already admits today, so this is never worse than before.Fixed, failing test first: the
if:test was too strict. It rejected equally strict rewordings, such as the${{ }}wrapper, reordered conditions, or an extra required condition.0066f9e9792ede&&-joined condition containing success, push andmainis accepted; a missing condition, `CI passes on
9792ede.Merge order: merge this first, before #253, #255 and #259, while no deploy is running or waiting. Its own deploy is the first one protected. After it, check that the next pull-request CI completion creates no deploy run.
🤖 Generated with Claude Code