Skip to content

ci(release): run the candidate gate on every build, resume included - #842

Merged
yeongseon merged 3 commits into
mainfrom
ci/candidate-gate-review-fixes
Oct 11, 2026
Merged

yeongseon merged 3 commits into
mainfrom
ci/candidate-gate-review-fixes

Conversation

@yeongseon

@yeongseon yeongseon commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

This applies the independent review findings on the candidate gate merged in #835:

  • Every successful build now goes through the candidate gate. verify-candidate and require-candidate run after every successful build, a tagged resume included. The tag_state == same skip is gone, so every run that publishes has passed the candidate gate in the same run.
  • A missing report no longer hides the reasons. The report download in require-candidate continues 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.
  • The report must come from this repository. The check also requires run.caller_repository == GITHUB_REPOSITORY.
  • scripts/verify_dist.py handles read errors. An OSError now 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

resume always rebuilds, and builds are not reproducible. In pycubrid, the same source gave wheel 24622ebc… in run 38141291415 and ecea62ce… in run 38142348703. With the skip, a resume at an existing tag could upload bytes that no cookbook cell had tested, which contradicts the "never a rebuild" rule in RELEASING.md.

Alternative not chosen

Keep the skip for a gh run rerun --failed of the same run, and run the gate only for a fresh resume. No skip is needed to tell these apart. A --failed rerun does not re-run jobs that already succeeded: build, verify-candidate and require-candidate keep their attempt-1 success, and publish re-runs on the same release-dist and build outputs. A fresh resume, 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-candidate if is now needs.build.result == 'success'.
    • require-candidate no longer has the skip branch or the PUBLISH/TAG_STATE inputs.
    • The report download has continue-on-error: true (and no if).
    • The check gains "report is from this repository".
    • The comments are updated.
  • scripts/release_summary.py: the skipped candidate state is removed.
  • scripts/verify_dist.py: catches OSError.
  • Tests:
    • the job graph (no tag_state in any gate condition) and the report download;
    • the step script, with new cases: another repository and no repository in the report, a skipped or cancelled call, and a missing report listing all reasons;
    • the summary (a tagged resume never skips the gate);
    • verify_dist.py (an OSError gives ::error::).
  • Docs: RELEASING.md (the verify-candidate row, the publish rule, the candidate section and the resume row now describe no skip and the --failed rerun) and the CHANGELOG.md [Unreleased] entry.

Known differences / Not changed

  • verify_dist.py and release_summary.py stay byte-identical with sqlalchemy-cubrid.
  • publish-pypi.yml keeps exactly the existing package-specific differences.
  • Behavior change for recovery: a resume after 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 is gh run rerun --failed within the 14-day artifact retention.
  • Not changed: the full matrix gate, the duplicate guard, immutable tags, and the post-publication verify-cookbook.

Validation

Commands run at the PR head:

  • lint and format: passed;
  • the full offline suite and the repository-tooling suite: passed;
  • the changelog lint, the docs translation check (where the repo has one) and actionlint on the release workflow: passed;
  • PR CI: all checks green.

The failing unit cases for the new require-candidate checks 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 at 1922c7e: success, wheel sha256 941b9a1c…83ae verified from 1922c7e. Earlier run 38155591485 at 1437533: success. The later merge of origin/main (3e7e13c) brings only performance-guide docs and changes no workflow, script or test file.

publish was skipped and the summary read dry run passed; nothing published; candidate verification success. A dry run never sets publish=true, and the pypi environment allows only main, so no tag, Release or PyPI file was created.

Not exercised:

  • a real release;
  • a resume dispatch (which now runs the gate on its rebuilt wheel);
  • a gh run rerun --failed after 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

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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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.

yeongseon and others added 2 commits October 12, 2026 01:45
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>
@yeongseon
yeongseon merged commit 9da71c1 into main Oct 11, 2026
33 checks passed
@yeongseon
yeongseon deleted the ci/candidate-gate-review-fixes branch October 11, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants