Skip to content

feat(release): per-package tags, publish gating, and version-existence check - #91

Merged
netty-linux merged 1 commit into
masterfrom
ci/per-package-release
Aug 12, 2026
Merged

feat(release): per-package tags, publish gating, and version-existence check#91
netty-linux merged 1 commit into
masterfrom
ci/per-package-release

Conversation

@netty-linux

Copy link
Copy Markdown
Owner

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 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: 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 — 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-apply take PACKAGE= alongside VERSION=, plus optional BUMP_DEPENDENTS=1.
  • docs/releasing.md: new — the full flow, and an explicit note that bare vX.Y.Z tags are legacy and must not be recreated.

Test plan

  • make check (ruff + pyright strict + pytest) green — 731 passed, 7 skipped
  • tests/unit/test_bump_version_script.py rewritten (14 tests) for single-package behavior, all passing
  • Manually verified tag→package parsing (nullain-sdk-v0.7.2nullain-sdk) and wheel-filename→version parsing against a real built wheel
  • Manually verified the PyPI version-existence check against the real index (200 for an existing version, 404 for a nonexistent one)
  • release.yml YAML validated
  • Confirmed no real package version was changed by this PR (0.7.1/0.4.0/0.1.0 unchanged)

Closes #80, closes #81.

🤖 Generated with Claude Code

…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>
@netty-linux
netty-linux merged commit a44c935 into master Aug 12, 2026
13 checks passed
@netty-linux
netty-linux deleted the ci/per-package-release branch August 12, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant