Skip to content

feat(packaging): shrink shipper to install façade (#95 PR 3)#142

Merged
EffortlessSteven merged 2 commits into
mainfrom
feat/95-pr3-shipper-facade
Apr 18, 2026
Merged

feat(packaging): shrink shipper to install façade (#95 PR 3)#142
EffortlessSteven merged 2 commits into
mainfrom
feat/95-pr3-shipper-facade

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Third step of the #95 three-crate split. After PRs 1-2 (#140, #141) the code was in the right crates, but crates/shipper/src/lib.rs was still blanket-re-exporting every shipper-core module, and the README/CLAUDE.md still framed shipper as the core library. This PR makes the façade framing real.

Architecture (finalized)

shipper (this crate — install handle + curated lib re-export)
  -> shipper-cli (real CLI adapter; pub fn run())
       -> shipper-core (engine — stable embedding surface)

Curated library re-export

crates/shipper/src/lib.rs now re-exports only the six modules a programmatic driver would reach for as entry points:

engine, plan, types, config, state, store

Engine internals — auth, cargo, cargo_failure, encryption, git, lock, registry, retry, runtime, webhook — are not in the façade. Embedders who need them depend on shipper-core directly. That's the point of the split: the shipper install crate shouldn't double as "the programmatic API surface for every engine internal."

Tests migrated

Several crates/shipper/tests/*.rs files reached into now-uncurated modules (shipper::auth, shipper::lock, shipper::registry, shipper::runtime). Their imports now read from shipper_core::X instead. shipper-core added as a dev-dep of shipper.

PR 4 will decide whether these engine-internal integration tests belong in crates/shipper-core/tests/ instead.

README

Rewritten as the product landing page:

  • Opens with cargo install shipper --locked
  • Names the five things Shipper does that raw cargo publish can't (resumable, backoff-aware, events-as-truth, prove, contain)
  • Three-crate architecture diagram so users see where shipper-core and shipper-cli fit
  • Quick-start preflight → publish → resume flow
  • Scope section separates publishing from versioning/tags/GitHub releases

CLAUDE.md

Updated to describe the façade role: behavior changes belong in shipper-core or shipper-cli; this crate's surface should move rarely; CLI dependencies stay out.

Verification

  • cargo check --workspace --all-targets — clean
  • cargo clippy --workspace --all-targets --all-features -- -D warnings — clean
  • cargo test --workspace --doc — clean
  • cargo test -p shipper — 39 passed

What this PR does not do

  • Does not physically move crates/shipper/tests/ into crates/shipper-core/tests/ (PR 4 decision)
  • Does not update workspace docs (docs/how-to/*, docs/explanation/*) that may reference shipper::X paths for no-longer-re-exported modules (PR 4 sweep)
  • Does not touch architecture guard or CI topology

Test plan

  • cargo check --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace --doc
  • cargo test -p shipper
  • CI green on all matrices
  • cargo install shipper --locked still produces a working binary

Part of #95. PR 4 (docs/examples/workspace import sweep) to follow.

Third step of the #95 three-crate split. After PRs 1-2, `shipper`'s
lib.rs was still blanket-re-exporting every `shipper-core` module; the
README and CLAUDE.md still framed `shipper` as "the core library."
This PR makes the façade framing real.

## Architecture (finalized after this PR)

    shipper (this crate — install handle + curated lib re-export)
      -> shipper-cli (real CLI adapter; pub fn run())
           -> shipper-core (engine — stable embedding surface)

## Curated library re-export

`crates/shipper/src/lib.rs` now re-exports only the six modules a
programmatic driver would reach for as entry points:

    engine, plan, types, config, state, store

Engine internals that used to be re-exported — `auth`, `cargo`,
`cargo_failure`, `encryption`, `git`, `lock`, `registry`, `retry`,
`runtime`, `webhook` — are **not** in the façade. Embedders who need
them should depend on `shipper-core` directly. That's the point of the
split: the `shipper` install crate shouldn't be both "the easy install
path" and "the programmatic API surface for every internal."

## Tests migrated

Four `crates/shipper/tests/*.rs` files reached into now-uncurated
modules (`shipper::auth`, `shipper::lock`, `shipper::registry`). Their
imports now read from `shipper_core::X` instead:

- `cross_crate_integration.rs`
- `facade_integration.rs`
- `pipeline_integration.rs`
- `execution_bdd.rs` (was already using `shipper::runtime`; now
  `shipper_core::runtime`)
- plus incidental sed matches in other tests that happened to
  reference the same paths

`shipper-core` is added as a dev-dep of `shipper` so the integration
tests can import it directly. PR 4 will decide whether these tests
belong in `crates/shipper-core/tests/` instead.

## README

`crates/shipper/README.md` rewritten as the product landing page:

- Opens with `cargo install shipper --locked`.
- Names the five things Shipper does that raw `cargo publish` can't
  (resumable, backoff-aware, events-as-truth, prove, contain).
- Shows the three-crate architecture diagram so users see where
  `shipper-core` and `shipper-cli` fit.
- Quick-start section with preflight / publish / resume flow.
- Scope section separates publishing concerns from versioning / tags /
  GitHub releases.

The old README framed this crate as a library; it now frames it as
the install handle and points embedders at `shipper-core`.

## CLAUDE.md

`crates/shipper/CLAUDE.md` updated to describe the façade role:
behavior changes belong in `shipper-core` or `shipper-cli`, not here;
this crate's surface should move rarely; CLI dependencies stay out.

## Verification

- `cargo check --workspace --all-targets` — clean
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- `cargo test --workspace --doc` — clean
- `cargo test -p shipper` — 39 passed

## What this PR does not do

- Does not physically move `crates/shipper/tests/` into
  `crates/shipper-core/tests/` (PR 4 decision).
- Does not update workspace docs (`docs/how-to/*`, `docs/explanation/*`)
  that may still reference `shipper::X` paths for things that are no
  longer re-exported (PR 4 sweep).
- Does not touch the architecture guard or CI topology.
@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 7 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 7 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ee7d4604-6f42-4c2f-a71a-bef48d2bddf1

📥 Commits

Reviewing files that changed from the base of the PR and between 8f2f9ae and c087d6f.

📒 Files selected for processing (13)
  • crates/shipper/CLAUDE.md
  • crates/shipper/Cargo.toml
  • crates/shipper/README.md
  • crates/shipper/src/lib.rs
  • crates/shipper/tests/cross_crate_integration.rs
  • crates/shipper/tests/execution_bdd.rs
  • crates/shipper/tests/facade_integration.rs
  • crates/shipper/tests/pipeline_integration.rs
  • fuzz/Cargo.toml
  • fuzz/fuzz_targets/auth_token_resolve.rs
  • fuzz/fuzz_targets/encrypt_decrypt.rs
  • fuzz/fuzz_targets/execution_core.rs
  • fuzz/fuzz_targets/resolve_token.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/95-pr3-shipper-facade

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.

@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 refactors the shipper crate to serve as a stable installation target and curated re-export layer, delegating core engine logic to shipper-core. Documentation across CLAUDE.md, README.md, and lib.rs has been updated to reflect this architectural shift, and integration tests have been adjusted to import internal modules directly from shipper-core. Feedback identifies a redundant dev-dependency in Cargo.toml and suggests removing unnecessary mut variable bindings in several test files to improve code cleanliness.

Comment thread crates/shipper/Cargo.toml
# directly into `shipper-core` (for engine internals like `runtime::`
# that aren't part of the curated façade) and `shipper-types` (for
# constructing fixtures).
shipper-core.workspace = 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.

medium

The shipper-core dependency is already listed in the [dependencies] section (line 30). In Cargo, regular dependencies are automatically available to integration tests, making this entry in [dev-dependencies] redundant.


// Acquire lock
let mut lock = shipper::lock::LockFile::acquire(state_dir, None).expect("acquire lock");
let mut lock = shipper_core::lock::LockFile::acquire(state_dir, None).expect("acquire lock");

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

The mut keyword for the lock variable appears to be unnecessary as it is not mutated in a way that requires it. Removing mut would improve code cleanliness and allow for the removal of the #[allow(unused_mut)] attribute (which is present on line 1512 but not shown in this diff hunk).

Suggested change
let mut lock = shipper_core::lock::LockFile::acquire(state_dir, None).expect("acquire lock");
let lock = shipper_core::lock::LockFile::acquire(state_dir, None).expect("acquire lock");

Comment on lines +400 to +401
let mut lock1 =
shipper_core::lock::LockFile::acquire(&state_dir, None).expect("acquire lock 1");

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

The mut keyword for lock1 is unnecessary as it is not mutated before being released. Removing it improves code clarity and avoids the need for unused_mut lint suppressions.

Suggested change
let mut lock1 =
shipper_core::lock::LockFile::acquire(&state_dir, None).expect("acquire lock 1");
let lock1 =
shipper_core::lock::LockFile::acquire(&state_dir, None).expect("acquire lock 1");

Comment on lines +458 to +463
let mut lock = shipper_core::lock::LockFile::acquire_with_timeout(
&state_dir,
None,
Duration::from_secs(60),
)
.expect("acquire with timeout");

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

The mut keyword for lock is unnecessary here as well. Removing it maintains consistency and follows Rust best practices for minimizing mutable bindings.

Suggested change
let mut lock = shipper_core::lock::LockFile::acquire_with_timeout(
&state_dir,
None,
Duration::from_secs(60),
)
.expect("acquire with timeout");
let lock = shipper_core::lock::LockFile::acquire_with_timeout(
&state_dir,
None,
Duration::from_secs(60),
)
.expect("acquire with timeout");

@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: d76e2e2dfb

ℹ️ 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 thread crates/shipper/src/lib.rs
auth, cargo, cargo_failure, config, encryption, engine, git, lock, plan, registry, retry,
runtime, state, store, types, webhook,
};
pub use shipper_core::{config, engine, plan, state, store, types};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep parallel publish callable from the shipper facade

Dropping registry from the facade re-exports while still re-exporting engine makes shipper::engine::parallel::run_publish_parallel effectively unusable for shipper-only consumers, because that public function requires RegistryClient (shipper-core defines it as crate::registry::RegistryClient) and the corresponding shipper::registry::... path no longer exists. This is a compile-time regression for existing facade callers of the parallel entry point unless registry stays re-exported or the parallel API is no longer exposed through shipper::engine.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Apr 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

After #95 PR 3 curated the `shipper` library re-exports, four fuzz
targets broke on CI's Fuzz Smoke (PR) lane:

- `resolve_token` + `auth_token_resolve` — imported `shipper::auth`
- `encrypt_decrypt` — imported `shipper::encryption`
- `execution_core` — imported `shipper::runtime`

None of these modules are re-exported from the `shipper` façade
anymore — they're engine internals. Updated each target to import
from `shipper_core::X` directly, and added `shipper-core` as a
dependency of the `shipper-fuzz` crate.
@EffortlessSteven EffortlessSteven merged commit 65782d5 into main Apr 18, 2026
21 checks passed
@EffortlessSteven EffortlessSteven deleted the feat/95-pr3-shipper-facade branch April 18, 2026 14:26
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