Repository navigation
ci(release): Create Release tags and builds the commit its run was created for (#1634) - #1656
Conversation
|
Verdict: GOOD TO GO at
It also stops uploads replacing published assets (#1640's replacement half). I re-ran both test files myself: 55 and 67 OK. Your decision is needed before merge (the PR body's last paragraph). Under the REST docs' reading, a run approved after |
|
Coverage: 89.68% (unit tier, Python 3.14,
Per-file detail: the |
|
Resolve conflicts |
|
Recorded: resolving the conflicts. With #1645 merged, three files conflict with The App-token round for #1634 is rewriting those same files right now, so |
…eated for (#1634) Run 38008819343 was created for b6b95bc and, approved three days later, tagged main's tip 2c0008c as v1.5.0b9 and conda-1.5.0b9+0: the GitHub Release defaulted target_commitish to main, and the tag job ran `git pull --rebase origin main` before tagging. - Every checkout pins `ref: ${{ github.sha }}`. github's release names it as target_commitish only on workflow_run, where it creates the tag; a v* push names none. - github creates the release with a GitHub App installation token, minted in the job by actions/create-github-app-token@v3 with Contents and Workflows write from the github-release environment secret RELEASE_APP_PRIVATE_KEY, since GitHub refuses GITHUB_TOKEN a target that changes a workflow file relative to main. With vars.RELEASE_APP_CLIENT_ID unset it spends GITHUB_TOKEN as before. version_check skips a push by vars.RELEASE_APP_BOT_ID, the run that App's tag starts, and release_status reports that skip with a notice. - pypi and conda attach files with `gh release upload`, not action-gh-release, which calls Update a release, re-checking that target, before every upload. An asset already in state "uploaded" is left alone rather than deleted and re-uploaded; one in any other state is replaced. A missing release or a pattern matching no file now fails the step. - tag cuts conda-<version>+0 at the run's commit before reading main; the conda/build reset is its own commit fast-forwarded onto main's tip, skipped where origin already has a conda-<version>+N tag other than +0. - github, tag, pypi and conda refuse to publish under a tag naming another commit; that check blocks KTLO legs too. github explains a 404 or 403 by the token it spent. - cron-conda's conda-dist updates only a published release, naming the default branch as its target, so it never creates v<version> or has a stale target re-checked. - releasing.rst gains "The Release App" with its one-time setup; releasing.rst and workflows.rst re-cited. New test: tests/project/test_release_tags_the_run_sha_1634.py (66 tests, 58 failing on origin/main's workflows). test_release_gates.py gains the App's rows; test_python_matrix_tiers_1633.py exempts the tag check. This fixes the replacement half of #1640; the convergence half (completeness waiting on legs that can fail) is #1645's. Closes #1634 Refs #1640
4d77d6d to
2fd8f1f
Compare
|
Verdict: GOOD TO GO at Round 7 mints the release App's token in What the reviewer verified:
Unmeasured until a real release: whether the tag the App creates fires Optional hardening, not blocking: no test catches |
Please follow the guide below
You will be asked some questions, please read them carefully and answer honestly
Put an
xinto all the boxes [ ] relevant to your pull request (like that [x])Use Preview tab to see how your pull request will actually look like
Searched for similar pull requests
Followed the coding style (
make pylint,make mypy,make isort)make testpasses, and a test case covers the change — a new test covers it; the whole suite is not run here, only the files that parse the workflowsAdded a changelog entry under
docs/source/changelog/and regeneratedCHANGELOG.md, if the change is user-visible — N/A — centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657What is the purpose of your pull request?
Tick the commit type your subject line carries.
fix— corrects a defectfeat— adds a featureperf— changes performance, not behaviourrefactor— changes neither behaviour nor performancetest— tests onlydocs— documentation onlyci— workflows or build toolingrelease— bumps the version or rolls up a distributionchore— anything elseDescription of your pull request and other information
Measured. Run 38008819343 has
github.shab6b95bc8e; its trigger, Vendor Update run 38008512299, ran on the bump's parent945c3c3d8, soworkflow_run.head_shais the wrong pin. The release'starget_commitishismainandv1.5.0b9→2c0008c3c: job 115087308551 checked outb6b95bc8e, then rangit pull --rebase origin mainand tagged2c0008c3c.Fix (
.github/workflows/create-release.yml). Every checkout pinsref: ${{ github.sha }}(:219,440,639,826,1026).github's release names it astarget_commitishonly onworkflow_run, where it creates the tag (:543); av*push names none.pypiandcondaattach files withgh release upload(:918,1249), notaction-gh-releasev3.0.2, which calls Update a release before every upload (src/github.ts:631-683); the REST docs re-check "the existing target" there, and Upload a release asset states no such rule. An asset in stateuploadedis left alone (#1640); any other is deleted and re-uploaded (--clobber), since a 502 "may leave an empty asset with a state of starter".tagtagsconda-<v>+0at the run's commit before readingmain(:656).github,tag,pypiandcondaexit with::errorif a tag they publish under names another commit (:552,656,829,1029).Two new failures, deliberate (also in
releasing.rst, "One Commit per Release"). No release for the tag fails the upload withgh's "release not found"; the action made one, withtarget_commitishunset, which Create a release defaults to the default branch (src/github.ts:617-628,1026). A pattern matching no file exits 1 with::error title=Nothing to upload; the action only warned (src/run.ts:15-20).cron-conda.yml.conda-dist'sncipollo/release-action@v1.21.0(allowUpdates: true) calls Update a release, so it now passescommit: ${{ github.ref_name }}, the default branch on both its triggers, which the update sends astarget_commitish(src/Action.ts:107-120,src/Releases.ts:150-174). A step before it runsgh release view, so the action runs only on a published release: with none it would create one and the tag atcommit(src/Action.ts:72-92), and it would publish a draft (src/Inputs.ts:210-213). The Anaconda upload is ungated.Reset (
:705). Its own commit on themaintip just fetched, pushed without force, settingconda/buildto 0. None if the run's commit is not onmain, the tip already says 0 or has another__version__, or origin has aconda-<v>+Ntag other than+0. A rejected push re-decides from a fresh fetch, up to 3 times;tagnever pulls, merges, rebases, resets or force-pushes.New
tests/project/test_release_tags_the_run_sha_1634.py: 66 tests; 28 runtag's scripts, the tag check, the upload script, the release check (against a stand-ingh) and the refusal explanation. With origin/main's workflows, 58 fail.test_release_gates.pygains agh release uploadpublishing marker, the App'sversion_checkrows andrelease_statuscases;releasing.rstandworkflows.rstre-cited.This fixes the replacement half of #1640; the convergence half (completeness waiting on legs that can fail) is #1645's. Rebased onto #1645:
pypi's upload keeps this run step plus #1645'sid: uploadandcontinue-on-error. The tag check stays blocking on KTLO legs, since it fails alike on every leg andcontinue-on-errorwould let one publish under a tag naming another commit, sotest_python_matrix_tiers_1633.pyexempts it andtest_the_check_blocks_every_legpins it.Owner chose the App-token design (#1634).
githubmints a GitHub App installation token withactions/create-github-app-token@v3(permission-contents: write,permission-workflows: write;:503-511) and spends it only as Create Release'stoken:(:547): Create a release refusesGITHUB_TOKENa target that changes a workflow file relative to the default branch, and accepts an App token with "Workflows" write. Withvars.RELEASE_APP_CLIENT_IDunset the step skips andGITHUB_TOKENis spent as before.version_checkskips apushwhosegithub.actor_idisvars.RELEASE_APP_BOT_ID(:210-212), andrelease_statusreports it as::notice title=Nothing to release(:1356-1358). Whether that tag push fires, with the bot as actor, is unmeasured; if not, the guard never matches. One-time setup, done on 2026-10-13: the Apppypcapkit-release(Contents and Workflows read and write, this repository only), repository variablesRELEASE_APP_CLIENT_IDandRELEASE_APP_BOT_ID(342101269), andRELEASE_APP_PRIVATE_KEYas agithub-releaseenvironment secret, which no job reads before approval. Only the first approved run can confirm the installation.Closes #1634
Refs #1640