Skip to content

fix: harden release workflow — tag-based versioning, no push to main#581

Merged
paddymul merged 3 commits intomainfrom
fix/release-notes-zero-prs
Feb 25, 2026
Merged

fix: harden release workflow — tag-based versioning, no push to main#581
paddymul merged 3 commits intomainfrom
fix/release-notes-zero-prs

Conversation

@paddymul
Copy link
Collaborator

Summary

Follow-up fixes to the automated release workflow after post-merge Codex review and CI failures.

  • Exclusive PR search boundary: get_tag_date() was truncating tag timestamps to YYYY-MM-DD and using merged:>=DATE, which re-pulls same-day PRs into the next release. Fixed by returning the full ISO-8601 timestamp and using exclusive merged:>TIMESTAMP.
  • Release workflow no longer pushes to main: The chore: release commit 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.
  • Version derived from git tags: Prevents version collisions when pyproject.toml on main lags behind (e.g. tag 0.12.13 exists but pyproject still says 0.12.12). Includes an early-exit guard for duplicate tags.
  • Changelog as artifact: The generated changelog_entry.md is uploaded as a workflow artifact to apply in the next dev PR via python scripts/update_changelog.py.
  • Lint: Remove unused pytest import (ruff F401).

Test plan

  • Lint passes (ruff)
  • Unit tests pass (test_release_scripts.py)
  • Release workflow: tag push succeeds, no direct push to main attempted

🤖 Generated with Claude Code

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines 108 to 110
- 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 }}"

Choose a reason for hiding this comment

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

P1 Badge 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>
@github-actions
Copy link

github-actions bot commented Feb 24, 2026

📦 TestPyPI package published

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

or 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.dev22417437423

MCP server for Claude Code

claude 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

@paddymul
Copy link
Collaborator Author

@codex review again

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +74 to +77
# 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

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

# 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)

Choose a reason for hiding this comment

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

P2 Badge 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>
@paddymul paddymul added this pull request to the merge queue Feb 25, 2026
Merged via the queue into main with commit b7956f8 Feb 25, 2026
22 of 24 checks passed
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.

1 participant