xtask: fix PUBLISH_CRATES order so compat shims publish after their owners#565
Conversation
…wners
The v0.7.0 release workflow failed on `cargo xtask publish`:
error: failed to prepare local package for uploading
Caused by: failed to select a version for the requirement
`uselesskey-core = "^0.7.0"`
candidate versions found which didn't match: 0.6.0, 0.5.1, ...
required by package `uselesskey-core-seed v0.7.0`
The v0.7.0 fold-into-`srp::*` refactor made the former core/x509 shards
into compatibility shims that depend on their owner crates. But
PUBLISH_CRATES still listed them at the top under "Leaf crates", which
walked them before `uselesskey-core` / `uselesskey-x509` could land on
crates.io.
Reorder the list so:
- core JWK shards publish first (the only true workspace-dep-free leaves)
- `uselesskey-core` follows
- the 9 core compat shims (seed, hash, id, cache, factory, negative-*,
sink) publish after `uselesskey-core`
- mid-level fixture and token crates follow
- `uselesskey-x509` publishes before its 5 x509 compat shims (spec,
derive, chain-negative, negative, x509)
- facade `uselesskey` stays last
Verified by topological sort over `cargo metadata` dependencies: 53
crates, 0 inversions (was 14).
publish-check did not catch this because its dry-run resolves workspace
deps against local paths, masking the crates.io-only failure. A
follow-up PR will add a topo-sort verification to publish-check.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request reorders the PUBLISH_CRATES array in xtask/src/main.rs to organize crates by category and dependency order. Feedback suggests further adjustments to ensure compatibility shims, such as uselesskey-core-hmac-spec, are published after their respective owner crates to prevent resolution errors during release. There is also a suggestion to clarify the 'True leaf crates' comment where internal dependencies exist for JWK shims.
| // True leaf crates (no workspace deps) | ||
| "uselesskey-core-hmac-spec", | ||
| "uselesskey-jwk", | ||
| "uselesskey-core-kid", |
There was a problem hiding this comment.
The crate uselesskey-core-hmac-spec appears to be a compatibility shim for uselesskey-hmac (as indicated by the mapping in compatibility_shim_owner at line 3943). If it depends on its owner crate, it must be published after it to avoid resolution errors during release. Currently, it is listed at line 844, while uselesskey-hmac is at line 872. I suggest moving it after its owner.
Also, the comment // True leaf crates (no workspace deps) is technically inaccurate for the JWK shims (like uselesskey-core-kid) if they depend on uselesskey-jwk. I've suggested a re-grouping to clarify this.
Note: uselesskey-core-rustls-pki (line 898) also appears to be an inversion relative to its owner uselesskey-rustls (line 902), though it falls outside this diff hunk.
// True leaf crates (no workspace deps)
"uselesskey-jwk",
// JWK compatibility shims (depend on uselesskey-jwk)
"uselesskey-core-kid",| "uselesskey-rsa", | ||
| "uselesskey-ecdsa", | ||
| "uselesskey-ed25519", | ||
| "uselesskey-hmac", |
The previous fix to PUBLISH_CRATES moved uselesskey-core-seed from index 0 to index 8, which broke `resolve_start_index_from_first_crate` in CI. The test was asserting that the *specific name* "uselesskey-core-seed" resolves to index 0; what it actually wants to verify is that the *first crate in the list* resolves to index 0, whatever its name happens to be. Switch the test to use PUBLISH_CRATES[0] so the assertion is robust to future reorderings. `cargo test -p xtask` — 258 passed.
Both v0.7.0 publish attempts via `cargo xtask publish` failed: 1. First attempt (tag at #562 merge): `uselesskey-core-seed` packaged before `uselesskey-core` because PUBLISH_CRATES listed compat shims before owner crates. Fixed in #565. 2. Second attempt (tag at #565 merge): `uselesskey-core` packaged but failed because its dev-dep `uselesskey-test-support` (workspace entry has `version = "0.7.0"`) is not in PUBLISH_CRATES at all. Both failures are the same class of bug: a hand-maintained linear PUBLISH_CRATES order that drifts out of sync with cargo metadata when deps move. The user built `shipper` for exactly this — it computes a deterministic DAG from cargo metadata, reconciles against registry truth, retries transient errors, and persists state under `.shipper/` so interrupted runs can resume. Switch both `release.yml` and `publish-retry.yml` to: - `cargo install shipper --locked --version "0.3.0-rc.2"` - `shipper plan` (emit JSON plan into `.shipper/`) - `shipper preflight --policy safe` - `shipper publish --policy safe --readiness-method both --max-attempts 12 --max-delay 15m` - Upload `.shipper/` state as artifacts (plan + final) `publish-retry.yml` adds a `shipper resume --resume-from <crate>` path when `inputs.resume_from` is set, and uses `shipper plan --format text` as the dry-run preview. Token continues to come from `secrets.CARGO_REGISTRY_TOKEN`. OIDC via `crates-io-auth-action` is a separate follow-up. ## Recovery for the in-flight v0.7.0 3 crates are at 0.7.0 on crates.io (`uselesskey-core-hmac-spec`, `uselesskey-jwk`, `uselesskey-core-jwk`); 50 remain at 0.6.0. After this PR merges, dispatch `Publish Retry` on `v0.7.0` with `dry_run: true` to preview, then dispatch again non-dry-run. Shipper reconciles against the registry before any cargo activity, so the 3 already- published crates will be skipped. ## Future work - Adopt `rust-lang/crates-io-auth-action` OIDC token flow. - Drop the hand-maintained `PUBLISH_CRATES` constant in `xtask/src/main.rs` once shipper handles all paths.
Shipper 0.3.0-rc.2 emits the publish plan in alphabetical-looking order rather than topological order (EffortlessMetrics/shipper#173). With \`uselesskey-aws-lc-rs\` at position 1 and \`uselesskey-core\` at #2, shipper's linear walk fails immediately because cargo publish of aws-lc-rs requires uselesskey-ecdsa (#22) and other deps to already be on crates.io. The owner of shipper will fix this in the next patch. Until then, revert both \`release.yml\` and \`publish-retry.yml\` to use \`cargo xtask publish\`. The PUBLISH_CRATES order is correct after #565, and the test-helper dev-dep fix in #569 removes the cargo verification gap that the original cargo xtask path also tripped over. Keep \`.shipper/\` in \`.gitignore\` and the existing \`Quality gate\` / \`Publish preflight\` / \`Publish dry-run\` steps in preflight job — those are unaffected by the shipper revert and still useful. After this PR merges, retag v0.7.0 on the new main HEAD and let \`release.yml\` run end-to-end. shipper#173 stays open for the next release cycle.
…ing) (#572) The v0.7.0 release lane hit a publish failure because PUBLISH_CRATES had compatibility shims listed before their owners: uselesskey-core-seed (a shim that depends on uselesskey-core) was at index 0 while uselesskey-core was at index 21. cargo xtask publish walks the list in order, so it tried to publish uselesskey-core-seed v0.7.0 first and cargo publish failed because uselesskey-core ^0.7.0 was not on crates.io yet. PR #565 reordered PUBLISH_CRATES to put owners before shims, but the existing cargo xtask publish-check did not catch the bug locally because its dry-run mode (cargo package --no-verify) resolves workspace deps against local paths, not crates.io. The bug was only visible against the live registry. This adds a verify_publish_order_is_topological() function that uses cargo metadata to walk every (crate, workspace-dep) pair in PUBLISH_CRATES and asserts the dep is listed earlier. publish_check now calls it before any dry-runs so a future reorder fails the local gate before it can break the live publish. A matching unit test (publish_order_is_topological) keeps the current ordering honest. 0 inversions exist today; the guard is a no-op against main HEAD.
…::spec (#595) * refactor(hmac): fold core-hmac-spec into uselesskey-hmac::srp::spec Inverts the dep direction between `uselesskey-hmac` and `uselesskey-core-hmac-spec`. `HmacSpec`, its impl block, and its inline tests now live in `crates/uselesskey-hmac/src/srp/spec.rs`; `uselesskey-core-hmac-spec` becomes a thin re-export shim re-exporting `uselesskey_hmac::srp::spec::*` to preserve the v0.7.x public-internal surface for downstream pinners. This matches the v0.7.0 fold pattern established for `core-cache`, `core-id`, `core-hash`, `core-factory`, `token-spec`, etc. (#565-#570 and follow-ups) where the owner fixture crate took canonical ownership and the previously-published microcrate became a compatibility shim. Mechanical changes: - New `crates/uselesskey-hmac/src/srp/{mod.rs,spec.rs}` carry the `HmacSpec` enum + helpers + inline `#[cfg(test)] mod tests` block. - `crates/uselesskey-hmac/src/spec.rs` and `lib.rs` now re-export from `srp::spec`; the public `uselesskey_hmac::HmacSpec` path is unchanged. - `uselesskey-hmac/Cargo.toml` drops the dep on `uselesskey-core-hmac-spec`. - `uselesskey-core-hmac-spec/src/lib.rs` is now `pub use uselesskey_hmac::srp::spec::*;`. Its Cargo.toml adds `uselesskey-hmac.workspace = true` and updates the description. - `xtask::PUBLISH_CRATES` and `xtask::plan::dependents()` move `uselesskey-core-hmac-spec` to the post-`uselesskey-hmac` position, matching `uselesskey-token-spec` after the token fold. - `docs/explanation/architecture.md`, `docs/metadata/workspace-docs.json`, and the regenerated `docs/reference/support-matrix.md` describe the crate as a compatibility shim. The shim's integration / mutant_killers / prop / snapshot tests are unchanged and continue to pass via the new re-export. `stable_bytes()` values, JOSE alg names, byte lengths, and Debug output are identical to v0.7.1; no derivation-stability impact. This is PR-1 of the v0.8.0 SRP collapse lane. The shim crate stays published for v0.7.x compat and is scheduled for removal in PR-4. * refactor(hmac): bump uselesskey-hmac and core-hmac-spec to 0.7.2 The fold introduces a new public `uselesskey_hmac::srp::spec` module which the `uselesskey-core-hmac-spec` shim now re-exports. The published v0.7.1 of `uselesskey-hmac` does not have this path, so `cargo publish --dry-run -p uselesskey-core-hmac-spec` would resolve its dep against registry v0.7.1 and fail with `E0433: cannot find 'srp' in 'uselesskey_hmac'`. Bump both crates to v0.7.2 so the live publish lane resolves the shim against the new (v0.7.2) `uselesskey-hmac` that ships in the same wave. Downstream consumers (`uselesskey`, `uselesskey-jsonwebtoken`, `uselesskey-jose-openid`, `uselesskey-rustcrypto`, `uselesskey-cli`, `uselesskey-interop-tests`) keep their existing `version = "0.7.1"` constraint — cargo treats it as `^0.7.1`, which accepts v0.7.2. `publish-check` once this PR lands: the v0.7.2 publish of `uselesskey-hmac` precedes the v0.7.2 publish of `uselesskey-core-hmac-spec` (already enforced by PUBLISH_CRATES topological order), so the shim's dry-run finds the new `srp::spec` path on the registry.
…::srp::pki (#598) * refactor(rustls): fold core-rustls-pki into uselesskey-rustls::srp::pki Inverts the dep direction between `uselesskey-rustls` and `uselesskey-core-rustls-pki`. The `RustlsPrivateKeyExt`, `RustlsCertExt`, and `RustlsChainExt` traits and their impls for RSA, ECDSA, Ed25519, and X.509 fixture types now live in `crates/uselesskey-rustls/src/srp/pki.rs`; `uselesskey-core-rustls-pki` becomes a thin re-export shim re-exporting `uselesskey_rustls::srp::pki::*` to preserve the v0.7.x public-internal surface for downstream pinners. This matches the v0.7.0 fold pattern established for `core-cache`, `core-id`, `core-hash`, `core-factory`, `token-spec`, etc. (#565-#570 and follow-ups) and the v0.8.0 hmac-spec fold (#595) where the owner crate took canonical ownership and the previously-published microcrate became a compatibility shim. Mechanical changes: - New `crates/uselesskey-rustls/src/srp/{mod.rs,pki.rs}` carry the three extension traits, all impl blocks, and the inline `#[cfg(test)] mod tests` blocks. - `crates/uselesskey-rustls/src/lib.rs` declares `pub mod srp` (doc- hidden) and re-exports the pki traits at the top level. The public `uselesskey_rustls::{RustlsPrivateKeyExt, RustlsCertExt, RustlsChainExt}` path is unchanged. - `uselesskey-rustls/Cargo.toml` drops the dep on `uselesskey-core-rustls-pki` and gains a direct `rustls-pki-types` dep; per-key-type feature flags drop the `uselesskey-core-rustls-pki/*` re-export legs. - `uselesskey-core-rustls-pki/src/lib.rs` is now a thin shim: `pub use uselesskey_rustls::srp::pki::*`. Its Cargo.toml drops the direct rustls-pki-types and key-type deps, adds `uselesskey-rustls.workspace = true`, and routes its features to `uselesskey-rustls/*`. - The shim's `tests/{integration,deterministic,snapshots_rustls_pki}.rs` and snapshot files move under `crates/uselesskey-rustls/tests/`. Snapshot file names and contents are preserved (same deterministic seed `uselesskey-rustls-pki-snap-seed-v1`); only the Rust import paths are rewritten from `uselesskey_core_rustls_pki::*` to `uselesskey_rustls::*`. - `xtask::PUBLISH_CRATES` moves `uselesskey-core-rustls-pki` to a post-`uselesskey-rustls` slot under a "rustls compatibility shim" comment, matching `uselesskey-core-hmac-spec` after the hmac fold. - `xtask::plan::dependents()` makes `uselesskey-core-rustls-pki` a leaf and adds it as a `uselesskey-rustls` dependent (so a rustls source change still triggers a shim rebuild). - `docs/explanation/architecture.md`, `docs/metadata/workspace-docs.json`, and `docs/reference/support-matrix.md` describe the crate as a compatibility shim. Both `uselesskey-rustls` and `uselesskey-core-rustls-pki` bump to 0.7.2 so the shim's re-export of `uselesskey_rustls::srp::pki` resolves against a registry version that ships the new path: `cargo publish --dry-run -p uselesskey-core-rustls-pki` would otherwise resolve against the published v0.7.1 of `uselesskey-rustls`, which does not have `srp::pki`, and fail with `E0432: unresolved imports`. Downstream consumers (`uselesskey`, `uselesskey-bdd-steps`, `uselesskey-interop-tests`) keep their existing `version = "0.7.1"` constraint — cargo treats it as `^0.7.1`, which accepts v0.7.2. This is PR-2 of the v0.8.0 SRP collapse lane. The shim crate stays published for v0.7.x compat and is scheduled for removal in a later v0.8.0 PR. * docs(rustls): bump version snippets to 0.7.2 for the rustls fold The `uselesskey-rustls` v0.7.2 bump (PR-2 of the v0.8.0 SRP collapse lane) outpaces the workspace-wide `release_version: "0.7.1"` field in `docs/metadata/workspace-docs.json`, which docs-sync uses to render all dependency snippets uniformly. The preflight `doc-versions` lane checks each rendered snippet against the per-crate `Cargo.toml` version, so a uniform `0.7.1` snippet for `uselesskey-rustls` would fail against its bumped manifest. Add an optional `version` override to `SnippetDependency`, route it through `render_single_dependency_snippet`, and set the override to `"0.7.2"` on the `uselesskey-rustls` snippet entry. The other deps in the same snippets continue to render against the workspace-wide `release_version` field. Regenerate the README and how-to dependency blocks accordingly. This same pattern can be reused on subsequent SRP-fold PRs where a single fold-target crate bumps ahead of the rest of the workspace. * xtask(docs-sync): add per-dep version override test Adds a unit test exercising the new optional `version` field on `SnippetDependency`, locking in the SRP-fold pattern where a single crate has been bumped ahead of the workspace-wide `release_version` (e.g. v0.8.0 SRP folds where one crate goes to 0.7.2 while the rest of the workspace stays on 0.7.1) so future regressions on the override path are caught in `cargo test -p xtask`. Also fixes the existing snippet-rendering test to provide the new `version: None` field after the struct literal added the field.
Summary
v0.7.0 release blocker. The release workflow for tag `v0.7.0` failed on `cargo xtask publish` with:
```
error: failed to prepare local package for uploading
Caused by: failed to select a version for the requirement `uselesskey-core = "^0.7.0"`
candidate versions found which didn't match: 0.6.0, 0.5.1, ...
required by package `uselesskey-core-seed v0.7.0`
```
The v0.7.0 fold-into-`srp::` refactor turned the former `uselesskey-core-` and `uselesskey-core-x509*` shards into compatibility shims that depend on their owner crates. But `PUBLISH_CRATES` still listed them at the top under "Leaf crates", which walked them before `uselesskey-core` / `uselesskey-x509` could land on crates.io.
Inversions before this PR
Topo-sort over `cargo metadata` deps found 14 inversions:
Fix
Reorder `PUBLISH_CRATES` so:
Validation
Recovery plan for v0.7.0
After this PR merges:
Nothing v0.7.0 reached crates.io on the first attempt (verified: all crates still default to 0.6.0 on crates.io), so retag is safe.
Follow-up
`cargo xtask publish-check` did not catch this because its dry-run resolves workspace deps against local paths. A follow-up PR will add a topo-sort verification to publish-check so future ordering bugs fail loudly before tag push.
Test plan