Skip to content

feat(packaging): unify shipper CLI into the shipper crate (#95)#135

Merged
EffortlessSteven merged 2 commits into
mainfrom
feat/95-package-unification
Apr 18, 2026
Merged

feat(packaging): unify shipper CLI into the shipper crate (#95)#135
EffortlessSteven merged 2 commits into
mainfrom
feat/95-package-unification

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

The reframed #95 landing. Supersedes the parked three-crate facade
experiment on #123.

`shipper` becomes a single public crate carrying both its library API
and the `shipper` binary. `shipper-cli` stays published as a
compatibility shim. `cargo install shipper --locked` is now the paved
road.

Shape

  • `shipper` crate: `[lib]` (unchanged public API) + `[[bin]] name = "shipper"`.
    CLI lives in `shipper::cli` (module, pub). Binary target at
    `src/bin/shipper.rs` is a three-line shim calling `shipper::cli::run()`.
  • `shipper-cli` crate: compatibility shim. `[[bin]] name = "shipper-cli"`
    (renamed to avoid workspace collision), `src/main.rs` is three lines,
    forwards to `shipper::cli::run()`. Deprecation window — operators
    still referencing it get a working CLI during migration.

Why not `shipper-core`

Captured the reasoning on #95 + #123 earlier. Short version:

  • One public product story.
  • No dep cycles (facade → CLI → library trap avoided).
  • No hidden core — publishing `shipper-core` would force consumers to
    pick between `shipper` and `shipper-core` for no gain.
  • Much smaller churn than the ~570-file three-crate attempt.

Mechanical churn

  • `git mv crates/shipper-cli/src/{main.rs,output}` →
    `crates/shipper/src/cli/{mod.rs,output}` (history preserved).
  • `fn main` → `pub fn run`. `use shipper::...` → `use crate::...`.
  • `shipper/Cargo.toml` gains CLI deps (clap, clap_complete, indicatif)
    and the `[[bin]]` target.
  • `shipper-cli/Cargo.toml` loses CLI deps; gains a renamed bin.
  • `shipper-cli/tests/` stay in place (25 files, comprehensive coverage).
    Every `cargo_bin!("shipper")` → `cargo_bin!("shipper-cli")` so tests
    exercise the shim (which internally dispatches to the same
    `shipper::cli::run`).
  • Help-text snapshots regenerated. Progress-reporter snapshots moved
    to the new module path under `shipper/src/cli/`.
  • Workspace `default-members` reordered (`shipper` first).
  • Release workflow builds `-p shipper` (facade) instead of `-p shipper-cli`.
  • Docs (README, release-runbook, how-to guides, tutorials, failure-modes,
    preflight, tech) switch to `cargo install shipper`.

Test plan

  • `cargo build --workspace` — clean.
  • `cargo clippy --workspace --all-targets --all-features -- -D warnings` — no issues.
  • `cargo fmt --all --check` — clean.
  • `cargo doc -p shipper --no-deps` — clean.
  • `target/debug/shipper --version` → `shipper 0.3.0-rc.1`.
  • `target/debug/shipper-cli --version` → `shipper 0.3.0-rc.1` (shim works).
  • Full `cargo test -p shipper-cli` green (pre-existing `preflight_command_*`
    flakiness under -j N remains, passes -j 1 — not caused by this PR).
  • `cargo test -p shipper --lib cli::output` — 90 pass (rebaseline).
  • CI multi-OS green.

Sequencing

Per the agreed plan: trust-core → Prove → Remediate → #95 (this)#96.
#96 (Trusted Publishing, draft at #122) follows once this lands and
can be rebased onto the final packaging layer.

Reframed packaging UX. Supersedes the parked three-crate facade (#123).

## What changes

- **\`shipper\`** becomes a single public crate with both \`[lib]\` and
  \`[[bin]]\`. The CLI lives at \`shipper::cli\` (module, pub) and the
  binary target at \`src/bin/shipper.rs\` is a three-line shim over
  \`shipper::cli::run()\`.
- **\`shipper-cli\`** stays published as a compatibility shim:
  \`[[bin]] name = \"shipper-cli\"\` that forwards into
  \`shipper::cli::run()\`. Operators on the old name keep working;
  new setups should \`cargo install shipper --locked\`.
- README, docs, release workflow updated to prefer
  \`cargo install shipper\` everywhere.

## Why this shape

- **One public product story.** Library users \`shipper = \"0.3\"\`,
  operators \`cargo install shipper\`. One crate to learn, document,
  cite.
- **No dep cycles.** The earlier three-crate \`shipper-core\` + \`shipper\`
  + \`shipper-cli\` facade kept tripping cycle risk because the facade
  depended on the CLI which depended on the library. A single crate
  with a \`cli\` submodule sidesteps this.
- **No hidden core.** \`shipper-core\` as a publicly meaningful crate
  would have forced consumers to choose between \`shipper\` and
  \`shipper-core\` for no gain.
- **Smaller churn than the three-crate attempt.** One directory move
  (\`shipper-cli/src/\` → \`shipper/src/cli/\`) + bin rename, vs the
  ~570-file shuffle the earlier attempt produced.

## Mechanical changes

- \`shipper-cli/src/main.rs\` + \`shipper-cli/src/output/\` moved under
  \`shipper/src/cli/\` (git mv preserves history). \`fn main\` became
  \`pub fn run\`. All \`use shipper::...\` rewrote to \`use crate::...\`.
- \`shipper/Cargo.toml\` gains \`[[bin]] name = \"shipper\"\` + the CLI's
  deps (clap, clap_complete, indicatif).
- \`shipper-cli/Cargo.toml\` reduces to a shim: \`[[bin]] name = \"shipper-cli\"\`,
  \`src/main.rs\` is three lines.
- \`shipper-cli/tests/\` preserved (25 files, extensive coverage). Every
  \`cargo_bin!(\"shipper\")\` → \`cargo_bin!(\"shipper-cli\")\`. Tests now
  exercise the shim, which internally calls the same \`shipper::cli::run\`
  — so functional coverage is identical.
- Help-text snapshots regenerated: \`Usage: shipper-cli\` when invoked
  via the shim (correct behavior — it IS shipper-cli when you run it
  that way). \`cli::output::progress\` snapshots moved to their new
  module path under \`shipper/src/cli/\`.
- Workspace \`default-members\` reordered (\`shipper\` first).
- Release workflow builds \`-p shipper\` instead of \`-p shipper-cli\`,
  installs \`target/release/shipper\` to PATH (facade bin name).
- Docs (README, release-runbook, run-in-github-actions, tutorials,
  failure-modes, preflight, tech) all switch to
  \`cargo install shipper --locked\`.

## Acceptance

- [x] \`cargo install --path crates/shipper --locked\` installs a working
      \`shipper\` binary.
- [x] \`cargo install --path crates/shipper-cli --locked\` still works
      (produces a \`shipper-cli\` binary via the shim).
- [x] \`shipper --version\` and \`shipper-cli --version\` both print the
      version.
- [x] Library imports (\`use shipper::engine;\` etc.) unchanged.
- [x] \`cargo clippy --workspace --all-targets --all-features -- -D warnings\` clean.
- [x] \`cargo doc -p shipper --no-deps\` clean.
- [x] \`cargo fmt --all --check\` clean.
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@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 28 minutes and 52 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 28 minutes and 52 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: 0df506c4-a8db-4e24-9105-6d3d8e6c9e74

📥 Commits

Reviewing files that changed from the base of the PR and between a42b025 and dd5922d.

⛔ Files ignored due to path filters (82)
  • Cargo.lock is excluded by !**/*.lock
  • crates/shipper-cli/tests/snapshots/cli_snapshots__ci_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__clean_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__completion_missing_shell.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__config_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__doctor_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__help_text.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__no_subcommand_error.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__plan_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__preflight_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__publish_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__resume_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__status_help.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/cli_snapshots__unknown_subcommand_error.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__error_missing_ci_subcommand.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__error_missing_completion_shell.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__error_missing_config_subcommand.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__error_unknown_subcommand.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_ci.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_ci_github_actions.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_ci_gitlab.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_clean.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_completion.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_config.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_config_init.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_config_validate.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_doctor.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_fix_forward.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_inspect_events.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_inspect_receipt.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_plan.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_plan_yank.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_preflight.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_publish.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_resume.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_root.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_status.snap is excluded by !**/*.snap
  • crates/shipper-cli/tests/snapshots/e2e_expanded__help_yank.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__display_empty_name_and_version.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__display_multi_package_sequence.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__display_prerelease_version.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__display_single_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__percentage_large_workspace.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__percentage_milestones.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__percentage_single_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__percentage_three_packages.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__percentage_zero_total.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__state_after_first_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__state_fresh_reporter.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__state_full_lifecycle.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__state_overwrite_same_index.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__snapshot_tests__state_zero_packages.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_display_format_edge_cases.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_failed_midway_state.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_progress_at_0_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_progress_at_100_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_progress_at_25_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_progress_at_50_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_progress_at_75_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__cli__output__progress__tests__snapshot_single_package_lifecycle.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__display_empty_name_and_version.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__display_multi_package_sequence.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__display_prerelease_version.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__display_single_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__percentage_large_workspace.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__percentage_milestones.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__percentage_single_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__percentage_three_packages.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__percentage_zero_total.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__state_after_first_package.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__state_fresh_reporter.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__state_full_lifecycle.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__state_overwrite_same_index.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__snapshot_tests__state_zero_packages.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_display_format_edge_cases.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_failed_midway_state.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_progress_at_0_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_progress_at_100_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_progress_at_25_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_progress_at_50_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_progress_at_75_percent.snap is excluded by !**/*.snap
  • crates/shipper/src/cli/output/progress/snapshots/shipper__output__progress__tests__snapshot_single_package_lifecycle.snap is excluded by !**/*.snap
📒 Files selected for processing (49)
  • .github/workflows/release.yml
  • Cargo.toml
  • README.md
  • crates/shipper-cli/Cargo.toml
  • crates/shipper-cli/src/main.rs
  • crates/shipper-cli/tests/bdd_config.rs
  • crates/shipper-cli/tests/bdd_doctor.rs
  • crates/shipper-cli/tests/bdd_error_handling.rs
  • crates/shipper-cli/tests/bdd_error_recovery.rs
  • crates/shipper-cli/tests/bdd_micro_backends.rs
  • crates/shipper-cli/tests/bdd_parallel.rs
  • crates/shipper-cli/tests/bdd_preflight.rs
  • crates/shipper-cli/tests/bdd_publish.rs
  • crates/shipper-cli/tests/bdd_publish_advanced.rs
  • crates/shipper-cli/tests/bdd_publish_flow.rs
  • crates/shipper-cli/tests/bdd_resume.rs
  • crates/shipper-cli/tests/bdd_status.rs
  • crates/shipper-cli/tests/bdd_workflow.rs
  • crates/shipper-cli/tests/cli_e2e.rs
  • crates/shipper-cli/tests/cli_snapshots.rs
  • crates/shipper-cli/tests/e2e_config.rs
  • crates/shipper-cli/tests/e2e_doctor.rs
  • crates/shipper-cli/tests/e2e_expanded.rs
  • crates/shipper-cli/tests/e2e_plan.rs
  • crates/shipper-cli/tests/e2e_preflight.rs
  • crates/shipper-cli/tests/e2e_publish.rs
  • crates/shipper-cli/tests/e2e_rehearse.rs
  • crates/shipper-cli/tests/e2e_resume.rs
  • crates/shipper-cli/tests/e2e_status.rs
  • crates/shipper-cli/tests/integration_config.rs
  • crates/shipper/Cargo.toml
  • crates/shipper/src/bin/shipper.rs
  • crates/shipper/src/cli/mod.rs
  • crates/shipper/src/cli/output/CLAUDE.md
  • crates/shipper/src/cli/output/mod.rs
  • crates/shipper/src/cli/output/progress/CLAUDE.md
  • crates/shipper/src/cli/output/progress/bdd_tests.rs
  • crates/shipper/src/cli/output/progress/mod.rs
  • crates/shipper/src/cli/output/progress/proptests.rs
  • crates/shipper/src/cli/output/progress/snapshot_tests.rs
  • crates/shipper/src/cli/output/progress/tests.rs
  • crates/shipper/src/lib.rs
  • docs/failure-modes.md
  • docs/how-to/rehearse-against-an-alt-registry.md
  • docs/how-to/run-in-github-actions.md
  • docs/preflight.md
  • docs/release-runbook.md
  • docs/tech.md
  • docs/tutorials/first-publish.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/95-package-unification

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: cf73a687bd

ℹ️ 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".

# `shipper`'s own `shipper` binary.
[[bin]]
name = "shipper"
name = "shipper-cli"

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 Preserve shipper binary name in compatibility crate

Renaming the shim binary to shipper-cli breaks the advertised migration path for users who still run cargo install shipper-cli and then invoke shipper: Cargo installs executables using [[bin]].name, so those workflows now fail with shipper: command not found after upgrading this crate. If this crate is meant to be a compatibility shim, it needs to continue providing a shipper executable (or an equivalent alias) during the deprecation window.

Useful? React with 👍 / 👎.

Fixes CI failures on Ubuntu + macOS where the bin has no .exe suffix.
The normalize_output / normalize_stderr helpers were stripping
`shipper.exe` but not `shipper-cli.exe`, so my Windows-regenerated
snapshots leaked the platform-specific suffix.

Order matters in the rewrite: strip `shipper-cli.exe` → `shipper-cli`
first, else the subsequent `shipper.exe` → `shipper` rule would eat
the `-cli` suffix after trimming `.exe`.
@EffortlessSteven EffortlessSteven merged commit 39592e8 into main Apr 18, 2026
19 checks passed
@EffortlessSteven EffortlessSteven deleted the feat/95-package-unification branch April 18, 2026 07:13
@codecov

codecov Bot commented Apr 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.66559% with 254 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/shipper/src/cli/mod.rs 83.80% 251 Missing ⚠️
crates/shipper/src/bin/shipper.rs 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

EffortlessSteven added a commit that referenced this pull request Apr 18, 2026
Reworking #95 into the three-crate split the user specified originally:

    shipper (installable facade)
      -> shipper-cli (real CLI adapter)           [PR 2]
           -> shipper-core (pure library/engine)  [THIS PR]

This PR is the bottom layer only. It moves every non-CLI module out of
`crates/shipper/src/` into a new `crates/shipper-core/` crate and
leaves `shipper` as a thin re-export facade so the rest of the
workspace (shipper-cli, integration tests, downstream code) keeps
compiling unchanged.

## Why this reopens #95

#135 merged a different shape: `shipper` carried the lib + bin, with
`shipper-cli` as a 9-line compat shim. That conflated engine code and
CLI code in one crate, left `shipper-cli` as a shim with no value, and
didn't match the design goal of a no-clap library surface. The
three-crate split below is what was originally asked for.

## What moved

All moved via `git mv` so history is preserved:

- `crates/shipper/src/{engine,state,ops,plan,runtime}/` → shipper-core
- `crates/shipper/src/{config,encryption,git,types,webhook,
  property_tests,stress_tests}.rs` → shipper-core
- `crates/shipper/src/lib.rs` → shipper-core (then edited to drop the
  `pub mod cli;` declaration and retarget docs at `shipper_core`)
- proptest-regressions fixtures moved with their owning tests

What stayed in `crates/shipper/`:

- `src/cli/` (moves to `shipper-cli` in PR 2)
- `src/bin/shipper.rs` (stays — this crate owns the installable binary)

## Facade

The new `crates/shipper/src/lib.rs` re-exports shipper-core's public
surface (auth, cargo, cargo_failure, config, encryption, engine, git,
lock, plan, registry, retry, runtime, state, store, types, webhook) so
every existing `shipper::X` import path still resolves. PR 3 slims
this to a curated re-export list once `shipper-cli` takes over the CLI.

## Snapshots

326 insta snapshots renamed `shipper__*.snap` → `shipper_core__*.snap`
to match the new crate prefix in `module_path!()`. Tests pass with
the renamed files; no content changes.

## CI

Architecture-guard workflow updated to watch
`crates/shipper-core/src/**` (where the layer dirs now live) in
addition to `crates/shipper/src/**`, so the upward-import check keeps
enforcing instead of silently skipping.

## Verification

- `cargo check --workspace --all-targets` — clean
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- `cargo test -p shipper-core --lib` — 1889 passed
- `cargo test -p shipper` — 39 passed
- Full workspace test run — all passed except pre-existing Windows-
  specific flakes (`concurrent_version_exists_checks` on macOS,
  `preflight_command_*` temp-dir locking on Windows); both pass in
  isolation and are unrelated to this split.

## What this PR does not do

- Does not move the CLI (that's PR 2)
- Does not slim `crates/shipper`'s Cargo.toml or README (PR 3)
- Does not touch workspace-wide docs or how-to guides (PR 4)
EffortlessSteven added a commit that referenced this pull request Apr 18, 2026
* feat(packaging): create shipper-core library crate (#95 PR 1)

Reworking #95 into the three-crate split the user specified originally:

    shipper (installable facade)
      -> shipper-cli (real CLI adapter)           [PR 2]
           -> shipper-core (pure library/engine)  [THIS PR]

This PR is the bottom layer only. It moves every non-CLI module out of
`crates/shipper/src/` into a new `crates/shipper-core/` crate and
leaves `shipper` as a thin re-export facade so the rest of the
workspace (shipper-cli, integration tests, downstream code) keeps
compiling unchanged.

## Why this reopens #95

#135 merged a different shape: `shipper` carried the lib + bin, with
`shipper-cli` as a 9-line compat shim. That conflated engine code and
CLI code in one crate, left `shipper-cli` as a shim with no value, and
didn't match the design goal of a no-clap library surface. The
three-crate split below is what was originally asked for.

## What moved

All moved via `git mv` so history is preserved:

- `crates/shipper/src/{engine,state,ops,plan,runtime}/` → shipper-core
- `crates/shipper/src/{config,encryption,git,types,webhook,
  property_tests,stress_tests}.rs` → shipper-core
- `crates/shipper/src/lib.rs` → shipper-core (then edited to drop the
  `pub mod cli;` declaration and retarget docs at `shipper_core`)
- proptest-regressions fixtures moved with their owning tests

What stayed in `crates/shipper/`:

- `src/cli/` (moves to `shipper-cli` in PR 2)
- `src/bin/shipper.rs` (stays — this crate owns the installable binary)

## Facade

The new `crates/shipper/src/lib.rs` re-exports shipper-core's public
surface (auth, cargo, cargo_failure, config, encryption, engine, git,
lock, plan, registry, retry, runtime, state, store, types, webhook) so
every existing `shipper::X` import path still resolves. PR 3 slims
this to a curated re-export list once `shipper-cli` takes over the CLI.

## Snapshots

326 insta snapshots renamed `shipper__*.snap` → `shipper_core__*.snap`
to match the new crate prefix in `module_path!()`. Tests pass with
the renamed files; no content changes.

## CI

Architecture-guard workflow updated to watch
`crates/shipper-core/src/**` (where the layer dirs now live) in
addition to `crates/shipper/src/**`, so the upward-import check keeps
enforcing instead of silently skipping.

## Verification

- `cargo check --workspace --all-targets` — clean
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean
- `cargo test -p shipper-core --lib` — 1889 passed
- `cargo test -p shipper` — 39 passed
- Full workspace test run — all passed except pre-existing Windows-
  specific flakes (`concurrent_version_exists_checks` on macOS,
  `preflight_command_*` temp-dir locking on Windows); both pass in
  isolation and are unrelated to this split.

## What this PR does not do

- Does not move the CLI (that's PR 2)
- Does not slim `crates/shipper`'s Cargo.toml or README (PR 3)
- Does not touch workspace-wide docs or how-to guides (PR 4)

* fix(shipper-core): update lock doctest imports to shipper_core

Three doctests in `ops/lock/mod.rs` still used `use shipper::lock::LockFile;`
after the #95 PR 1 split. Doctests compile as external code against the
crate being tested, so the crate name now has to be `shipper_core`. Caught
by the `cargo test --workspace --doc` lane in CI (not covered by
`cargo test -p shipper-core --lib` locally).
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