Skip to content

decrating: absorb shipper-cargo into shipper::ops::cargo#74

Merged
EffortlessSteven merged 1 commit into
mainfrom
feature/decrating-absorb-cargo
Apr 15, 2026
Merged

decrating: absorb shipper-cargo into shipper::ops::cargo#74
EffortlessSteven merged 1 commit into
mainfrom
feature/decrating-absorb-cargo

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Phase-2 decrating: absorb the shipper-cargo microcrate into the core shipper crate as crate::ops::cargo. Public path shipper::cargo is preserved via pub use for backward compatibility — no breaking changes for shipper-cli or external consumers of the library.

  • crates/shipper/src/cargo.rs (the local micro-cargo-off implementation) and crates/shipper-cargo/src/lib.rs (the absorbed crate) are merged into one module at crates/shipper/src/ops/cargo/mod.rs. The merged surface includes CargoOutput, cargo_publish (+ dry-run variants), load_metadata, WorkspaceMetadata, PackageInfo, is_valid_package_name, workspace_member_names, get_version, get_package_name, plus a pub use shipper_output_sanitizer::redact_sensitive re-export.
  • The micro-cargo feature is removed from both crates/shipper/Cargo.toml and crates/shipper-cli/Cargo.toml; shipper::cargo becomes an unconditional pub use crate::ops::cargo.
  • Internal call-sites updated:
    • crates/shipper/src/engine/parallel/publish.rsuse shipper_cargo as cargo;use crate::ops::cargo;
    • crates/shipper/src/plan/mod.rs::load_metadata — delegates to crate::ops::cargo::load_metadata
  • crates/shipper-cargo/ physically deleted; dropped from [workspace.members] in the root Cargo.toml and from [dependencies] in crates/shipper/Cargo.toml.
  • All tests (unit, proptest, snapshot) from both the absorbed crate and the in-tree duplicate are ported. Snapshot files renamed to match the new module path.
  • New crates/shipper/src/ops/cargo/CLAUDE.md documenting invariants and gotchas, matching the convention used by ops/auth, ops/lock, ops/process.

Pre-flight (per R-PR-4): on this base, the only consumer of shipper-cargo was crates/shipper/Cargo.toml itself. fuzz/ only references the distinct shipper-cargo-failure crate. After this PR the shipper_cargo:: namespace is gone from the workspace entirely.

Test plan

Verified locally before pushing (per R-PR-5):

  • cargo check --workspace --all-targets — clean
  • cargo check --workspace --all-targets --all-features — clean
  • cargo clippy --workspace --all-targets --all-features -- -D warnings — clean
  • cargo test --workspace — 2000+ tests pass. One pre-existing snapshot flake (preflight_allow_dirty_snapshot in shipper-cli) was confirmed to fail identically on origin/main without these changes; it is a localhost-mock-registry timing issue unrelated to this absorption.
  • No new fmt deltas introduced by this PR (pre-existing fmt drift in unrelated files remains; my touched files format cleanly).

CI checks to watch

  • cargo check matrix passes
  • cargo test workspace (the preflight_allow_dirty_snapshot flake may surface — pre-existing)
  • cargo clippy -- -D warnings
  • Architecture guard (ops/cargo is a layer-1 module; it imports only crate::ops::process, crate::types-equivalent external types, and external crates — no upward imports)

@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 900e7805-aea6-4a04-a7c1-c4c15187ea47

📥 Commits

Reviewing files that changed from the base of the PR and between 783f6b9 and 6c94851.

