feat: prepare 0.3.0 publish infrastructure#27
Conversation
- Add 5 missing microcrates to PUBLISH_CRATES in topological order (37 total) - Set tooling crates (feature-grid, test-grid, bdd-steps) to publish = false - Add [package.metadata.docs.rs] to 25 publishable crates - Create GitHub Actions release workflow (ci → publish → release) - Fix broken intra-doc links (public_jwk → Self::public_jwk)
- Add 5 missing microcrates to PUBLISH_CRATES in topological order (37 total) - Set tooling crates (feature-grid, test-grid, bdd-steps) to publish = false - Add [package.metadata.docs.rs] to 25 publishable crates - Create GitHub Actions release workflow (ci -> publish -> release) - Add cargo xtask publish command (single source of truth for crate list) - Fix broken intra-doc links (public_jwk -> Self::public_jwk)
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Summary by CodeRabbitRelease Notes
WalkthroughIntroduces release automation infrastructure for the Rust project with a GitHub Actions workflow (ci → publish → release pipeline), configures documentation builds across crates via docs.rs metadata, disables publication for test/feature grid crates, adds a publish CLI command with retry logic to xtask, and updates minor documentation references. Changes
Sequence DiagramsequenceDiagram
participant Tag as Git Tag Push
participant GHA as GitHub Actions
participant CI as CI Job
participant Cargo as Cargo Registry
participant Pub as Publish Job
participant GH as GitHub API
participant Rel as Release Job
Tag->>GHA: Trigger on v*
GHA->>CI: Start CI Job
CI->>CI: Setup Rust (stable/nightly)
CI->>CI: Install NASM & Tools
CI->>CI: Run cargo xtask ci
CI->>GHA: ✓ CI Complete
GHA->>Pub: Start Publish Job (after CI)
Pub->>Pub: Setup Rust & NASM
Pub->>Cargo: cargo publish (with retry logic)
Cargo->>Pub: ✓ Published
Pub->>GHA: ✓ Publish Complete
GHA->>Rel: Start Release Job (after Publish)
Rel->>GH: gh release create
GH->>GH: Generate notes from tag
GH->>Rel: ✓ Release Created
Rel->>GHA: ✓ Release Complete
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 40-42: Move the CARGO_REGISTRY_TOKEN out of the job-level env
block and instead add it only to the specific step that executes the publish
command (the step that runs "cargo xtask publish") so the token is scoped to
that single step; remove CARGO_REGISTRY_TOKEN from the job env and add an env
entry CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} under the step
that runs the publish command (the "cargo xtask publish" step) to restrict
exposure.
In `@crates/uselesskey-feature-grid/Cargo.toml`:
- Around line 13-15: The Cargo.toml for the uselesskey-feature-grid crate
includes a stale documentation field even though publish = false; remove the
documentation = "https://docs.rs/uselesskey-feature-grid" entry from the
manifest (or replace it with a local/internal URL if you intentionally want a
doc link) so the unpublished crate doesn't reference a non-existent docs.rs
page—look for the publish and documentation keys in the Cargo.toml to make this
change.
In `@xtask/src/main.rs`:
- Around line 252-259: The publish order lists dependent crate
"uselesskey-core-token" before its dependency "uselesskey-core-token-shape",
which can break publish; update the list so "uselesskey-core-token-shape"
appears before "uselesskey-core-token" (and verify any other dependency pairs
like "uselesskey-core-jwk-shape" vs "uselesskey-core-jwk-builder") so that each
crate's dependencies are listed earlier than the crate that depends on them.
- Around line 311-322: The publish step currently bails on non-retriable stderr
contents; update the logic around the stderr handling in xtask/src/main.rs (the
block using the stderr variable and the bail!("{name} publish failed...") call)
to treat crate-already-published errors as success instead of failure: detect
substrings like "already uploaded", "already exists", "already published",
"duplicate", and/or "already uploaded by another client" (case-insensitive) and,
when present, log an informational message (e.g., that the crate was already
published) and continue rather than calling bail!; keep the existing
retry/backoff for true transient errors (the current retriable logic) and only
convert the explicit already-published messages into non-failing outcomes.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (33)
.github/workflows/release.ymlcrates/uselesskey-bdd-steps/Cargo.tomlcrates/uselesskey-core-cache/Cargo.tomlcrates/uselesskey-core-factory/Cargo.tomlcrates/uselesskey-core-hash/Cargo.tomlcrates/uselesskey-core-id/Cargo.tomlcrates/uselesskey-core-jwk-builder/Cargo.tomlcrates/uselesskey-core-jwk-shape/Cargo.tomlcrates/uselesskey-core-jwk/Cargo.tomlcrates/uselesskey-core-jwks-order/Cargo.tomlcrates/uselesskey-core-keypair-material/Cargo.tomlcrates/uselesskey-core-keypair/Cargo.tomlcrates/uselesskey-core-kid/Cargo.tomlcrates/uselesskey-core-negative-pem/Cargo.tomlcrates/uselesskey-core-negative/Cargo.tomlcrates/uselesskey-core-seed/Cargo.tomlcrates/uselesskey-core-sink/Cargo.tomlcrates/uselesskey-core-token-shape/Cargo.tomlcrates/uselesskey-core-token/Cargo.tomlcrates/uselesskey-core-x509-derive/Cargo.tomlcrates/uselesskey-core-x509-negative/Cargo.tomlcrates/uselesskey-core-x509-spec/Cargo.tomlcrates/uselesskey-core-x509/Cargo.tomlcrates/uselesskey-core/Cargo.tomlcrates/uselesskey-ecdsa/src/keypair.rscrates/uselesskey-ed25519/src/keypair.rscrates/uselesskey-feature-grid/Cargo.tomlcrates/uselesskey-jwk/Cargo.tomlcrates/uselesskey-pgp/Cargo.tomlcrates/uselesskey-rsa/src/keypair.rscrates/uselesskey-test-grid/Cargo.tomlcrates/uselesskey-token/Cargo.tomlxtask/src/main.rs
| env: | ||
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | ||
| steps: |
There was a problem hiding this comment.
Scope CARGO_REGISTRY_TOKEN to the publish step only.
Setting the crates.io token at job scope exposes it to all steps/actions in the publish job. Restrict it to the single step that runs cargo xtask publish.
Suggested hardening change
publish:
needs: ci
runs-on: ubuntu-latest
- env:
- CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- uses: actions/checkout@v4
@@
- name: Publish all crates
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo xtask publish📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| steps: | |
| publish: | |
| needs: ci | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Publish all crates | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: cargo xtask publish |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 40 - 42, Move the
CARGO_REGISTRY_TOKEN out of the job-level env block and instead add it only to
the specific step that executes the publish command (the step that runs "cargo
xtask publish") so the token is scoped to that single step; remove
CARGO_REGISTRY_TOKEN from the job env and add an env entry CARGO_REGISTRY_TOKEN:
${{ secrets.CARGO_REGISTRY_TOKEN }} under the step that runs the publish command
(the "cargo xtask publish" step) to restrict exposure.
| publish = false | ||
| readme = "README.md" | ||
| documentation = "https://docs.rs/uselesskey-feature-grid" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Minor: Stale documentation URL for unpublished crate.
Setting publish = false is correct for this tooling crate. However, the documentation field on line 15 still points to docs.rs/uselesskey-feature-grid, which will never exist since this crate won't be published.
Consider removing the documentation field for clarity, or leave it if you prefer consistency across all manifests.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-feature-grid/Cargo.toml` around lines 13 - 15, The
Cargo.toml for the uselesskey-feature-grid crate includes a stale documentation
field even though publish = false; remove the documentation =
"https://docs.rs/uselesskey-feature-grid" entry from the manifest (or replace it
with a local/internal URL if you intentionally want a doc link) so the
unpublished crate doesn't reference a non-existent docs.rs page—look for the
publish and documentation keys in the Cargo.toml to make this change.
| "uselesskey-core-negative-pem", | ||
| "uselesskey-core-negative", | ||
| "uselesskey-core-sink", | ||
| "uselesskey-core-token", | ||
| "uselesskey-core-token-shape", | ||
| "uselesskey-core-jwk-shape", | ||
| "uselesskey-core-jwks-order", | ||
| "uselesskey-core-jwk-builder", |
There was a problem hiding this comment.
Fix publish order: dependency appears after dependent crate.
uselesskey-core-token depends on uselesskey-core-token-shape (see crates/uselesskey-core-token/Cargo.toml), but Line 255 currently lists uselesskey-core-token before uselesskey-core-token-shape. This can make publish fail before reaching the needed dependency.
Suggested ordering fix
- "uselesskey-core-token",
- "uselesskey-core-token-shape",
+ "uselesskey-core-token-shape",
+ "uselesskey-core-token",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "uselesskey-core-negative-pem", | |
| "uselesskey-core-negative", | |
| "uselesskey-core-sink", | |
| "uselesskey-core-token", | |
| "uselesskey-core-token-shape", | |
| "uselesskey-core-jwk-shape", | |
| "uselesskey-core-jwks-order", | |
| "uselesskey-core-jwk-builder", | |
| "uselesskey-core-negative-pem", | |
| "uselesskey-core-negative", | |
| "uselesskey-core-sink", | |
| "uselesskey-core-token-shape", | |
| "uselesskey-core-token", | |
| "uselesskey-core-jwk-shape", | |
| "uselesskey-core-jwks-order", | |
| "uselesskey-core-jwk-builder", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@xtask/src/main.rs` around lines 252 - 259, The publish order lists dependent
crate "uselesskey-core-token" before its dependency
"uselesskey-core-token-shape", which can break publish; update the list so
"uselesskey-core-token-shape" appears before "uselesskey-core-token" (and verify
any other dependency pairs like "uselesskey-core-jwk-shape" vs
"uselesskey-core-jwk-builder") so that each crate's dependencies are listed
earlier than the crate that depends on them.
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| let retriable = stderr.contains("failed to select a version") | ||
| || stderr.contains("no matching package") | ||
| || stderr.to_lowercase().contains("not found"); | ||
|
|
||
| if retriable { | ||
| eprintln!(" Dependency indexing race detected. Waiting 60s before retry..."); | ||
| std::thread::sleep(std::time::Duration::from_secs(60)); | ||
| } else { | ||
| eprint!("{stderr}"); | ||
| bail!("{name} publish failed with a non-retriable error"); | ||
| } |
There was a problem hiding this comment.
Make publish resumable after partial success.
If a run partially publishes crates and then fails, rerunning currently aborts on “already published/already exists” errors as non-retriable. That blocks recovery for tag workflows. Treat those cases as success and continue.
Suggested resiliency fix
let stderr = String::from_utf8_lossy(&output.stderr);
- let retriable = stderr.contains("failed to select a version")
+ let stderr_lc = stderr.to_lowercase();
+ let already_published = stderr_lc.contains("already uploaded")
+ || stderr_lc.contains("already exists")
+ || stderr_lc.contains("already published");
+ let retriable = stderr.contains("failed to select a version")
|| stderr.contains("no matching package")
- || stderr.to_lowercase().contains("not found");
+ || stderr_lc.contains("not found");
+
+ if already_published {
+ eprintln!(" {name} is already published; continuing.");
+ success = true;
+ break;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let stderr = String::from_utf8_lossy(&output.stderr); | |
| let retriable = stderr.contains("failed to select a version") | |
| || stderr.contains("no matching package") | |
| || stderr.to_lowercase().contains("not found"); | |
| if retriable { | |
| eprintln!(" Dependency indexing race detected. Waiting 60s before retry..."); | |
| std::thread::sleep(std::time::Duration::from_secs(60)); | |
| } else { | |
| eprint!("{stderr}"); | |
| bail!("{name} publish failed with a non-retriable error"); | |
| } | |
| let stderr = String::from_utf8_lossy(&output.stderr); | |
| let stderr_lc = stderr.to_lowercase(); | |
| let already_published = stderr_lc.contains("already uploaded") | |
| || stderr_lc.contains("already exists") | |
| || stderr_lc.contains("already published"); | |
| let retriable = stderr.contains("failed to select a version") | |
| || stderr.contains("no matching package") | |
| || stderr_lc.contains("not found"); | |
| if already_published { | |
| eprintln!(" {name} is already published; continuing."); | |
| success = true; | |
| break; | |
| } | |
| if retriable { | |
| eprintln!(" Dependency indexing race detected. Waiting 60s before retry..."); | |
| std::thread::sleep(std::time::Duration::from_secs(60)); | |
| } else { | |
| eprint!("{stderr}"); | |
| bail!("{name} publish failed with a non-retriable error"); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@xtask/src/main.rs` around lines 311 - 322, The publish step currently bails
on non-retriable stderr contents; update the logic around the stderr handling in
xtask/src/main.rs (the block using the stderr variable and the bail!("{name}
publish failed...") call) to treat crate-already-published errors as success
instead of failure: detect substrings like "already uploaded", "already exists",
"already published", "duplicate", and/or "already uploaded by another client"
(case-insensitive) and, when present, log an informational message (e.g., that
the crate was already published) and continue rather than calling bail!; keep
the existing retry/backoff for true transient errors (the current retriable
logic) and only convert the explicit already-published messages into non-failing
outcomes.
Summary
PUBLISH_CRATESin topological order (37 total)feature-grid,test-grid,bdd-steps) topublish = false[package.metadata.docs.rs]to 25 publishable cratesci→publish→release)cargo xtask publishcommand — single source of truth for the crate list, replacing the inline bash array in the workflowpublic_jwk→Self::public_jwk)Test plan
cargo xtask publish-preflightsucceeds (validates metadata +cargo package --no-verify)cargo check -p xtaskconfirms the newPublishvariant compilesv0.3.0and push to trigger release workflow