fix: harden release workflow — tag-based versioning, no push to main#581
fix: harden release workflow — tag-based versioning, no push to main#581
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f17220d913
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
.github/workflows/release.yml
Outdated
| - name: Tag and push | ||
| run: | | ||
| if [ -f /tmp/changelog_entry.md ]; then | ||
| python3 - <<'PYEOF' | ||
| import sys | ||
| changelog = open('CHANGELOG.md').read() | ||
| entry = open('/tmp/changelog_entry.md').read() | ||
| lines = changelog.split('\n', 1) | ||
| header = lines[0] | ||
| rest = lines[1] if len(lines) > 1 else '' | ||
| with open('CHANGELOG.md', 'w') as f: | ||
| f.write(header + '\n\n' + entry + '\n' + rest) | ||
| PYEOF | ||
| fi | ||
|
|
||
| - name: Commit, tag, and push | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git add pyproject.toml CHANGELOG.md | ||
| git commit -m "chore: release ${{ steps.versions.outputs.new }}" | ||
| git tag "${{ steps.versions.outputs.new }}" |
There was a problem hiding this comment.
Tag a committed tree for the release version
The workflow creates and pushes the release tag after mutating pyproject.toml locally but without committing that change, so the tag points to a commit that still contains the previous version/changelog state. Because the build then runs from the dirty workspace, published artifacts can differ from what users get from the tagged source (including GitHub auto-generated source archives), which breaks release reproducibility and can yield mismatched version metadata.
Useful? React with 👍 / 👎.
Two root causes of the branch-protection push failure: 1. The workflow committed pyproject.toml + CHANGELOG directly to main, which is blocked by the merge-queue branch protection rule. 2. pyproject.toml on main can lag one version behind the latest tag (e.g. tag 0.12.13 exists but pyproject says 0.12.12), causing version collisions on re-run. Fixes: - Determine the current version from the latest semver git tag instead of pyproject.toml, so the workflow is correct even after a partial run. - Add an early-exit guard if the computed new tag already exists. - Remove the commit-to-main step entirely; push only the git tag (tag pushes bypass branch protection — the tag push already succeeded in the failing run). - pyproject.toml is still sed-bumped locally so the built wheel carries the right version; it just isn't committed back to the repo. - Upload the generated changelog_entry.md as a workflow artifact so it can be applied in the next dev PR (python scripts/update_changelog.py). - Simplify Find-previous-tag step: reuse versions.current output. - Remove pull-requests: write permission (no longer needed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
f17220d to
4b81ac9
Compare
📦 TestPyPI package publishedpip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.12.12.dev22417437423or with uv: uv pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.12.12.dev22417437423MCP server for Claude Codeclaude mcp add buckaroo-table -- uvx --from "buckaroo[mcp]==0.12.12.dev22417437423" --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo-table |
|
@codex review again |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b81ac9569
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
.github/workflows/release.yml
Outdated
| # Guard: abort early if this tag already exists (idempotency check) | ||
| if git tag -l "$NEW" | grep -q "^${NEW}$"; then | ||
| echo "::error::Tag $NEW already exists — this release may have already run. Aborting." | ||
| exit 1 |
There was a problem hiding this comment.
Detect stale partial-release tags before auto-bumping
This idempotency guard checks only the computed NEW tag, but NEW is derived from the latest existing semver tag (CURRENT) first; if a previous run failed after Tag and push (for example during gh release create or publish), rerunning with the same bump will silently advance to the next version instead of retrying the failed one. That can skip versions and generate release notes from the wrong boundary (prev_tag becomes the failed tag), which corrupts release history rather than safely aborting.
Useful? React with 👍 / 👎.
.github/workflows/release.yml
Outdated
| # Derive current version from the latest semver git tag so this | ||
| # stays correct even when the pyproject.toml version-bump commit | ||
| # hasn't been merged to main yet (e.g. partial previous run). | ||
| CURRENT=$(git tag --sort=-creatordate | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -1) |
There was a problem hiding this comment.
Make semver-tag fallback survive pipefail
The semver lookup pipeline can exit non-zero when no matching tags exist, and GitHub Actions run steps use bash with -o pipefail; in that case the step exits before reaching the fallback that reads pyproject.toml. So the intended "no semver tags found" path is currently unreachable in repositories without matching tags (or with only non-semver tag names), causing release runs to fail early.
Useful? React with 👍 / 👎.
- Fix pipefail: add `|| true` to semver tag grep pipeline so the fallback to pyproject.toml is reachable when no tags match. - Detect incomplete releases: if the latest tag has no GitHub release, retry that version instead of silently bumping to the next one. - Tag reproducibility: commit the pyproject.toml version bump before tagging so the tag points to source with the correct version string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Follow-up fixes to the automated release workflow after post-merge Codex review and CI failures.
get_tag_date()was truncating tag timestamps toYYYY-MM-DDand usingmerged:>=DATE, which re-pulls same-day PRs into the next release. Fixed by returning the full ISO-8601 timestamp and using exclusivemerged:>TIMESTAMP.chore: releasecommit was blocked by branch protection (requires PR + merge queue). The workflow now pushes only the git tag (which bypasses branch protection — confirmed by the partially-successful run). pyproject.toml is bumped locally for the wheel build only.0.12.13exists but pyproject still says0.12.12). Includes an early-exit guard for duplicate tags.changelog_entry.mdis uploaded as a workflow artifact to apply in the next dev PR viapython scripts/update_changelog.py.pytestimport (ruff F401).Test plan
test_release_scripts.py)🤖 Generated with Claude Code