Skip to content

ci(release): Create Release tags and builds the commit its run was created for (#1634) - #1656

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1634-release-tags-the-run-sha
Oct 13, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1634-release-tags-the-run-sha

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

  • You will be asked some questions, please read them carefully and answer honestly

  • Put an x into 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 test passes, 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 workflows

  • Added a changelog entry under docs/source/changelog/ and regenerated CHANGELOG.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) #657

What is the purpose of your pull request?

Tick the commit type your subject line carries.

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else

Description of your pull request and other information

Measured. Run 38008819343 has github.sha b6b95bc8e; its trigger, Vendor Update run 38008512299, ran on the bump's parent 945c3c3d8, so workflow_run.head_sha is the wrong pin. The release's target_commitish is main and v1.5.0b9 → 2c0008c3c: job 115087308551 checked out b6b95bc8e, then ran git pull --rebase origin main and tagged 2c0008c3c.

Fix (.github/workflows/create-release.yml). Every checkout pins ref: ${{ github.sha }} (:219,440,639,826,1026). github's release names it as target_commitish only on workflow_run, where it creates the tag (:543); a v* push names none. pypi and conda attach files with gh release upload (:918,1249), not action-gh-release v3.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 state uploaded is left alone (#1640); any other is deleted and re-uploaded (--clobber), since a 502 "may leave an empty asset with a state of starter". tag tags conda-<v>+0 at the run's commit before reading main (:656). github, tag, pypi and conda exit with ::error if 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 with gh's "release not found"; the action made one, with target_commitish unset, 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's ncipollo/release-action@v1.21.0 (allowUpdates: true) calls Update a release, so it now passes commit: ${{ github.ref_name }}, the default branch on both its triggers, which the update sends as target_commitish (src/Action.ts:107-120, src/Releases.ts:150-174). A step before it runs gh release view, so the action runs only on a published release: with none it would create one and the tag at commit (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 the main tip just fetched, pushed without force, setting conda/build to 0. None if the run's commit is not on main, the tip already says 0 or has another __version__, or origin has a conda-<v>+N tag other than +0. A rejected push re-decides from a fresh fetch, up to 3 times; tag never pulls, merges, rebases, resets or force-pushes.

New tests/project/test_release_tags_the_run_sha_1634.py: 66 tests; 28 run tag's scripts, the tag check, the upload script, the release check (against a stand-in gh) and the refusal explanation. With origin/main's workflows, 58 fail. test_release_gates.py gains a gh release upload publishing marker, the App's version_check rows and release_status cases; releasing.rst and workflows.rst re-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's id: upload and continue-on-error. The tag check stays blocking on KTLO legs, since it fails alike on every leg and continue-on-error would let one publish under a tag naming another commit, so test_python_matrix_tiers_1633.py exempts it and test_the_check_blocks_every_leg pins it.

Owner chose the App-token design (#1634). github mints a GitHub App installation token with actions/create-github-app-token@v3 (permission-contents: write, permission-workflows: write; :503-511) and spends it only as Create Release's token: (:547): Create a release refuses GITHUB_TOKEN a target that changes a workflow file relative to the default branch, and accepts an App token with "Workflows" write. With vars.RELEASE_APP_CLIENT_ID unset the step skips and GITHUB_TOKEN is spent as before. version_check skips a push whose github.actor_id is vars.RELEASE_APP_BOT_ID (:210-212), and release_status reports 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 App pypcapkit-release (Contents and Workflows read and write, this repository only), repository variables RELEASE_APP_CLIENT_ID and RELEASE_APP_BOT_ID (342101269), and RELEASE_APP_PRIVATE_KEY as a github-release environment secret, which no job reads before approval. Only the first approved run can confirm the installation.

Closes #1634
Refs #1640

@JarryShaw JarryShaw added ci Pull requests that change CI or workflow configuration (ci: subject prefix) review: good-to-go Cross-review at the current head says ready; CI state is separate labels Oct 13, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 4d77d6d97. Sonnet cross-review took six rounds. Rounds 4–6 each removed a failure mode that the previous round's fix had introduced:

  • Hand-tag recovery: the round-3 target pin would have refused it.
  • Stale-target re-checks before every PyPI and conda upload.
  • cron-conda.yml's ncipollo call, which could create a pending version's release and tag early during an approval wait.

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 main changes any workflow file fails at github with a scripted recovery. The alternatives are a PAT or the deploy key. Merging #1645 alongside this needs test_no_ktlo_leg_can_fail_an_aggregated_job to exempt the tag-check step; see the body.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Coverage: 89.68% (unit tier, Python 3.14, 2fd8f1fb2, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 3 20 3 95.16%
pcapkit/const 18858 954 2366 868 91.06%
pcapkit/corekit 2366 67 760 35 96.48%
pcapkit/dumpkit 275 3 100 3 98.40%
pcapkit/foundation 3030 152 1074 35 94.23%
pcapkit/interface 138 7 52 5 93.68%
pcapkit/protocols 17166 226 4580 191 98.06%
pcapkit/toolkit 1267 190 500 8 83.59%
pcapkit/utilities 494 4 146 4 98.75%
pcapkit/vendor 4409 2342 1006 157 43.25%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Resolve conflicts

@JarryShaw

Copy link
Copy Markdown
Owner Author

Recorded: resolving the conflicts. With #1645 merged, three files conflict with main: .github/workflows/create-release.yml, docs/source/contributing/releasing.rst and docs/source/contributing/workflows.rst.

The App-token round for #1634 is rewriting those same files right now, so main will be merged in on top of that round rather than beside it. The merged head then gets a fresh cross-review before the label goes back to good-to-go.

…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
@JarryShaw
JarryShaw force-pushed the fix/1634-release-tags-the-run-sha branch from 4d77d6d to 2fd8f1f Compare October 13, 2026 16:48
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 2fd8f1fb2. A Sonnet cross-review checked this Opus round, and I re-ran its three test files myself: 66, 116 and 17 passed.

Round 7 mints the release App's token in github (create-release.yml:503-511) and spends it only as Create Release's token: (:547). It also skips the App's own tag push (:210-212). The branch is rebased onto main at c3380134d, which clears the conflicts.

What the reviewer verified:

  • The guard is right across 108 event/actor/variable combinations.
  • The private key is read only in github, behind github-release.
  • With the client ID unset, Create Release spends the same token as before.
  • On the round-6 workflow, 15 + 3 of the new tests fail.

Unmeasured until a real release: whether the tag the App creates fires push with the bot as actor_id. If it does not, the guard never matches, and the cost is one approval to reject.

Optional hardening, not blocking: no test catches continue-on-error being added to the mint step, which would fall back to GITHUB_TOKEN silently.

@JarryShaw
JarryShaw merged commit edada31 into main Oct 13, 2026
44 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 13, 2026
@JarryShaw
JarryShaw deleted the fix/1634-release-tags-the-run-sha branch October 13, 2026 17:26
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Pull requests that change CI or workflow configuration (ci: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(ci): Create Release tags main's tip at approval time, not the commit it was created for

1 participant