feat(release): per-package tags, publish gating, and version-existence check - #91
Merged
Conversation
…e check Fixes #80 and #81: releasing a single package used to publish all three in parallel jobs, leaving the other two waiting on a reviewer approval for a version already live on PyPI — the only way out was cancelling the whole run by hand, which happened three times. - release.yml: tag trigger changes from bare `vX.Y.Z` to per-package `<package>-v<version>` (e.g. nullain-sdk-v0.7.2); workflow_dispatch gains a `package` input. A new resolve-target job determines the package from the tag/input and checks whether that version already exists on the target index BEFORE any environment's approval gate — a duplicate version fails there, not after an approval was already burned. Each publish-*-<package> job's `if:` now also requires the resolved package to match, so the other two packages' jobs are simply not part of the run (skipped, never waiting). - scripts/bump_version.py: rewritten to bump exactly one package's pyproject.toml `version =` line, matching how the packages already version independently in practice (sdk 0.7.1, tools 0.4.0, agentd 0.1.0). Drops the __version__ literal search (the three __init__.py files derive it from importlib.metadata.version(...) dynamically now) and the cross-package sync check that assumed a single shared version. Tightening another package's internal >=X.Y.Z pin is opt-in via --bump-dependents (a compatibility decision, not a mechanical one) — but the dry-run always warns when a stale pin exists and the flag wasn't passed, naming the file and the exact command, since silent opt-in is how the pins went stale in the first place. - Makefile: bump-version/bump-version-apply take PACKAGE= alongside VERSION=, plus optional BUMP_DEPENDENTS=1. - tests/unit/test_bump_version_script.py: rewritten for the new single-package behavior (isolation between packages, opt-in pin bumping, the stale-pin warning, silent skip of a pin relationship that doesn't exist in a given fixture). - docs/releasing.md: new — the full flow (bump, tag, what to expect in Actions, manual dispatch), and an explicit note that bare vX.Y.Z tags are legacy and must not be recreated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
Fixes #80 and #81: releasing a single package used to publish all three in parallel jobs, leaving the other two waiting on a reviewer approval for a version already live on PyPI — the only way out was cancelling the whole run by hand, which happened three times today.
release.yml: tag trigger changes from barevX.Y.Zto per-package<package>-v<version>(e.g.nullain-sdk-v0.7.2);workflow_dispatchgains apackageinput. A newresolve-targetjob determines the package from the tag/input and checks whether that version already exists on the target index before any environment's approval gate — a duplicate version fails there, not after an approval was already burned. Eachpublish-*-<package>job'sif:also requires the resolved package to match, so the other two packages' jobs are simply not part of the run (skipped, neverwaiting).scripts/bump_version.py: rewritten to bump exactly one package'spyproject.tomlversion =line, matching how the packages already version independently in practice (sdk 0.7.1, tools 0.4.0, agentd 0.1.0). Drops the__version__literal search (the three__init__.pyfiles derive it fromimportlib.metadata.version(...)dynamically now) and the cross-package sync check that assumed a single shared version. Tightening another package's internal>=X.Y.Zpin is opt-in via--bump-dependents— but the dry-run always warns when a stale pin exists and the flag wasn't passed, naming the file and the exact command.Makefile:bump-version/bump-version-applytakePACKAGE=alongsideVERSION=, plus optionalBUMP_DEPENDENTS=1.docs/releasing.md: new — the full flow, and an explicit note that barevX.Y.Ztags are legacy and must not be recreated.Test plan
make check(ruff + pyright strict + pytest) green — 731 passed, 7 skippedtests/unit/test_bump_version_script.pyrewritten (14 tests) for single-package behavior, all passingnullain-sdk-v0.7.2→nullain-sdk) and wheel-filename→version parsing against a real built wheel200for an existing version,404for a nonexistent one)release.ymlYAML validated0.7.1/0.4.0/0.1.0unchanged)Closes #80, closes #81.
🤖 Generated with Claude Code