Summary
Migrate the crates.io release pipeline from the hand-rolled publish-crates
job in .github/workflows/release.yml to release-plz.
Motivation
The current release workflow hand-rolls workspace publishing in bash, and that
hand-rolled logic has produced two distinct release-blocking bugs:
-
Per-crate dry-run cannot resolve internal deps. The preflight ran
cargo publish -p <crate> --dry-run per crate. On a version bump, a dependent
crate (e.g. gts-macros -> gts-id ^0.10.0) fails because the new version is
not yet on crates.io. Fixed by switching to cargo publish --workspace --dry-run
(which resolves internal deps against the locally packaged crates).
-
crates.io API polled without a User-Agent. is_published() / wait_visible()
call the crates.io API with curl but no UA header. crates.io's data-access
policy rejects such requests with HTTP 403, so is_published always returns
false and wait_visible spins for 5 minutes then errors out — even though the
crate was actually published. Fixed by sending a descriptive UA header.
Both are symptoms of the same root problem: we are re-implementing publish
ordering, registry-index propagation waiting, and API access ourselves, instead
of delegating to a mature tool. Each release exercises fragile code paths that
only break on real version bumps, which is exactly when they are hardest to test.
Proposal
Adopt release-plz, as already done in the sibling Cyber Fabric (cyberware-rust)
repository. release-plz handles:
- topological publish ordering across interdependent workspace crates;
- registry-index propagation waiting + throttling (
publish_timeout);
- crates.io API access with a correct client;
- changelog generation from Conventional Commits (already followed here);
- version bumping via a "release PR" flow;
- GitHub release creation.
Reference implementation: cyberware-rust — .github/workflows/release-plz.yml
and release-plz.toml.
Open questions / decisions
- Cross-platform binaries. The current
release.yml also builds gts binaries
for linux x64/arm64, macOS arm64, and windows, and uploads them to the GitHub
Release with SHA256SUMS. release-plz does not build binaries. Decide between:
(a) keep a separate binary-build job triggered off the release-plz tag/release;
(b) drop prebuilt binaries (rely on cargo install);
(c) keep the current tag-driven workflow for binaries + GH release and use
release-plz only for crates.io publishing.
- Release model. Move from the current tag-driven manual version bump to the
release-plz release-PR flow, or keep manual control?
publish = false crates. gts-cli and gts-macros-cli must remain unpublished
(already publish = false in their manifests; mirror in release-plz.toml).
- Repository guard. Gate the publish job on
github.repository == 'GlobalTypeSystem/gts-rust'
so forks (e.g. aviator5/gts-rust) never publish.
Out of scope / follow-up
The two bugs above are already fixed in release.yml as a stopgap so that the
in-flight v0.10.0 release can complete. This issue tracks the proper migration
that removes the hand-rolled logic entirely.
Summary
Migrate the crates.io release pipeline from the hand-rolled
publish-cratesjob in
.github/workflows/release.ymltorelease-plz.Motivation
The current release workflow hand-rolls workspace publishing in bash, and that
hand-rolled logic has produced two distinct release-blocking bugs:
Per-crate dry-run cannot resolve internal deps. The preflight ran
cargo publish -p <crate> --dry-runper crate. On a version bump, a dependentcrate (e.g.
gts-macros->gts-id ^0.10.0) fails because the new version isnot yet on crates.io. Fixed by switching to
cargo publish --workspace --dry-run(which resolves internal deps against the locally packaged crates).
crates.io API polled without a
User-Agent.is_published()/wait_visible()call the crates.io API with
curlbut no UA header. crates.io's data-accesspolicy rejects such requests with HTTP 403, so
is_publishedalways returnsfalse and
wait_visiblespins for 5 minutes then errors out — even though thecrate was actually published. Fixed by sending a descriptive UA header.
Both are symptoms of the same root problem: we are re-implementing publish
ordering, registry-index propagation waiting, and API access ourselves, instead
of delegating to a mature tool. Each release exercises fragile code paths that
only break on real version bumps, which is exactly when they are hardest to test.
Proposal
Adopt
release-plz, as already done in the sibling Cyber Fabric (cyberware-rust)repository.
release-plzhandles:publish_timeout);Reference implementation:
cyberware-rust—.github/workflows/release-plz.ymland
release-plz.toml.Open questions / decisions
release.ymlalso buildsgtsbinariesfor linux x64/arm64, macOS arm64, and windows, and uploads them to the GitHub
Release with SHA256SUMS.
release-plzdoes not build binaries. Decide between:(a) keep a separate binary-build job triggered off the release-plz tag/release;
(b) drop prebuilt binaries (rely on
cargo install);(c) keep the current tag-driven workflow for binaries + GH release and use
release-plz only for crates.io publishing.
release-plz release-PR flow, or keep manual control?
publish = falsecrates.gts-cliandgts-macros-climust remain unpublished(already
publish = falsein their manifests; mirror inrelease-plz.toml).github.repository == 'GlobalTypeSystem/gts-rust'so forks (e.g.
aviator5/gts-rust) never publish.Out of scope / follow-up
The two bugs above are already fixed in
release.ymlas a stopgap so that thein-flight
v0.10.0release can complete. This issue tracks the proper migrationthat removes the hand-rolled logic entirely.