Repository navigation
ci(release): run the candidate gate on every build, resume included - #842
Merged
Merged
Conversation
Builds are not reproducible, so a tagged resume (which rebuilds) could upload bytes no cookbook cell had seen. verify-candidate now runs after every successful build, with no tag_state skip; a `gh run rerun --failed` of the same run keeps its earlier, verified results and artifact. require-candidate: the report download continues on error so the check lists every failed reason, and the report must come from this repository (run.caller_repository == GITHUB_REPOSITORY). verify_dist.py reports an OSError as ::error:: instead of a traceback. Refs cubrid-lab/cubrid-cookbook-python#240 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The changes are coherent, but they modify the publication-critical release path and need final human review with recorded workflow validation.
0 open findings
What changed in this PR
Ensures every rebuilt release artifact, including recovery resumes, passes same-run candidate verification before publication.
Changes:
- Removes the tagged-resume candidate-gate bypass.
- Validates report provenance and preserves diagnostics when reports are missing.
- Updates error handling, tests, release summaries, and documentation.
| File | Description |
|---|---|
.github/workflows/publish-pypi.yml |
Makes candidate verification mandatory and validates repository provenance. |
scripts/release_summary.py |
Removes the skipped-candidate state. |
scripts/verify_dist.py |
Reports filesystem errors cleanly. |
tests/test_release_workflows.py |
Tests mandatory gating and report validation. |
tests/test_release_summary.py |
Tests resume summary behavior. |
tests/test_verify_dist.py |
Tests filesystem-error reporting. |
RELEASING.md |
Documents mandatory candidate verification and recovery. |
CHANGELOG.md |
Records the release workflow correction. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
With continue-on-error, download-artifact can extract the report and only then fail its digest check, so the extracted JSON could still satisfy the checks. require-candidate now also requires steps.download.outcome == success, while still listing every other failed check. Refs cubrid-lab/cubrid-cookbook-python#240 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 2026
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.
What this PR does
This applies the independent review findings on the candidate gate merged in #835:
verify-candidateandrequire-candidaterun after every successfulbuild, a taggedresumeincluded. Thetag_state == sameskip is gone, so every run that publishes has passed the candidate gate in the same run.require-candidatecontinues on error, so the check runs anyway and lists every failed check. The check also requires the download itself to have succeeded (steps.download.outcome == success): the action can extract the file before it fails its digest check.run.caller_repository == GITHUB_REPOSITORY.scripts/verify_dist.pyhandles read errors. AnOSErrornow gives an::error::line instead of a traceback.This is a follow-up to #835, which merged before the review. The twin change is cubrid-lab/sqlalchemy-cubrid#814; both should merge together. cubrid-lab/cubrid-mcp-server#273 has the same change.
Refs cubrid-lab/cubrid-cookbook-python#240
Refs #833
Why
resumealways rebuilds, and builds are not reproducible. In pycubrid, the same source gave wheel24622ebc…in run 38141291415 andecea62ce…in run 38142348703. With the skip, aresumeat an existing tag could upload bytes that no cookbook cell had tested, which contradicts the "never a rebuild" rule inRELEASING.md.Alternative not chosen
Keep the skip for a
gh run rerun --failedof the same run, and run the gate only for a freshresume. No skip is needed to tell these apart. A--failedrerun does not re-run jobs that already succeeded:build,verify-candidateandrequire-candidatekeep their attempt-1 success, andpublishre-runs on the samerelease-distand build outputs. A freshresume, or a push whose tag already points at the commit, rebuilds, so it now runs the gate. So I dropped the skip entirely, as the review suggested for that case.Changes
publish-pypi.yml:verify-candidateifis nowneeds.build.result == 'success'.require-candidateno longer has the skip branch or thePUBLISH/TAG_STATEinputs.continue-on-error: true(and noif).scripts/release_summary.py: theskippedcandidate state is removed.scripts/verify_dist.py: catchesOSError.tag_statein any gate condition) and the report download;verify_dist.py(anOSErrorgives::error::).RELEASING.md(theverify-candidaterow, the publish rule, the candidate section and theresumerow now describe no skip and the--failedrerun) and theCHANGELOG.md[Unreleased]entry.Known differences / Not changed
verify_dist.pyandrelease_summary.pystay byte-identical with sqlalchemy-cubrid.publish-pypi.ymlkeeps exactly the existing package-specific differences.resumeafter a partial publish now needs a passing cookbook run on its rebuilt wheel. If the duplicate guard then finds different bytes already on PyPI, it still fails by design. The preferred recovery isgh run rerun --failedwithin the 14-day artifact retention.verify-cookbook.Validation
Commands run at the PR head:
actionlinton the release workflow: passed;The failing unit cases for the new
require-candidatechecks are: another repository, no repository in the report, a skipped call, a cancelled call, a failed download (with a valid report extracted anyway), a download that did not run, and a missing report listing every failed reason.No-publication dry run (
-f action=dry-run): 38156817588 at1922c7e: success, wheel sha256941b9a1c…83aeverified from1922c7e. Earlier run 38155591485 at1437533: success. The later merge oforigin/main(3e7e13c) brings only performance-guide docs and changes no workflow, script or test file.publishwas skipped and the summary readdry run passed; nothing published; candidate verification success. A dry run never setspublish=true, and thepypienvironment allows onlymain, so no tag, Release or PyPI file was created.Not exercised:
resumedispatch (which now runs the gate on its rebuilt wheel);gh run rerun --failedafter a partial publish. That case relies on GitHub keeping earlier successful job results, and is covered only by docs and the job graph, not live.Review: the Copilot review at the first head had no findings. Merge decision: pending.
🤖 Generated with Claude Code