⛔ Files ignored due to path filters (11)
  • Cargo.lock is excluded by !**/*.lock
  • crates/shipper-cargo/src/snapshots/shipper_cargo__tests__snapshot_tests__snapshot_cargo_output_failure.snap is excluded by !**/*.snap
  • crates/shipper-cargo/src/snapshots/shipper_cargo__tests__snapshot_tests__snapshot_cargo_output_success.snap is excluded by !**/*.snap
  • crates/shipper-cargo/src/snapshots/shipper_cargo__tests__snapshot_tests__snapshot_cargo_output_timeout.snap is excluded by !**/*.snap
  • crates/shipper-cargo/src/snapshots/shipper_cargo__tests__snapshot_tests__snapshot_cargo_output_with_multiline_stderr.snap is excluded by !**/*.snap
  • crates/shipper-cargo/src/snapshots/shipper_cargo__tests__snapshot_tests__snapshot_cargo_output_zero_duration.snap is excluded by !**/*.snap
  • crates/shipper/src/ops/cargo/snapshots/shipper__ops__cargo__tests__snapshot_tests_absorbed__snapshot_invalid_package_names.snap is excluded by !**/*.snap
  • crates/shipper/src/ops/cargo/snapshots/shipper__ops__cargo__tests__snapshot_tests_absorbed__snapshot_package_info_prerelease_version.snap is excluded by !**/*.snap
  • crates/shipper/src/ops/cargo/snapshots/shipper__ops__cargo__tests__snapshot_tests_absorbed__snapshot_package_info_simple.snap is excluded by !**/*.snap
  • crates/shipper/src/ops/cargo/snapshots/shipper__ops__cargo__tests__snapshot_tests_absorbed__snapshot_package_info_with_registries.snap is excluded by !**/*.snap
  • crates/shipper/src/ops/cargo/snapshots/shipper__ops__cargo__tests__snapshot_tests_absorbed__snapshot_valid_package_names.snap is excluded by !**/*.snap
📒 Files selected for processing (17)
  • Cargo.toml
  • crates/shipper-cargo/CLAUDE.md
  • crates/shipper-cargo/Cargo.toml
  • crates/shipper-cargo/README.md
  • crates/shipper-cargo/src/lib.rs
  • crates/shipper-cargo/tests/cargo_commands.rs
  • crates/shipper-cargo/tests/redaction_contract.rs
  • crates/shipper-cli/Cargo.toml
  • crates/shipper/Cargo.toml
  • crates/shipper/src/cargo_micro.rs
  • crates/shipper/src/engine/parallel/CLAUDE.md
  • crates/shipper/src/engine/parallel/publish.rs
  • crates/shipper/src/lib.rs
  • crates/shipper/src/ops/cargo/CLAUDE.md
  • crates/shipper/src/ops/cargo/mod.rs
  • crates/shipper/src/ops/mod.rs
  • crates/shipper/src/plan/mod.rs

Summary by CodeRabbit

  • Chores
    • Consolidated internal cargo operation modules into the main workspace structure.
    • Removed micro-cargo feature flag; cargo functionality now available by default.

Walkthrough

The pull request removes the shipper-cargo crate from the workspace and relocates its entire public API and implementation into a new crates/shipper/src/ops/cargo/ module within the main shipper crate. All workspace members are updated to reference the relocated functionality via the new internal module path, and feature flags are adjusted accordingly.

Changes

Cohort / File(s) Summary
Workspace Configuration
Cargo.toml, crates/shipper-cli/Cargo.toml, crates/shipper/Cargo.toml
Removed shipper-cargo from workspace members; removed micro-cargo feature from shipper-cli and shipper; updated micro-all feature to exclude micro-cargo dependency.
shipper-cargo Crate Removal
crates/shipper-cargo/CLAUDE.md, crates/shipper-cargo/Cargo.toml, crates/shipper-cargo/README.md, crates/shipper-cargo/src/lib.rs, crates/shipper-cargo/tests/cargo_commands.rs, crates/shipper-cargo/tests/redaction_contract.rs
Deleted entire standalone crate including manifest, documentation, implementation (~1450 lines), and test suites (~400 lines).
shipper Crate Reorganization
crates/shipper/src/lib.rs, crates/shipper/src/ops/mod.rs, crates/shipper/src/ops/cargo/mod.rs, crates/shipper/src/ops/cargo/CLAUDE.md
Absorbed cargo operations into ops submodule; converted feature-gated pub mod cargo into unconditional re-export from crate::ops::cargo; added comprehensive metadata utilities, serializable PackageInfo, and validation helpers (~800 lines added).
Import Path Updates
crates/shipper/src/engine/parallel/CLAUDE.md, crates/shipper/src/engine/parallel/publish.rs, crates/shipper/src/plan/mod.rs
Updated internal import paths from external shipper_cargo crate to local crate::ops::cargo module.
Re-export Module
crates/shipper/src/cargo_micro.rs
Removed public re-exports of CargoOutput, cargo_publish variants, and redact_sensitive (functionality now accessible via consolidated crate::ops::cargo).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Poem

🐰 Boxes unpacked, cargo consolidated,
From separate crate to ops-module folded,
No feature flags to gate the way,
Just cleaner paths for code to play,
A warren reorganized, more streamlined today! 📦

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/decrating-absorb-cargo

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c02070671

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 33 to 37
default = ["micro-all"]
micro-encrypt = ["shipper/micro-encrypt"]
micro-cargo = ["shipper/micro-cargo"]
micro-webhook = ["shipper/micro-webhook"]
micro-types = ["shipper/micro-types"]
micro-config = ["shipper/micro-config"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore micro-cargo feature for BDD matrix compatibility

Removing the micro-cargo entry from shipper-cli features makes the existing compatibility matrix fail immediately: CI still runs cargo test -p shipper-cli --test bdd_publish --features micro-cargo in .github/workflows/ci.yml (lines 217 and 233–238) and the CircleCI template does the same (templates/circleci-config.yml:51-56), but this commit now errors with the package 'shipper-cli' does not contain this feature: micro-cargo. This turns one matrix leg into a guaranteed failure until the alias is kept (possibly deprecated/no-op) or the matrix is updated in the same change.

Useful? React with 👍 / 👎.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request absorbs the shipper-cargo microcrate into the main shipper crate as crate::ops::cargo, consolidating workspace metadata and publishing logic. Feedback focuses on improving the migrated code by ensuring is_publishable correctly filters for workspace members, eliminating logic duplication in metadata loading, and optimizing performance in dependency graph construction and package name validation.

Comment on lines +231 to +243
pub fn is_publishable(&self, package: &Package) -> bool {
if let Some(publish) = &package.publish
&& publish.is_empty()
{
return false;
}

if package.version.to_string() == "0.0.0" {
return false;
}

true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

is_publishable should verify that the package is a member of the workspace. Without this check, publishable_packages() and topological_order() will include all public dependencies (like serde or anyhow) found in the metadata, which is incorrect for workspace publishing operations.

    pub fn is_publishable(&self, package: &Package) -> bool {
        if !self.metadata.workspace_members.contains(&package.id) {
            return false;
        }

        if let Some(publish) = &package.publish
            && publish.is_empty()
        {
            return false;
        }

        if package.version.to_string() == "0.0.0" {
            return false;
        }

        true
    }

Comment on lines +189 to +192
let metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.context("failed to load cargo metadata")?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic duplicates the load_metadata function defined at line 170. It's better to call load_metadata(manifest_path) directly to ensure consistent behavior and error handling.

Suggested change
let metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.context("failed to load cargo metadata")?;
let metadata = load_metadata(manifest_path)?;

Comment on lines +338 to +341
.packages
.iter()
.find(|p| p.name == dep.name)
.map(|p| p.name.to_string())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Performing a linear search for each dependency in self.metadata.packages results in poor performance ($O(N^2)$ or worse) as the number of packages and dependencies grows. Consider building a lookup map (e.g., HashMap<String, &Package>) once at the start of the function.

Comment on lines +412 to +421
let chars: Vec<char> = name.chars().collect();

if chars[0].is_ascii_digit() || chars[0] == '-' {
return false;
}

chars
.iter()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || *c == '-' || *c == '_')
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Collecting all characters into a Vec<char> is an unnecessary allocation. You can check the first character and validate the rest of the string using iterators directly on the &str.

Suggested change
let chars: Vec<char> = name.chars().collect();
if chars[0].is_ascii_digit() || chars[0] == '-' {
return false;
}
chars
.iter()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || *c == '-' || *c == '_')
}
let mut chars = name.chars();
match chars.next() {
None => return false,
Some(c) if c.is_ascii_digit() || c == '-' => return false,
_ => {}
}
name.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-' || c == '_')

@EffortlessSteven EffortlessSteven force-pushed the feature/decrating-absorb-cargo branch from 2c02070 to a6b052a Compare April 15, 2026 08:45
The former `shipper-cargo` microcrate is pulled into the core `shipper`
crate as `crate::ops::cargo`, following the Phase-2 decrating plan
(`docs/decrating-plan.md` §3.2). The historical public path
`shipper::cargo` is preserved via `pub use crate::ops::cargo` for
backward compatibility with `shipper-cli` and external consumers.

Scope of changes:

- New module `crates/shipper/src/ops/cargo/` containing merged
  functionality of the absorbed crate PLUS the pre-existing
  `crates/shipper/src/cargo.rs` (the `micro-cargo`-off branch's local
  implementation). The merged module exposes `CargoOutput`,
  `cargo_publish` / dry-run variants, `load_metadata`,
  `WorkspaceMetadata`, `PackageInfo`, `is_valid_package_name`,
  `workspace_member_names`, `get_version`, `get_package_name`, plus a
  `pub use shipper_output_sanitizer::redact_sensitive` re-export. All
  tests (unit, proptest, snapshot) from both the absorbed crate and the
  in-tree duplicate are ported; snapshot filenames rewritten to match
  the new module path.
- `shipper::cargo` is now `pub use crate::ops::cargo;` — no more
  `#[cfg(feature = "micro-cargo")]` dispatch. The `micro-cargo` feature
  is removed from `crates/shipper/Cargo.toml` and
  `crates/shipper-cli/Cargo.toml`.
- `crates/shipper/src/engine/parallel/publish.rs` swaps
  `use shipper_cargo as cargo;` for `use crate::ops::cargo;`.
- `crates/shipper/src/plan/mod.rs::load_metadata` now delegates to
  `crate::ops::cargo::load_metadata`.
- `crates/shipper-cargo/` physically deleted; dropped from workspace
  members in the root `Cargo.toml` and from `[dependencies]` in
  `crates/shipper/Cargo.toml`.
- Engine-parallel CLAUDE.md updated to reflect the new internal import.
- New `crates/shipper/src/ops/cargo/CLAUDE.md` describing invariants and
  gotchas of the absorbed module, matching the convention used by
  `ops/auth`, `ops/lock`, `ops/process`.

Pre-flight (per R-PR-4): the only consumer of `shipper-cargo` on main
was `crates/shipper/Cargo.toml` itself; `fuzz/` only references the
distinct `shipper-cargo-failure` crate. After this change the
`shipper_cargo::` namespace is gone from the workspace entirely.

Verified locally:

- `cargo check --workspace --all-targets` — clean
- `cargo check --workspace --all-targets --all-features` — clean
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
  — clean
- `cargo test --workspace` — 2000+ tests pass. One pre-existing
  `preflight_allow_dirty_snapshot` flake in `shipper-cli` was confirmed
  to fail identically on `origin/main` without these changes, so it is
  not caused by the absorption.

Part of the decrating effort — unblocks the next absorption round now
that `shipper-cargo` is off the dependency list of every crate other
than `shipper`.
@EffortlessSteven EffortlessSteven force-pushed the feature/decrating-absorb-cargo branch from a6b052a to 6c94851 Compare April 15, 2026 08:48
@EffortlessSteven EffortlessSteven merged commit ce52098 into main Apr 15, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant