Release v0.3.0-rc.1#23
Conversation
- Introduced `shipper-output-sanitizer` crate for sanitizing cargo command output. - Implemented sensitive data redaction in logs, including authorization tokens and cargo registry tokens. - Added tests for redaction functionality and integration tests for various credential shapes. - Created `shipper-progress` crate for CLI progress reporting utilities. - Implemented a `ProgressReporter` for tracking package publishing progress with TTY support. - Added integration tests for retry strategies in the `shipper-retry` crate. - Enhanced policy application logic in the `shipper` crate to respect runtime flags. - Added fuzzing targets for configuration parsing, runtime options, and output sanitization. - Created integration tests for chunking functionality in the `shipper-engine-parallel` crate.
…d integrate into existing modules
- Deleted `types_micro.rs` and `webhook_micro.rs` files as they are no longer needed. - Updated `webhook.rs` to streamline webhook configuration and event handling. - Replaced custom webhook configuration with `shipper-webhook` microcrate's configuration. - Simplified event handling and payload structure for webhook notifications. - Improved error handling and logging for webhook delivery failures. - Added `shipper-retry` dependency to `fuzz/Cargo.toml` for enhanced retry logic. - Cleaned up tests related to webhook functionality and ensured they align with new structure.
…atures - Fix flaky \ eadiness_modes\ test by extending mock server timeout to prevent premature socket closure before \shipper\ initialization. - Implement granular workspace locking using path hashing in \shipper-lock\ and \shipper/lock.rs\ to fix the global \~/.shipper/lock\ collision bug, enabling parallel distinct workspace publishing. - Introduce robust atomic file operations for lock acquisition to close race conditions between concurrent \shipper\ instances. - Optimize \ eadiness.md\ checks by adding ETag-based disk caching for Cargo sparse index fragments, reducing bandwidth and registry throttling. - Expand \shipper-cargo-failure\ classification to reliably detect edge-case network errors (500s, DNS, timeouts) as retryable. - Support deep visibility into dry-run failures in \PreflightReport\ models to improve user debugging. - Add \--quiet\ flag support across \shipper-progress\ and \shipper-cli\ for CI/CD integration. - Ensure \micro-all\ features are default-enabled for the CLI to use modular components. - Modernize \shipper-config\ by adding and validating \schema_version\ in \.shipper.toml\.
…i-registry publishing - Implement ETag-based disk caching for Cargo sparse index to accelerate readiness polling. - Add \--resume-from <pkg>\ support to sequential and parallel engines for flexible recovery. - Enhance \shipper doctor\ with health checks for registry connectivity, git state, and permissions. - Support multi-registry publishing in CLI with state directory segregation by registry name. - Update CI templates and snippets to include \--quiet\ flag and better state caching patterns. - Fix clippy warnings and ensure workspace compilation with all features enabled. - Synchronize monolithic and modularized implementations for all new features.
- Document \--resume-from\ and multi-registry support in README. - Expand CI commands section with CircleCI and Azure DevOps. - Include \completion\ command in README. - Stabilize BDD tests by increasing mock server timeouts to 60s.
- Bump workspace version to 0.3.0. - Update CHANGELOG.md with 0.3.0 entry. - Update ROADMAP.md current release to v0.3.0. - Add RELEASE_NOTES_v0.3.0.md and RELEASE_CHECKLIST_v0.3.0.md. - Synchronize dependency versions in Cargo.toml files. - Final pass of clippy and test fixes for release readiness.
- Handle Dependabot dependency updates (major bumps for dirs, rand, indicatif, toml). - Update GitHub Actions versions in workflows and templates. - Bump workspace version to 0.3.0-rc.1. - Migrate shipper-retry to rand 0.10.0 API. - Final pass of release assets and checklist.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Rate limit exceeded
⌛ 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. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (28)
WalkthroughThis PR prepares the Shipper 0.3.0-rc.1 release by extracting domain logic into new modular crates (shipper-auth, shipper-cargo-failure, shipper-chunking), refactoring shipper-cli to use external progress reporting, adding multi-registry publishing support, expanding CI/mutation testing automation, and introducing comprehensive BDD test coverage for CLI workflows. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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 |
- Fully addresses remaining Dependabot PR.
Add extensive test coverage across all 31 workspace microcrates: Unit tests: - shipper-auth: 41 tests (token resolution, credentials, priority ordering) - shipper-encrypt: 58 tests (roundtrips, corruption, property tests) - shipper-events: 50 tests (serialization, JSONL, filtering, property tests) - shipper-git: 49 tests (status, branches, tags, context, serialization) - shipper-levels: 26 tests (computation, DAGs, cycles, property tests) - shipper-lock: 29 tests (acquisition, contention, cleanup) - shipper-plan: 37 tests (building, ordering, selection, snapshots) - shipper-process: 44 tests (execution, timeouts, output capture) - shipper-webhook: 45 tests (payloads, HMAC, delivery, mock server) - shipper-environment: 69 tests (CI detection, fingerprints, versions) - shipper-registry: 58 tests (HTTP mocking, URLs, owners, sparse index) - shipper-store: 47 tests (persistence, receipts, corruption) - shipper-state: 25 integration tests (atomic writes, migration) - shipper-storage: 33 integration tests (file ops, backends) Snapshot tests (insta): - shipper-config: 10 snapshots (defaults, validation errors, roundtrips) - shipper-types: 5 snapshots (plans, states, receipts, reports) - shipper-plan: 7 snapshots (plans, errors, selections) - shipper-cli: 14 snapshots (help texts, version, errors) Property-based tests (proptest): - shipper-config: 5 property tests (roundtrips, validation, merge) - shipper-types: 27 property tests (all type roundtrips, transitions) - shipper-events: 11 property tests (serialization, ordering) - shipper-registry: 6 property tests (URLs, sparse index, roundtrips) BDD tests: - bdd_doctor: 5 tests (environment, cargo, token, workspace) - bdd_publish_flow: 12 tests (preview, errors, filtering, state) - bdd_resume: 4 tests (no state, empty, corrupt, missing dir) E2E tests: - e2e_plan: 9 tests (workspaces, filtering, determinism, ordering) - e2e_config: 8 tests (init, validate, flags, errors) - e2e_preflight: 14 tests (clean, git, ownership, JSON, events) - e2e_status: 10 tests (published/missing, filtering, format) Doc tests: - 12 doc-test examples across 6 crates (duration, retry, chunking, output-sanitizer, policy, schema) Fuzz targets: - webhook_payload: payload deserialization, HMAC, URL parsing - plan_builder: workspace manifests, topo sort verification - git_context: context deserialization, commit parsing Infrastructure: - .github/workflows/mutation.yml: weekly cargo-mutants CI - .cargo/mutants.toml: mutation testing configuration - Fixed clippy warnings across workspace Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
- Add 22 unit tests for shipper-progress - Add 38 unit tests + 5 proptests for shipper-execution-core - Add 53 tests for shipper-cargo (unit + integration) - Add 88 tests + 5 proptests for shipper-cargo-failure - Add 47 contract tests for shipper-config-runtime - Add 14 tests for shipper-engine-parallel - Add 11 cross-crate integration tests for shipper facade - Add 8 BDD parallel publishing tests - Add README.md for 29 microcrates (crates.io display) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Property tests added (proptest): - shipper-auth: 8 property tests (token roundtrip, env resolution, precedence) - shipper-cargo: 13 property tests (CargoOutput, package validation, tail_lines) - shipper-cargo-failure: 5 property tests (classification roundtrip) - shipper-environment: 14 property tests (CI detection, fingerprint, serde) - shipper-git: 12 property tests (context fields, status parsing, serde) - shipper-process: 10 property tests (output, exit codes, command building) - shipper-state: 6 property tests (state/receipt roundtrip, save/load) - shipper-storage: 6 property tests (paths, atomic write, nested dirs) - shipper-store: 4 property tests (receipt roundtrip, events append) - shipper-webhook: 10 property tests (payload roundtrip, signatures) Snapshot tests added (insta): - shipper-auth: 14 snapshots (token resolution, errors, credentials) - shipper-cargo-failure: 12 snapshots (classification, formatting, debug) - shipper-events: 13 snapshots (event serialization, log format) - shipper-registry: 16 snapshots (responses, URLs, sparse index, errors) - shipper-state: 5 snapshots (state/receipt JSON, transitions) BDD tests added: - bdd_preflight.rs: 7 tests covering all preflight_checks.feature scenarios - bdd_micro_backends.rs: 2 tests for micro_backend_feature_flags.feature - bdd_error_recovery.rs: 18 tests for error recovery and retry scenarios Integration tests: - facade_integration.rs: 23 tests (plan, config, state, events, auth, registry) Bug fixes: - Fix use-after-move in fs::set_permissions across 8 test files (Linux CI) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Prepares the workspace for the v0.3.0-rc.1 release candidate by introducing new microcrates, expanding test coverage (BDD/e2e/snapshots), updating documentation/release artifacts, and refreshing CI workflows for the new architecture.
Changes:
- Add new microcrates (e.g.,
shipper-execution-core,shipper-chunking,shipper-config-runtime,shipper-engine-parallel) and wire them into the workspace. - Expand automated coverage via new BDD/e2e tests, proptest regressions, and snapshot baselines across several crates.
- Update release docs/changelog and refresh GitHub Actions workflows (including adding mutation testing).
Reviewed changes
Copilot reviewed 145 out of 283 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/shipper-levels/README.md | Adds crate README for the levels microcrate. |
| crates/shipper-git/README.md | Adds crate README for git operations microcrate. |
| crates/shipper-git/Cargo.toml | Adds dev-deps for property/snapshot-style testing. |
| crates/shipper-execution-core/tests/execution_core_bdd.rs | Adds BDD tests covering state updates/persistence. |
| crates/shipper-execution-core/README.md | Adds crate README for execution-core microcrate. |
| crates/shipper-execution-core/Cargo.toml | Introduces new microcrate manifest + deps/dev-deps. |
| crates/shipper-events/src/snapshots/shipper_events__tests__readiness_started_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__readiness_complete_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__preflight_ownership_check_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__preflight_complete_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__plan_created_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__package_started_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__package_started_json.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__package_skipped_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__package_published_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__package_failed_retryable_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__multiple_events_jsonl.snap | Adds insta snapshot baseline for jsonl output. |
| crates/shipper-events/src/snapshots/shipper_events__tests__execution_finished_success_yaml.snap | Adds insta snapshot baseline for events. |
| crates/shipper-events/src/snapshots/shipper_events__tests__event_log_package_lifecycle.snap | Adds insta snapshot baseline for event log aggregation. |
| crates/shipper-events/README.md | Adds crate README for events microcrate. |
| crates/shipper-events/Cargo.toml | Adds dev-deps for snapshot/property testing. |
| crates/shipper-environment/proptest-regressions/lib.txt | Checks in proptest regression seeds for stability. |
| crates/shipper-environment/README.md | Adds crate README for environment microcrate. |
| crates/shipper-environment/Cargo.toml | Adds dev-deps for serial/env/property testing. |
| crates/shipper-engine-parallel/tests/chunking_integration.rs | Adds integration test for chunking order preservation. |
| crates/shipper-engine-parallel/tests/chunking_bdd.rs | Adds BDD tests for chunking behavior. |
| crates/shipper-engine-parallel/src/webhook.rs | Refines docs/attributes and removes async thread-spawn API. |
| crates/shipper-engine-parallel/README.md | Adds crate README for parallel engine microcrate. |
| crates/shipper-engine-parallel/Cargo.toml | Introduces new crate manifest + deps/dev-deps for parallel engine. |
| crates/shipper-encrypt/README.md | Adds crate README for encryption microcrate. |
| crates/shipper-encrypt/Cargo.toml | Adds dev-deps for property tests and serde_json. |
| crates/shipper-duration/src/lib.rs | Adds rustdoc examples for duration parsing. |
| crates/shipper-duration/README.md | Adds crate README for duration microcrate. |
| crates/shipper-duration/Cargo.toml | Bumps toml dev-dependency major version. |
| crates/shipper-config/tests/config_integration.rs | Adds integration tests for config template loading/merging. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_zero_output_lines.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_zero_max_attempts.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_zero_base_delay.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_max_delay_lt_base.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_jitter_out_of_range.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__validation_error_empty_registry_name.snap | Adds validation error insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__toml_roundtrip_parsed.snap | Adds roundtrip parse insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__default_toml_template.snap | Adds default template insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__default_config.snap | Adds default config insta snapshot. |
| crates/shipper-config/src/snapshots/shipper_config__tests__snapshot_tests__config_all_fields.snap | Adds full config insta snapshot. |
| crates/shipper-config/README.md | Adds crate README for config microcrate. |
| crates/shipper-config/Cargo.toml | Bumps toml and adds schema + testing deps. |
| crates/shipper-config-runtime/tests/config_runtime_bdd.rs | Adds BDD tests for config→types conversion behavior. |
| crates/shipper-config-runtime/src/lib.rs | Introduces conversion layer crate + unit/proptest coverage. |
| crates/shipper-config-runtime/README.md | Adds crate README for config-runtime microcrate. |
| crates/shipper-config-runtime/Cargo.toml | Introduces new crate manifest + deps. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__version_flag.snap | Adds CLI snapshot for version output. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__unknown_subcommand_error.snap | Adds CLI snapshot for error output. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__status_help.snap | Adds CLI snapshot for status help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__resume_help.snap | Adds CLI snapshot for resume help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__publish_help.snap | Adds CLI snapshot for publish help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__preflight_help.snap | Adds CLI snapshot for preflight help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__plan_help.snap | Adds CLI snapshot for plan help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__no_subcommand_error.snap | Adds CLI snapshot for missing-subcommand error. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__help_text.snap | Adds CLI snapshot for top-level help. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__doctor_help.snap | Adds CLI snapshot for doctor help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__config_help.snap | Adds CLI snapshot for config help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__completion_missing_shell.snap | Adds CLI snapshot for completion arg error. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__clean_help.snap | Adds CLI snapshot for clean help text. |
| crates/shipper-cli/tests/snapshots/cli_snapshots__ci_help.snap | Adds CLI snapshot for ci help text. |
| crates/shipper-cli/tests/e2e_status.rs | Adds end-to-end tests for shipper status using mock HTTP server. |
| crates/shipper-cli/tests/e2e_plan.rs | Adds end-to-end tests for shipper plan output/ordering. |
| crates/shipper-cli/tests/e2e_config.rs | Adds end-to-end tests for shipper config init/validate. |
| crates/shipper-cli/tests/cli_snapshots.rs | Adds insta snapshot harness for CLI help/error outputs. |
| crates/shipper-cli/tests/cli_e2e.rs | Fixes permissions call to use &path. |
| crates/shipper-cli/tests/bdd_resume.rs | Adds BDD coverage for resume failure scenarios. |
| crates/shipper-cli/tests/bdd_publish.rs | Expands publish BDD coverage including redaction + policy behavior. |
| crates/shipper-cli/tests/bdd_preflight.rs | Adds BDD coverage for preflight workflows. |
| crates/shipper-cli/tests/bdd_micro_backends.rs | Adds BDD coverage verifying micro backend stability. |
| crates/shipper-cli/tests/bdd_doctor.rs | Adds BDD coverage for doctor diagnostics flows. |
| crates/shipper-cli/src/progress.rs | Removes in-crate progress reporting implementation. |
| crates/shipper-cli/Cargo.toml | Bumps shipper dependency version, adds shipper-progress, enables micro-all by default. |
| crates/shipper-chunking/tests/chunking_contract_integration.rs | Adds integration test for chunking on structured items. |
| crates/shipper-chunking/tests/chunking_bdd.rs | Adds BDD tests for chunking edge cases. |
| crates/shipper-chunking/src/lib.rs | Introduces chunking microcrate implementation + tests/proptests. |
| crates/shipper-chunking/README.md | Adds crate README for chunking microcrate. |
| crates/shipper-chunking/Cargo.toml | Introduces new crate manifest + dev-deps. |
| crates/shipper-cargo/tests/redaction_contract.rs | Adds contract test aligning redaction behavior across crates. |
| crates/shipper-cargo/tests/cargo_commands.rs | Adds broad integration tests for cargo command execution helpers. |
| crates/shipper-cargo/README.md | Adds crate README for cargo microcrate. |
| crates/shipper-cargo/Cargo.toml | Pulls in output sanitizer dependency and test deps. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__retryable_precedence.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__retryable_classification.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__realistic_rate_limit.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__realistic_compilation_failure.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__realistic_already_published.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__permanent_classification.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_retryable.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_permanent.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_class_retryable.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_class_permanent.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_class_ambiguous.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__debug_ambiguous.snap | Adds debug-format snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__ambiguous_classification.snap | Adds classification snapshot baseline. |
| crates/shipper-cargo-failure/src/snapshots/shipper_cargo_failure__tests__all_messages.snap | Adds snapshot baseline for message set. |
| crates/shipper-cargo-failure/README.md | Adds crate README for cargo-failure microcrate. |
| crates/shipper-cargo-failure/Cargo.toml | Adds insta dev-dependency. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_resolve_token_none_found.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_resolve_token_from_env_registry.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_resolve_token_from_env_default.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_resolve_token_from_credentials_file.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_resolve_token_custom_registry_from_credentials.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_list_configured_registries_mixed.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_list_configured_registries_empty.snap | Adds auth snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_error_token_not_found_for_registry.snap | Adds auth error snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_error_missing_credentials_file.snap | Adds auth error snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_error_malformed_credentials_file.snap | Adds auth error snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_credentials_legacy_toplevel_format.snap | Adds credentials parsing snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_credentials_custom_registry_section.snap | Adds credentials parsing snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_credentials_crates_io_registry_section.snap | Adds credentials parsing snapshot baseline. |
| crates/shipper-auth/src/snapshots/shipper_auth__tests__snapshots__snapshot_auth_info_default.snap | Adds default auth snapshot baseline. |
| crates/shipper-auth/proptest-regressions/lib.txt | Checks in proptest regression seed for auth. |
| crates/shipper-auth/README.md | Adds crate README for auth microcrate. |
| crates/shipper-auth/Cargo.toml | Bumps toml/dirs and adds testing deps. |
| ROADMAP.md | Updates roadmap current release pointer to 0.3.0-rc.1. |
| RELEASE_NOTES_v0.3.0.md | Adds release notes content for 0.3.0-rc.1. |
| RELEASE_CHECKLIST_v0.3.0.md | Adds release checklist for 0.3.0-rc.1. |
| README.md | Updates top-level docs for new commands/features and multi-registry. |
| Cargo.toml | Bumps workspace version to 0.3.0-rc.1, adds new members, adds workspace dev-deps. |
| CHANGELOG.md | Adds 0.3.0-rc.1 changelog entry. |
| .github/workflows/release.yml | Updates workflow action versions related to releases/artifacts. |
| .github/workflows/mutation.yml | Adds scheduled/manual mutation testing workflow. |
| .github/workflows/fuzz.yml | Updates fuzz targets and workflow action versions. |
| .github/workflows/ci.yml | Updates CI action versions and expands feature-matrix. |
| .cargo/mutants.toml | Adds cargo-mutants configuration. |
| - [x] Verify `Cargo.toml` workspace version is `0.3.0-rc.1` | ||
| - [x] Update `CHANGELOG.md` with 0.3.0-rc.1 entry | ||
| - [x] Update `ROADMAP.md` current version | ||
| - [x] Create `RELEASE_NOTES_v0.3.0-rc.1.md` |
There was a problem hiding this comment.
The checklist references RELEASE_NOTES_v0.3.0-rc.1.md, but this PR adds RELEASE_NOTES_v0.3.0.md (with v0.3.0-rc.1 content). Align the filename in the checklist (or rename the release notes file) so release automation/manual steps are unambiguous.
| - [x] Create `RELEASE_NOTES_v0.3.0-rc.1.md` | |
| - [x] Create `RELEASE_NOTES_v0.3.0.md` |
|
|
||
| /// Split a list of items into contiguous chunks bounded by `max_concurrent`. | ||
| /// | ||
| /// - `max_concurrent <= 0` is treated as `1`. |
There was a problem hiding this comment.
The docs say max_concurrent <= 0, but the parameter type is usize, so the only non-positive value representable is 0. Consider updating the wording to max_concurrent == 0 (or change the API type if you truly want to accept negatives).
| /// - `max_concurrent <= 0` is treated as `1`. | |
| /// - If `max_concurrent == 0`, a batch size of `1` is used. |
| for idx in 0..expected_requests { | ||
| let req = server.recv().expect("request"); | ||
| let status = statuses | ||
| .get(idx) | ||
| .copied() | ||
| .or_else(|| statuses.last().copied()) | ||
| .unwrap_or(404); |
There was a problem hiding this comment.
The mock registry thread uses a blocking server.recv(), and the test unconditionally join()s the thread. If the CLI makes fewer requests than expected_requests (e.g., early exit on failure), this will block forever and hang the test suite. Use recv_timeout(...) (with a short timeout) and exit the loop on timeout/error to keep tests fail-fast and non-hanging.
| for _ in 0..10 { | ||
| let req = match server.recv_timeout(Duration::from_secs(60)) { | ||
| Ok(Some(req)) => req, | ||
| _ => break, | ||
| }; |
There was a problem hiding this comment.
This increases the maximum wait for readiness mock synchronization to 60s per test run attempt. Since the loop breaks on the first timeout, a missing request now costs up to 60 seconds per failing/flaky run, which can significantly slow CI feedback. Consider lowering the timeout (e.g., 5–10s) and/or making it configurable so failures remain quick while still being robust.
Add comprehensive E2E tests for publish flow (8 tests) and doctor command (7 tests). Add BDD-style tests for status command (10 tests) and config init/validate commands (7 tests). Add 3 new fuzz targets for config_parse, state_load, and receipt_load. Test coverage: +32 E2E/BDD tests, +3 fuzz targets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Property tests: levels (9), encrypt (8), progress (7), duration (9), lock (5) Snapshot tests: config (5), environment (10), git (17), lock (15), plan (3), process (12), storage (21), store (23), webhook (16) Integration tests: plan→engine (9), config→CLI (10) Documentation: docs/testing.md, docs/architecture.md Test coverage: +38 proptests, +122 snapshots, +19 integration tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 4 new feature files: - status_command.feature (8 scenarios): version comparison, registry status - config_management.feature (12 scenarios): init, validate, merge, precedence - resume_workflow.feature (9 scenarios): resume partial publishes, plan_id checks - error_handling.feature (13 scenarios): auth failures, network, rate limiting Add scenarios to existing feature files: - preflight_checks.feature: +4 scenarios (allow-dirty, finishability, ownership skip, ordering) - parallel_levels.feature: +5 scenarios (independent, linear, single, diamond, concurrency limit) - publish_resume.feature: +4 scenarios (events log, custom state dir, no-verify, failure receipt) - micro_backend_feature_flags.feature: +2 scenarios (plan output, config loading) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Snapshot tests: types (22), cargo-failure (20) Doc comments: shipper-plan, shipper-config, shipper-types, shipper-auth Feature files: 4 new (status, config, resume, error_handling) + 4 expanded CI: Add concurrency group with cancel-in-progress Dependency: Merge dirs 5→6 (PR #16) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…optests, lock proptests Proptests: execution-core (12), config-runtime (15), lock (12) Snapshots: execution-core (21) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, types Snapshot tests: sparse-index (13), schema (17), policy (11), execution-core (21) Property tests: schema (7), policy (13), sparse-index (17), types (13), execution-core (12), config-runtime (15), lock (12) BDD feature files: status, config, resume, error handling (42 scenarios) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ocs update Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- retry: +14 tests (41 total) — zero/overflow retries, jitter edges, backoff saturation, policy snapshots, proptest backoff cap - chunking: +19 tests (43 total) — empty/single/large inputs, chunk boundaries, ordering preservation, proptest conservation - engine-parallel: +13 tests (49 total) — zero/one/large concurrency, level ordering, diamond/linear snapshots, error propagation - plan: +18 tests (58 total) — empty/linear/diamond/wide topologies, special names, stability (10 runs), proptest ordering + determinism - progress: +23 tests (74 total) — zero/max totals, beyond-total, concurrent threads, percentage snapshots, status transitions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- auth: +45 tests (108 total) — credential precedence, malformed/unicode tokens, trusted publishing detection, OIDC, proptest robustness - git: +37 tests (115 total) — empty/detached/no-commits repos, binary files, submodules, unicode filenames, long paths, proptest parsing - events: +50 tests (114 total) — corrupt JSONL, large payloads, unicode, trailing newlines, all EventType roundtrips + snapshots, concurrent append - lock: +42 tests (99 total) — stale recovery, concurrent acquire, force break, corrupt content, unicode paths, zero/max timeout, proptest pairing - storage: +18 tests (105 total) — concurrent r/w, large files, unicode, deeply nested dirs, atomic write, proptest roundtrip Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 27721360 | Triggered | Basic Auth String | a8d6133 | crates/shipper-git/src/lib.rs | View secret |
| 27722904 | Triggered | Bearer Token | 1ffd228 | crates/shipper-output-sanitizer/src/lib.rs | View secret |
| 27725107 | Triggered | Generic Password | d5ca640 | crates/shipper-encrypt/src/lib.rs | View secret |
| 27725108 | Triggered | Generic Password | d5ca640 | crates/shipper-encrypt/src/lib.rs | View secret |
| 27725953 | Triggered | Generic Password | 783fc50 | crates/shipper/src/config.rs | View secret |
| 27725954 | Triggered | Bearer Token | 3274118 | crates/shipper/src/cargo.rs | View secret |
| 27725952 | Triggered | Generic High Entropy Secret | 783fc50 | crates/shipper/src/auth.rs | View secret |
| 27725955 | Triggered | Generic High Entropy Secret | 783fc50 | crates/shipper/src/auth.rs | View secret |
| 27728455 | Triggered | Generic Password | f000312 | crates/shipper-encrypt/src/lib.rs | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
…lippy warnings - webhook: +39 tests (110 total) — malformed URLs, timeouts, large payloads, concurrent sends, HTTP status codes, snapshots - execution-core: +44 tests (~127 total) — state machine transitions, concurrent updates, empty/single plans, snapshots, proptests, fp tolerance fix - config: +69 tests (~110 total) — empty TOML, unknown sections, each section individually, policy presets, unicode, proptest roundtrip - encrypt: +19 tests (105 total) — large data, key boundaries, tampered data, snapshots, proptests - facade: +28 integration tests — deep chains, registry errors, state roundtrip, resume, event logs, receipts, lock lifecycle, snapshots - Fix clippy: div_ceil in chunking, needless borrow in lock, unused mut in facade/cross_crate tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- types: +60 tests (153 total) — state transitions, plan determinism, receipt roundtrips, policy/mode exhaustive, snapshots, proptests - process: +40 tests (106 total) — command building, output parsing, timeout, error classification, env passthrough, snapshots, proptests - levels: +24 tests (64 total) — disconnected components, cycles, large graphs, stability, double diamond, snapshots, proptests - sparse-index: +38 tests (62 total) — URL construction, crate name edges, response parsing, version filtering, snapshots, proptests - CLI E2E: +26 tests — doctor, config init/validate, status, plan, clean, completion, CI templates, version, error output, snapshots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- environment: +28 tests (121 total) — fingerprint completeness, OS/arch detection, Rust version parsing, cargo home, reproducibility, snapshots, proptests - registry: +20 tests — HTTP error codes (429/500/502/503), malformed responses, version comparison, timeout, readiness edges, snapshots, proptests - BDD: +10 workflow scenarios — resume, parallel publish, status, doctor diagnostics with full Given-When-Then step definitions - fuzz: +3 new targets (receipt_deserialize, execution_state_deserialize, plan_levels) - fix: restore mut lock for set_plan_id/release in facade/cross_crate tests with #[allow(unused_mut)] for all-features clippy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r(76), duration(73), schema(55) - cargo-failure: +39 tests — real-world cargo errors (DNS, SSL, OOM, segfault, HTTP 409), ambiguous edge cases, cross-stream classification, 5 snapshots, 2 proptests - config-runtime: +35 tests — flag precedence, default values, partial config, policy combinations, registry config, 6 snapshots, 2 proptests - output-sanitizer: +26 tests — token patterns (JWT, basic auth, registry), no false positives, unicode, large output, 3 snapshots, 2 proptests, bug fix for empty line handling - duration: +16 tests — zero forms, combined units, serde roundtrips, invalid inputs, 2 snapshots, 2 proptests - schema: +17 tests — upgrade chains, downgrade rejection, unicode digits, edge cases, 2 snapshots, 3 proptests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- retry: +17 tests — backoff curves, strategy selection, jitter bounds, zero attempts, executor classification, 2 snapshots, 3 proptests - policy: +23 tests — ordering invariants, field coverage, serialization, trait behavior, debug format, 4 snapshots, 4 proptests - plan: +17 tests — build-dep ordering, multi-select, cycle detection, plan identity, dev-dep exclusion, determinism, 2 snapshots, 2 proptests - state: +23 tests — persistence roundtrips, lifecycle transitions, plan ID validation, edge cases, 2 snapshots, 2 proptests - store: +18 tests — directory creation, partial clear, custom state-dir, concurrent safety, 2 snapshots, 3 proptests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- auth: +21 tests — credentials parsing, token format, alternative registries, priority chain, 3 snapshots, 3 proptests - git: +21 tests — status transitions, branch detection, detached HEAD, error handling, 3 snapshots, 2 proptests - events: +16 tests — JSONL roundtrips, timestamp ordering, filtering, large logs, unicode, 2 snapshots, 2 proptests - lock: +22 tests — acquisition, plan_id, stale lock, RAII drop, force acquire, 3 snapshots, 4 proptests - storage: +22 tests — atomic writes, directory creation, roundtrips, error handling, concurrent access, 2 snapshots, 2 proptests - fix: ignore concurrent_reads_and_writes_same_file on Windows (pre-existing rename race) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g(65), encrypt + storage fix - engine-parallel: +19 tests — concurrency control, dependency ordering, error propagation, progress tracking, 2 snapshots, 3 proptests - webhook: +20 tests — payload construction, HTTP delivery, HMAC signatures, edge cases, 3 snapshots, 3 proptests - progress: +17 tests — percentage precision, package tracking, display formatting, 2 snapshots, 2 proptests - chunking: +15 tests — chunk boundaries, remainder handling, determinism, 2 snapshots, 3 proptests - encrypt: additional hardening (agent in progress) - storage: atomic write roundtrip snapshot fix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…+5), encrypt hardening - BDD: +7 scenarios — config validation, doctor diagnostics, clean command, plan filtering, preflight checks - facade: +13 integration tests — error recovery, partial publish, plan filtering, readiness, events, receipts, locks - CLI E2E: +22 snapshot tests — error output, help text, version, config, CI snippets, clean, plan, inspect - fuzz: +5 new targets (config_deserialize, webhook_payload, auth_token, output_sanitizer, duration_parse) - encrypt: additional hardening from long-running agent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…argo(+43), plan(+32) - engine: +30 tests — state transitions, retry/backoff strategies, error classification, receipts, resume-from logic, multi-package scenarios - engine-parallel: +19 tests — concurrent state, dependency ordering, webhook delivery, progress tracking, resume skipping - registry: +48 tests — HTTP errors (4xx/5xx), rate limiting (429), malformed responses, large payloads, concurrent checks, sparse index edge cases - cargo: +43 tests — token redaction positions, unicode output, long lines, timeout behavior, env resolution, dry-run variants, idempotent redaction - plan: +32 tests — diamond dependencies, deep chains, cycle detection, plan determinism, plan ID stability, package filtering, invalid manifests Total: 3,953 tests (up from 3,764) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- state: +36 tests — corruption handling, atomic writes, receipt states, schema versions, large files, resume partial states, concurrent writes - config: +71 tests — TOML edge cases, CLI override precedence, retry presets, boolean OR semantics, readiness/webhook/registry merging, validation boundaries - auth: +35 tests — env var priority, credentials.toml parsing, malformed TOML, unicode/base64 tokens, OIDC detection, cargo_home resolution - git: +20 tests — dirty tree variants (staged/modified/deleted), branch detection, empty repo, unicode filenames, merge conflicts, shallow clones, submodules - CLI E2E: +30 tests — help snapshots, doctor output, status with mock registry, plan filtering, error messages, config init variants, quiet mode Total: 4,152 tests (up from 3,953) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- output-sanitizer: normalize \r\n and bare \r to \n at start of redact_sensitive() for true idempotence (fuzz target redact_output found bare \r causing different results across successive calls) - cargo: merge redundant publish_timeout_message_includes_duration_text test into publish_with_very_short_timeout_times_out (eliminates flaky CI failure on Linux llvm-cov) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- failure-modes.md: fix [retry.strategy] nested TOML to flat fields on [retry] - failure-modes.md: add 6 missing permanent patterns (readme, repository, compilation failed, failed to verify, publish is disabled, yanked) - configuration.md: add missing retry fields (policy, strategy, jitter) to [retry] section and example config - README.md: add missing --retry-strategy, --retry-jitter, --verbose, -q/--quiet flags to Options section - templates: update Rust versions from 1.75 to latest (MSRV is 1.92) - templates: add --locked to cargo install in azure/circleci templates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add comprehensive E2E snapshot tests covering previously uncovered CLI error paths and behaviors: - publish: missing manifest, invalid manifest content, exit codes - resume: no state file, corrupted state, empty state, missing manifest - preflight: non-git directory, --allow-dirty with --skip-ownership-check - config validate: wrong value type, malformed TOML, invalid nested section - plan: all publish=false workspace, --format json flag, --verbose single crate - help text: preflight --help, ci --help - errors: unknown subcommand, exit code assertions - inspect-events: with populated events file Also adds normalize_timing() helper to stabilize snapshots containing variable duration values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ning - output-sanitizer: normalize \r\n and bare \r in tail_lines() (fixes proptest CI failure with bare \r in input, same root cause as redact_sensitive fix) - BDD: +7 edge-case scenarios (empty workspace, missing manifest, corrupted state, etc.) - integration: +14 pipeline integration tests (cross-module state/events/receipts/locks) - fuzz: +2 new targets (plan_ordering, event_log_roundtrip), register 5 unregistered targets - fuzz: harden all existing fuzz targets with better input handling and crash resistance Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hots), BDD hardening - engine: +22 tests — retry logic edge cases, state persistence verification, error classification matrix, init state snapshots, mixed transition snapshots, proptests - registry: +34 tests — HTTP error handling (429/500/502/503/504), response parsing, version comparison, owner verification, readiness evidence, snapshots, proptests - state: +22 tests — atomic write safety, receipt completeness with evidence/git context, empty packages state, migration/versioning, concurrent access, snapshots, proptests - plan: +5 snapshot tests — dev-dep cycles, double diamond, mixed dep kinds, prerelease, double diamond selected mid - BDD: hardened workflow scenarios with improved step definitions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…E2E(+16), BDD(+5) - cargo: +25 tests — tail_lines edge cases (empty, Unicode, mixed endings), env var handling, package name validation, prerelease version parsing, 3 snapshots, proptests - encrypt: +20 snapshot tests — passphrase masking, config display, empty plaintext, no-passphrase error, key boundary edge cases, round-trip verification - facade: +20 pipeline integration tests — full pipeline execution, error propagation, resume after partial publish, event log integrity, receipt completeness, lock lifecycle - CLI E2E: +16 new snapshot tests — clean commands, config validate sections, plan formatting, resume edge cases, status multi-package, help subcommands, error messages - BDD: +5 workflow scenarios — enhanced edge case coverage for publish/resume flows Total: 4,431 #[test] annotations, 33 fuzz targets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sume fixes, env isolation - Add 36 new insta snapshots across auth(6), cargo-failure(6), config(7), plan(5), state(6), engine(2), registry(2), CLI e2e(2) - Fix mock server hangs: replace blocking recv() and 60s recv_timeout() with 5s recv_timeout() across all test files (engine, engine_parallel, bdd_workflow, bdd_resume, bdd_doctor, bdd_preflight, bdd_publish, bdd_micro_backends, bdd_status, cli_e2e, e2e_doctor, e2e_expanded, e2e_preflight, e2e_resume, e2e_status) - Fix bdd_resume response sequences: correct request counts for resume_from_specific_package and force_resume_then_bypasses_mismatch - Add #[serial] to shipper-environment tests that read CI env vars - Add normalize_error helper in shipper-state for deterministic snapshots - Fix shipper-events doc comment indentation - Add *.log to .gitignore
There was a problem hiding this comment.
Actionable comments posted: 46
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
ROADMAP.md (2)
7-7:⚠️ Potential issue | 🟡 MinorUpdate Table of Contents to reflect current version.
The ToC still references v0.2.0, but the document has been updated to v0.3.0-rc.1.
📝 Proposed fix
-1. [Current Release: v0.2.0](`#current-release-v020`) +1. [Current Release: v0.3.0-rc.1](`#current-release-v030-rc1`)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ROADMAP.md` at line 7, Update the Table of Contents entry that currently reads "Current Release: v0.2.0" (and its anchor "(`#current-release-v020`)") to match the document's new release string "v0.3.0-rc.1" and update the anchor to the corresponding heading anchor (e.g., "(`#current-release-v030-rc1`)" or whatever the generated slug for the "Current Release: v0.3.0-rc.1" heading is) so the ToC label and link both reflect v0.3.0-rc.1.
560-563:⚠️ Potential issue | 🟡 MinorUpdate version history table to reflect current release.
The version history table still marks v0.2.0 as "Current", but v0.3.0-rc.1 is now the current release.
📝 Proposed fix
| Version | Status | Theme | Notes | |---------|--------|-------|-------| +| v0.3.0-rc.1 | **Current** | User Experience | Multi-registry publishing, sparse index caching, selective resumability | -| v0.2.0 | **Current** | Evidence & Verification | Parallel publishing, configuration files, readiness verification | +| v0.2.0 | Released | Evidence & Verification | Parallel publishing, configuration files, readiness verification | | v0.1.0 | Released | Core Functionality | Initial release with basic publish workflow |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ROADMAP.md` around lines 560 - 563, The version table currently marks v0.2.0 as "Current"; change the table so the "Version" cell shows v0.3.0-rc.1 with "Status" set to **Current** (replace the v0.2.0 row), and update the v0.2.0 row to "Released" under Status with its existing Theme/Notes; ensure the table includes v0.3.0-rc.1 Theme/Notes (e.g., release candidate details) and that rows remain properly aligned (refer to the Version entries v0.3.0-rc.1, v0.2.0 as unique identifiers)..github/workflows/fuzz.yml (1)
82-100:⚠️ Potential issue | 🟠 MajorDon't swallow non-crasher fuzz failures.
continue-on-error: truemasks step failures, and the workflow only checks for crash artifacts—not whether the fuzz step itself failed. Build errors, missing targets, or toolchain failures produce noartifacts/directory and leave the job green, creating false-positive test results.Add an outcome check to catch fuzz failures that don't produce crash artifacts:
Proposed fix
- name: Run fuzz target + id: fuzz_run run: | cargo fuzz run ${{ matrix.target }} \ --corpus fuzz/corpus/${{ matrix.target }} \ -- \ -max_total_time=${{ steps.duration.outputs.value }} \ -artifact_prefix=artifacts/${{ matrix.target }}_ continue-on-error: true - name: Check for crashers id: check_crashers run: | if [ -d "artifacts" ] && [ "$(ls -A artifacts 2>/dev/null)" ]; then echo "crashers_found=true" >> $GITHUB_OUTPUT echo "::error::Fuzz target ${{ matrix.target }} found crashers" ls -la artifacts/ else echo "crashers_found=false" >> $GITHUB_OUTPUT fi + + - name: Fail on non-crasher fuzz errors + if: steps.fuzz_run.outcome == 'failure' && steps.check_crashers.outputs.crashers_found != 'true' + run: | + echo "::error::cargo fuzz failed before producing crash artifacts" + exit 1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/fuzz.yml around lines 82 - 100, The fuzz run step currently uses continue-on-error: true and has no id, so non-crasher failures are hidden; give the "Run fuzz target" step a stable id (e.g., id: run_fuzz_target), keep continue-on-error: true if you want to collect artifacts, then in the "Check for crashers" step inspect both the artifacts directory and the run step outcome (check steps.run_fuzz_target.outcome != 'success') and emit crashers_found=true plus an error if either crash artifacts exist or the run step failed (and set crashers_found=false only when no artifacts exist and the run step outcome is success) so build/toolchain failures are surfaced.crates/shipper-cargo-failure/src/lib.rs (1)
26-47:⚠️ Potential issue | 🟡 MinorPotential false positive with numeric pattern "500".
The pattern
"500"on line 40 can match port numbers (e.g.,15003) or version strings. The test on lines 599-605 documents this behavior, but it could cause legitimate non-error messages to be classified as retryable failures.Consider using a more specific pattern like
" 500 "or"500 "to reduce false positives, or document this as an accepted trade-off.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/shipper-cargo-failure/src/lib.rs` around lines 26 - 47, RETRYABLE_PATTERNS currently contains the bare substring "500" which can false-match numeric contexts (e.g., ports or versions); update the RETRYABLE_PATTERNS constant to use a more specific match for HTTP 500 (for example change "500" to " 500 " or "500 " or use a regex word-boundary variant like r"\b500\b") so only standalone status codes are matched; modify the entry for "500" (and similarly "502","503","504" if desired) inside the RETRYABLE_PATTERNS array or switch to a regex-based check where the matching code consults the new pattern to avoid the documented false positives.
♻️ Duplicate comments (2)
crates/shipper-cargo/CLAUDE.md (1)
1-29:⚠️ Potential issue | 🟡 MinorSame template and formatting issues as other CLAUDE.md files.
This file has identical issues to the other CLAUDE.md files in this PR. Please substitute
shipper-cargofor the placeholders and apply the formatting fixes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/shipper-cargo/CLAUDE.md` around lines 1 - 29, The CLAUDE.md uses placeholders and a broken code fence; replace every placeholder ($name, $entryPoint, $rootDocs) with the actual crate name "shipper-cargo" (and the correct primary entry if known), fix the malformed code block (remove the stray control char and use a proper triple-backtick fenced block with "bash"), clean up Windows path escaping (use consistent path formatting or a relative path), and apply the same formatting fixes you applied to other CLAUDE.md files so headers, bullets, and the "Useful commands" block are stable and readable; locate and edit the CLAUDE.md in crates/shipper-cargo and update the placeholders and code fence accordingly.crates/shipper-cargo-failure/CLAUDE.md (1)
1-29:⚠️ Potential issue | 🟡 MinorSame template and formatting issues as other CLAUDE.md files.
This file has identical issues to the other CLAUDE.md files in this PR. Please substitute
shipper-cargo-failurefor the placeholders and apply the formatting fixes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/shipper-cargo-failure/CLAUDE.md` around lines 1 - 29, Replace template placeholders and fix formatting in CLAUDE.md: substitute $name with "shipper-cargo-failure", set $entryPoint to the crate's primary entry (e.g., lib.rs or main.rs as applicable), and update $rootDocs to the actual relative docs path; correct the malformed shell code fence (replace the stray control character and use a proper ```bash block) and ensure the cargo commands are on separate lines; normalize backslashes in the workspace path or use forward slashes for consistency and fix any punctuation/typo issues so the file matches the other CLAUDE.md files' format.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Cargo.toml`:
- Around line 29-32: The workspace members list in Cargo.toml has inconsistent
indentation for the entries "crates/shipper-output-sanitizer",
"crates/shipper-levels", "crates/shipper-execution-core", and
"crates/shipper-config-runtime" (they use 4 spaces while others use 2); update
those four lines to use the same 2-space indentation as the rest of the members
list so the workspace members block is consistently formatted.
In `@crates/shipper-auth/CLAUDE.md`:
- Around line 1-29: Replace unsubstituted placeholders ($name, $entryPoint,
$rootDocs) with the crate-specific values for shipper-auth (use "shipper-auth"
for crate name and the crate's actual primary entry point), fix the fenced code
block marker from "`ash" to a proper triple-backtick block for bash, convert
Windows path separators in the workspace/root path to POSIX-style or neutral
notation, and ensure the file ends with a trailing newline; update the header
and context text accordingly so the CLAUDE.md content matches the other
corrected crate CLAUDE.md files.
In `@crates/shipper-auth/README.md`:
- Around line 1-36: The README contains template artifacts: remove the
duplicated "# shipper-auth" header, replace the "$entryPoint" placeholder with
the actual source entry (e.g., "src/lib.rs" or the crate's real entry), fix the
malformed code fence by replacing "`�ash" and the trailing single backtick with
a proper triple backtick fence "```bash" ... "```", and correct the broken tests
path " ests/" to "`tests/`" in the Contributing section; update those exact
tokens ("# shipper-auth", "$entryPoint", "`�ash", "`", and " ests/") in the
README to render clean crate docs.
In `@crates/shipper-auth/src/lib.rs`:
- Around line 1310-1337: The tests currently treat whitespace-only strings as
valid tokens; instead, update the token handling so whitespace-only credentials
are considered absent: in resolve_token(...) and
token_from_credentials_file(...), trim the extracted token (e.g., token.trim())
and only mark auth as detected / return Some(token) when the trimmed string is
non-empty; if trimmed is empty, return None / detected = false. Ensure
callers/use of token preserve the original stored token only when trimmed
non-empty.
- Around line 258-266: The mask_token function currently slices &str by byte
offsets which can panic on Unicode; update mask_token to operate on Unicode
scalar values via .chars(): compute char_count = token.chars().count(), if
char_count <= 8 return "*" repeated char_count, otherwise build the masked
string by collecting the first four chars
(token.chars().take(4).collect::<String>()) and the last four chars
(token.chars().rev().take(4).collect::<String>() then reverse that collected
tail) and concatenate with "****"; apply the same char-safe approach to the
other occurrence noted (the block around symbols handling at the second
location).
- Around line 269-276: The current is_trusted_publishing_available function
treats empty OIDC env vars as present; change the check to ensure both
ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN are non-empty
strings (e.g., use env::var_os(...).and_then(|s| if s.is_empty() { None } else {
Some(s) }).is_some() or use env::var(...) and verify non-empty), so the function
only returns true when both env vars exist and are not empty; apply the same
non-empty validation to the other identical check found later in the file (the
duplicate OIDC availability check around lines ~1531-1543).
- Around line 410-1761: Tests that mutate global environment or filesystem
(e.g., resolve_token_env_default_takes_priority_over_credentials,
resolve_token_env_registry_takes_priority_over_credentials,
has_token_returns_true_when_found, cargo_home_path_falls_back_to_env_var,
credential/file tests, trusted_publishing_* and many others) must be run
serially to avoid flakiness; add the #[serial] attribute from the serial_test
crate to each affected #[test] function (or to enclosing test modules like
snapshots, proptests, edge_snapshots, error_message_snapshots) and add use
serial_test::serial at the top of the test module so tests that set CARGO_HOME,
CARGO_REGISTRY_TOKEN, CARGO_REGISTRIES_*, or ACTIONS_ID_TOKEN_REQUEST_* run one
at a time.
In `@crates/shipper-cargo-failure/README.md`:
- Around line 1-36: The README contains leftover scaffold artifacts: a duplicate
H1, an unresolved placeholder $entryPoint, a malformed bash code fence, and a
broken tests path; fix by removing the redundant H1, replace $entryPoint with
the actual main entry (or remove the "Source entry point" section) and correct
the fenced code block marker to ```bash (or ```), and update the tests path to
`tests/` in the Contributing section (refer to the README headings and the
"Source entry point", the malformed code fence block containing cargo commands,
and the Contributing line that currently shows "( ests/, src/)").
In `@crates/shipper-cargo/Cargo.toml`:
- Around line 19-26: The crate declares shipper-output-sanitizer in both
[dependencies] and [dev-dependencies]; remove the duplicate entry from the
[dev-dependencies] section so shipper-output-sanitizer is only listed once as a
regular dependency, leaving the existing shipper-output-sanitizer = { path =
"../shipper-output-sanitizer" } in [dependencies] and deleting the same line
under [dev-dependencies]; keep other dev-dependencies (tempfile,
proptest.workspace, insta, temp-env) unchanged and run cargo check to verify.
In `@crates/shipper-cargo/README.md`:
- Around line 1-36: Remove the duplicate top-level heading and clean up
placeholders in README.md: keep a single H1/title, replace or remove the
unresolved $entryPoint placeholder in the "Source entry point" section (use the
actual entry or omit the section), fix the malformed code fence label that
currently reads "�ash" to a proper "```bash" fenced block for the development
commands, and correct the broken tests path by changing "( ests/, src/)" to
backticked "`tests/`, `src/`" in the Contributing paragraph so the markdown
renders correctly.
In `@crates/shipper-cargo/src/lib.rs`:
- Around line 1198-1222: These three tests (cargo_program_defaults_to_cargo,
cargo_program_respects_shipper_cargo_bin, cargo_program_empty_env_returns_empty)
mutate the process-wide SHIPPER_CARGO_BIN env var and must be serialized; add
the #[serial] attribute from the serial_test crate above each test (or import
serial and annotate all three) so they run sequentially and cannot race with
other tests that call cargo_program().
- Around line 1217-1221: The test cargo_program_empty_env_returns_empty
currently treats SHIPPER_CARGO_BIN="" as a valid program path, which leads to
Command::new("") later; change cargo_program() to treat an environment value
that is empty or only whitespace as unset (e.g., if
env::var("SHIPPER_CARGO_BIN").ok().filter(|s| !s.trim().is_empty()).map(...) {
... } else fallback to "cargo"), and update the test
cargo_program_empty_env_returns_empty (and any related tests) to expect the
fallback ("cargo") when SHIPPER_CARGO_BIN is set to "" rather than returning an
empty string. Ensure references to cargo_program and the test name are used when
locating the change.
In `@crates/shipper-cargo/tests/redaction_contract.rs`:
- Around line 4-9: The test
cargo_crate_redact_sensitive_matches_output_sanitizer is tautological because it
only compares two implementations; change it to call
shipper_cargo::redact_sensitive(input) and assert the returned string does not
contain the raw secrets ("shipper_secret_token" and "abc123") and/or matches the
expected redaction pattern; in other words, replace the equality check against
shipper_output_sanitizer::redact_sensitive with direct assertions that
redact_sensitive(input) does not contain the original tokens (and optionally
that it contains the expected mask placeholder) so the test fails if secrets are
leaked.
In `@crates/shipper-chunking/CLAUDE.md`:
- Around line 1-29: The CLAUDE.md contains unsubstituted template variables and
syntax/platform issues: replace $name with "shipper-chunking", $entryPoint with
"src/lib.rs" (or the crate's actual entry file), and $rootDocs with the relative
path "../../CLAUDE.md"; fix the code fence header from "`ash" to "```bash"; and
convert Windows backslashes in the workspace root and link
("h:\Code\Rust\shipper" and "$rootDocs\CLAUDE.md") to cross-platform
forward-slash/relative forms (e.g., "h:/Code/Rust/shipper" or preferably remove
the absolute path and use "../../CLAUDE.md" for the link). Ensure the commands
remain inside a proper fenced block and update any remaining placeholders
consistently.
In `@crates/shipper-chunking/README.md`:
- Around line 1-36: Remove duplicate headings and repeated description in README
(the second "# shipper-chunking" and the repeated "Chunking helpers..."
sentence), replace the unsubstituted placeholder "$entryPoint" with a meaningful
entry point description or remove the "Source entry point" section if none
exists, fix the fenced code block marker by changing the malformed "`ash" to a
proper triple-backtick with language "bash", correct the typo "( ests/" to
"(tests/" in the Contributing line, and ensure the file ends with a trailing
newline; update the README content in crates/shipper-chunking/README.md
accordingly.
In `@crates/shipper-chunking/tests/chunking_bdd.rs`:
- Around line 1-46: Add a property-based test using proptest that generates
arbitrary vectors of items and an integer max_concurrent >= 1 and asserts the
chunking invariants for chunk_by_max_concurrent: for any generated items and
limit, the flattened sequence of chunks equals the original items (order
preserved, no loss/duplication), every chunk has length <= max_concurrent, and
the total number of chunks is consistent (e.g., sum of chunk lengths ==
items.len()); import proptest, create a test (e.g.,
prop_bdd_chunking_invariants) that uses proptest::collection::vec(...) for items
and a strategy for max_concurrent with a min of 1, call
chunk_by_max_concurrent(&items, max_concurrent) and check the three invariants
above to catch regressions.
In `@crates/shipper-cli/CLAUDE.md`:
- Line 29: The file CLAUDE.md is missing a trailing newline; open the CLAUDE.md
file and add a single newline character at the end of the file (ensure the file
ends with exactly one '\n'), then save so markdownlint MD047 is satisfied.
- Around line 14-20: Replace the malformed code fence marker "ash" with a proper
fenced code block using triple backticks and the "bash" language tag and ensure
the block is closed with triple backticks; in CLAUDE.md locate the code block
starting with "`�ash" and change it to "```bash" and add a closing "```" after
the cargo commands so the snippet renders with correct syntax highlighting.
- Around line 3-10: Replace the template placeholders in CLAUDE.md with concrete
values: change all occurrences of $name to "shipper-cli", replace $entryPoint
with "src/main.rs", and remove or replace $rootDocs (e.g., remove the $rootDocs
variable or use a relative path) so the file no longer contains unsubstituted
template variables; ensure the header and Scope section reflect the actual crate
name and entry point.
In `@crates/shipper-cli/src/main.rs`:
- Around line 1457-1475: The cleanup currently deletes directories as it
iterates dirs_to_clean which can leave partial state if a later subdirectory has
an active lock; before calling clean_single_dir, add a read-only lock-validation
pass that scans the same dirs_to_clean (derived from abs_state and registry
subdirs) and checks for active locks (unless force is true), returning an error
and aborting before any mutation if any lock is held; only if the validation
passes (or force is set) proceed to the existing deletion loop that calls
clean_single_dir(workspace_root, keep_receipt, force). Apply the same two-phase
validation-then-delete change for the other block noted around lines 1489-1516.
- Around line 450-486: The loop over target_registries currently prints
per-registry banners/progress and calls print_receipt per registry, which breaks
JSON output when cli.format == "json"; instead collect each registry's receipt
(from engine::run_publish) into a Vec and, after the loop, if cli.format is json
serialize and print a single JSON array/object (suppress banners/progress during
iteration), otherwise keep the existing human-mode behavior (show
banner/progress and call print_receipt per registry). Apply the same change to
the analogous publish/resume loop (the other target_registries loop that also
calls ProgressReporter, engine::run_publish and print_receipt).
- Around line 467-479: The ProgressReporter is being preset to package 1 but
never updated, so multi-package runs stall; either remove the pre-call to
ProgressReporter::set_package(...) and let the engine drive progress via the
existing reporter, or explicitly wire ProgressReporter into the publish/resume
flow so engine::run_publish and engine::run_resume (or the reporter callbacks
they use) call ProgressReporter::set_package/update as packages change; locate
the ProgressReporter instantiation and the set_package call near
current_planned/current_opts/reporter and either delete that initial
set_package(1, ...) or pass &mut progress (or register progress callbacks) into
engine::run_publish/run_resume so the progress bar advances for each package
before calling progress.finish().
- Around line 549-565: The loop uses the original opts when calling run_doctor
so checks run against the shared state_dir instead of the registry-scoped
directory; create a registry-scoped RuntimeOptions (clone opts into a new
variable, update its state_dir to re-root under state_dir/<registry> or the same
logic used by publish/resume) and pass that into run_doctor (use the same scope
where current_planned.plan.registry is set). Ensure you reference and update the
RuntimeOptions instance used for runtime checks (opts -> registry_opts) before
calling run_doctor(¤t_planned, ®istry_opts, &mut reporter).
In `@crates/shipper-cli/tests/bdd_doctor.rs`:
- Around line 87-264: The tests mutate process-wide env/state and must be run
serially: add a use serial_test::serial; import at top of the test file and
annotate each affected test function with #[serial] (e.g. above
given_clean_environment_when_running_doctor_then_reports_environment_info,
given_cargo_is_installed_when_running_doctor_then_cargo_version_is_detected,
given_no_cargo_registry_token_when_running_doctor_then_warns_about_missing_token,
given_valid_workspace_when_running_doctor_then_checks_workspace_health, and
given_nonexistent_state_dir_when_running_doctor_then_reports_will_be_created) so
they run sequentially and avoid cross-test interference.
In `@crates/shipper-cli/tests/bdd_error_handling.rs`:
- Around line 723-738: Replace the permissive existence checks that let
scenarios pass when files are missing with hard assertions so missing
persistence fails the test: instead of wrapping the state.json handling in if
state_path.exists(), assert that state_path.exists() (with a clear message like
"state.json not written"), then read and parse the file into
state/serde_json::Value and continue the same checks (e.g. the packages lookup
and core package assertion in this block); do the same for the receipt.json
checks referenced (the blocks around the other ranges) so they assert presence
before reading rather than silently skipping when the file is absent.
- Around line 219-239: The test helper spawn_registry uses
server.recv_timeout(Duration::from_secs(30)) which can add long stalls when
tests overprovision expected_requests; change the hardcoded 30s to a much
shorter, shared constant (e.g., TEST_SERVER_RECV_TIMEOUT =
Duration::from_secs(1) or Duration::from_millis(500)) and replace all
occurrences of server.recv_timeout(Duration::from_secs(30)) in spawn_registry
and the other test helpers to use that constant so the loop breaks quickly when
fewer requests arrive; keep the existing Ok(Some(r)) / break behavior unchanged.
In `@crates/shipper-cli/tests/bdd_error_recovery.rs`:
- Around line 325-343: The test currently invokes the real cargo and default
registry, causing non-hermetic, network-dependent behavior; instead create a
local tiny_http mock registry and a fake cargo executable that simulates cargo
behavior (responds to metadata, skip/publish flow and then deterministically
fails on publish), start the tiny_http server for registry/index endpoints,
point the test to the mock registry (via environment or a temporary
.cargo/config) and ensure shipper_cmd() is run with the PATH or CARGO env
updated to pick up the fake cargo; update the invocation around shipper_cmd() /
td.path().join("Cargo.toml") / --state-dir & state_dir so the resume run hits
the mock registry and fake cargo and then assert the stderr contains the skip
message and the deterministic terminal failure.
In `@crates/shipper-cli/tests/bdd_micro_backends.rs`:
- Around line 131-234: The two tests that mutate process-wide environment
(given_dependency_chain_and_no_token_when_preflight_with_micro_backends_then_stable_output
and
given_dependency_chain_and_no_token_when_preflight_with_all_micro_crates_then_stable_output)
must be run serially to avoid cross-test pollution: add the #[serial] attribute
from the serial_test crate above each test and ensure serial_test::serial is
imported in the test module so tests that call env("CARGO_HOME", ...) and
env_remove(...) execute one at a time.
In `@crates/shipper-cli/tests/bdd_parallel.rs`:
- Around line 435-669: Tests that mutate PATH/REAL_CARGO/SHIPPER_* run in
parallel and interfere; mark the tests that set those env vars with #[serial] to
run them serially. Add use serial_test::serial; (near other imports) and
annotate the test functions
given_partially_published_workspace_when_resuming_then_skips_completed_levels,
given_failure_in_one_level_when_continuing_then_stops_subsequent_levels, and
given_max_concurrent_setting_when_publishing_then_limits_parallelism with
#[serial] directly above their fn declarations so each test that rewrites
PATH/SHIPPER_* runs in isolation.
- Around line 231-260: In prepend_fake_cargo and prepend_fake_cargo_failing,
resolve the real cargo path to an absolute path before you mutate PATH (e.g.,
call which/resolution on std::env::var("CARGO") or use
std::env::current_exe/which equivalent) so that real_cargo is an absolute path
(not the bare "cargo") before creating fake_bin and prepending it; keep
referencing real_cargo, fake_cargo_bin_path and
create_fake_cargo_proxy/create_fake_cargo_failing to locate the logic to change.
Also ensure tests that mutate PATH, REAL_CARGO and SHIPPER_CARGO_BIN are
annotated with #[serial] from serial_test to prevent parallel runs.
In `@crates/shipper-cli/tests/bdd_preflight.rs`:
- Around line 261-270: The test currently only asserts aggregate counts in
stdout, which allows package classifications to be swapped; update the
assertions to assert the package/state pairing instead by checking that stdout
contains the specific package lines for core@0.1.0 as already published and
app@0.1.0 as new (e.g., assert stdout contains the "Already published:
core@0.1.0" and "New crates: app@0.1.0" strings) using the existing stdout
variable so the test verifies per-package status rather than just totals.
- Around line 343-353: The tests invoking
shipper_cmd(...).arg("--manifest-path")...preflight must mock the crates
registry to avoid real network calls; call spawn_registry(vec![404, 404], 0)
before the shipper_cmd invocation, pass its base URL to the command via
.arg("--api-base").arg(®istry.base_url), and after the assertion call
registry.join() to clean up; do the same change for the other block referenced
(lines 374-385) so both expected-zero-request tests use the local mock.
In `@crates/shipper-cli/tests/bdd_publish_advanced.rs`:
- Around line 7-9: Add the #[serial] attribute from the serial_test crate to
every test function in this file (i.e., replace or augment each existing #[test]
on the test functions) so tests run serially instead of relying on the comment
workaround; also import the attribute (use serial_test::serial) near the top if
not already present so the attribute resolves. Ensure you add #[serial] to all
test functions in bdd_publish_advanced.rs to match other tests like
bdd_resume.rs and bdd_workflow.rs and prevent concurrent tiny_http server
resource exhaustion on Windows.
In `@crates/shipper-cli/tests/bdd_publish_flow.rs`:
- Around line 196-227: The test
given_unpublished_workspace_when_plan_then_shows_publish_order_without_side_effects
currently only checks presence of "core@0.1.0", "utils@0.1.0", and "app@0.1.0";
add an assertion that enforces their order by locating their positions in the
captured stdout (e.g., use stdout.find("core@0.1.0"),
stdout.find("utils@0.1.0"), stdout.find("app@0.1.0")) and assert pos_core <
pos_utils < pos_app so the plan output respects dependency order while keeping
the existing non-destructive .shipper check.
In `@crates/shipper-cli/tests/bdd_workflow.rs`:
- Around line 1567-1575: Add an assertion to the preflight (dry-run) test to
verify events.jsonl is not created: in the same test block that currently checks
state_dir.join("state.json") and state_dir.join("receipt.json"), also assert
that state_dir.join("events.jsonl").exists() is false with a clear failure
message (e.g., "preflight (dry-run) should not create events.jsonl") so the test
fails if any event log is emitted; this change should be made in the test
function in crates/shipper-cli/tests/bdd_workflow.rs where state_dir is used.
- Around line 22-162: This file duplicates shared BDD helpers—extract the common
helpers into a single test-support module and import it here: move functions and
types like write_file, shipper_cmd, TestRegistry (and its join), spawn_registry,
spawn_doctor_registry, path_sep, create_fake_cargo_proxy, fake_cargo_bin_path,
setup_fake_cargo, and fast_args into the new module, update this test to use
that module's public API (remove local definitions), and update other BDD test
files (the same duplicated regions) to import the same test-support module so
all tests share one implementation.
- Around line 1996-2035: In bdd_ci_github_actions_produces_valid_yaml_steps
replace the multiple substring asserts with a YAML validity check and an insta
snapshot assertion: parse stdout as YAML to ensure it's valid, then call
insta::assert_snapshot!() on the full stdout to capture the template; update the
test to import/enable insta and any YAML parser you already use (or serde_yaml)
and apply the same refactor to the GitLab and CircleCI tests (functions around
lines noted in the review) so structural regressions are caught by snapshots
instead of simple contains() checks.
In `@crates/shipper-cli/tests/cli_e2e.rs`:
- Around line 290-310: The snapshot in crates/shipper-cli/tests/cli_e2e.rs
contains hardcoded git metadata (git_commit, git_branch, git_dirty) that vary
per environment; update the test to normalize those values before asserting by
replacing the concrete values with stable placeholders (e.g. <GIT_COMMIT>,
<GIT_BRANCH>, <GIT_DIRTY>) or by calling the same normalization helper used
previously (the function used in the other test—apply_snapshot_sanitizer or
similar) so the snapshot assertion in this test no longer depends on
environment-specific git_commit/git_branch/git_dirty values.
- Around line 236-256: The snapshot includes environment-specific git fields
(git_commit, git_branch, git_dirty) that aren't being normalized by
normalize_output, so update the normalize_output function in tests/cli_e2e.rs
(the function currently handling " Commit: " and " Branch: ") to also match
and replace lines starting with "git_commit:", "git_branch:", and "git_dirty:"
with stable placeholders (e.g., <GIT_COMMIT>, <GIT_BRANCH>, <GIT_DIRTY>) and
then update the expected snapshot block to use those placeholders instead of
concrete values.
In `@crates/shipper-cli/tests/e2e_doctor.rs`:
- Around line 48-77: The test server can run past a panic and the fixed
expected_requests loop is brittle; modify TestRegistry and spawn_registry to
support graceful shutdown by adding a shared Arc<AtomicBool> (e.g., stop_flag)
stored on TestRegistry, have the spawned thread loop until stop_flag is set
(using recv_timeout in a short interval to remain responsive) instead of relying
on expected_requests, and replace join(self) with a shutdown method that sets
stop_flag and then joins the thread handle; update callers to call that shutdown
method (or ensure Drop triggers it) so the server thread always exits promptly
even if tests panic or request counts change.
- Around line 175-206: The test doctor_reports_missing_token unnecessarily wraps
the command in temp_env::with_vars while the closure only sets variables to None
and the Command already calls env_remove; remove the temp_env::with_vars wrapper
and run the shipper_cmd()... chain directly (keep the same args,
.env("CARGO_HOME", ...), .env_remove("CARGO_REGISTRY_TOKEN") and
.env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") calls) so the subprocess
environment is controlled only via the Command; update the test body accordingly
and leave spawn_registry(1) / registry.join() unchanged.
- Around line 146-173: The test doctor_detects_token_when_set currently mutates
the process environment via temp_env::with_vars which can cause races; instead
remove the temp_env::with_vars wrapper and set the token only on the subprocess
by calling .env("CARGO_REGISTRY_TOKEN", "secret-test-token") on the Command
returned by shipper_cmd() (keep the existing .env("CARGO_HOME", ...) and
.env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") as needed), or alternatively add
#[serial] to the test if global env mutation is desired; update imports/usages
accordingly so the process-wide temp_env is not used.
In `@RELEASE_CHECKLIST_v0.3.0.md`:
- Around line 21-23: Add a blank line before the fenced code block that starts
with "```bash" so the Markdown renders correctly; locate the checklist item line
"- [ ] Publish to crates.io (dry-run first):" and insert a single empty line
between that paragraph and the following "```bash" fence to separate the block
from the preceding text.
- Around line 22-50: The publish checklist is out of dependency order and
missing crates; reorder and update the bash block so crates are topologically
sorted and include the missing workspace crates (shipper-levels,
shipper-storage, shipper-git, shipper-chunking). Specifically, ensure
dependencies like shipper-encrypt, shipper-webhook, shipper-retry,
shipper-duration, and shipper-levels appear before shipper-types; place
shipper-state before shipper-execution-core; place shipper-chunking before
shipper-engine-parallel; and add shipper-storage and shipper-git in the correct
positions according to the workspace dependency graph before running the cargo
publish dry-run list.
In `@RELEASE_NOTES_v0.3.0.md`:
- Around line 20-22: Add a single blank line immediately before the fenced code
block that begins with "```bash" containing the command "shipper publish
--resume-from my-critical-crate" so the code block is surrounded by blank lines
per Markdown best practices; locate the fenced block in RELEASE_NOTES_v0.3.0.md
and insert one empty line above the "```bash" marker.
---
Outside diff comments:
In @.github/workflows/fuzz.yml:
- Around line 82-100: The fuzz run step currently uses continue-on-error: true
and has no id, so non-crasher failures are hidden; give the "Run fuzz target"
step a stable id (e.g., id: run_fuzz_target), keep continue-on-error: true if
you want to collect artifacts, then in the "Check for crashers" step inspect
both the artifacts directory and the run step outcome (check
steps.run_fuzz_target.outcome != 'success') and emit crashers_found=true plus an
error if either crash artifacts exist or the run step failed (and set
crashers_found=false only when no artifacts exist and the run step outcome is
success) so build/toolchain failures are surfaced.
In `@crates/shipper-cargo-failure/src/lib.rs`:
- Around line 26-47: RETRYABLE_PATTERNS currently contains the bare substring
"500" which can false-match numeric contexts (e.g., ports or versions); update
the RETRYABLE_PATTERNS constant to use a more specific match for HTTP 500 (for
example change "500" to " 500 " or "500 " or use a regex word-boundary variant
like r"\b500\b") so only standalone status codes are matched; modify the entry
for "500" (and similarly "502","503","504" if desired) inside the
RETRYABLE_PATTERNS array or switch to a regex-based check where the matching
code consults the new pattern to avoid the documented false positives.
In `@ROADMAP.md`:
- Line 7: Update the Table of Contents entry that currently reads "Current
Release: v0.2.0" (and its anchor "(`#current-release-v020`)") to match the
document's new release string "v0.3.0-rc.1" and update the anchor to the
corresponding heading anchor (e.g., "(`#current-release-v030-rc1`)" or whatever
the generated slug for the "Current Release: v0.3.0-rc.1" heading is) so the ToC
label and link both reflect v0.3.0-rc.1.
- Around line 560-563: The version table currently marks v0.2.0 as "Current";
change the table so the "Version" cell shows v0.3.0-rc.1 with "Status" set to
**Current** (replace the v0.2.0 row), and update the v0.2.0 row to "Released"
under Status with its existing Theme/Notes; ensure the table includes
v0.3.0-rc.1 Theme/Notes (e.g., release candidate details) and that rows remain
properly aligned (refer to the Version entries v0.3.0-rc.1, v0.2.0 as unique
identifiers).
---
Duplicate comments:
In `@crates/shipper-cargo-failure/CLAUDE.md`:
- Around line 1-29: Replace template placeholders and fix formatting in
CLAUDE.md: substitute $name with "shipper-cargo-failure", set $entryPoint to the
crate's primary entry (e.g., lib.rs or main.rs as applicable), and update
$rootDocs to the actual relative docs path; correct the malformed shell code
fence (replace the stray control character and use a proper ```bash block) and
ensure the cargo commands are on separate lines; normalize backslashes in the
workspace path or use forward slashes for consistency and fix any
punctuation/typo issues so the file matches the other CLAUDE.md files' format.
In `@crates/shipper-cargo/CLAUDE.md`:
- Around line 1-29: The CLAUDE.md uses placeholders and a broken code fence;
replace every placeholder ($name, $entryPoint, $rootDocs) with the actual crate
name "shipper-cargo" (and the correct primary entry if known), fix the malformed
code block (remove the stray control char and use a proper triple-backtick
fenced block with "bash"), clean up Windows path escaping (use consistent path
formatting or a relative path), and apply the same formatting fixes you applied
to other CLAUDE.md files so headers, bullets, and the "Useful commands" block
are stable and readable; locate and edit the CLAUDE.md in crates/shipper-cargo
and update the placeholders and code fence accordingly.
| "crates/shipper-output-sanitizer", | ||
| "crates/shipper-levels", | ||
| "crates/shipper-execution-core", | ||
| "crates/shipper-config-runtime", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Inconsistent indentation in workspace members list.
Lines 29-32 use 4-space indentation while the rest of the members use 2-space indentation.
📝 Proposed fix
"crates/shipper-policy",
- "crates/shipper-output-sanitizer",
- "crates/shipper-levels",
- "crates/shipper-execution-core",
- "crates/shipper-config-runtime",
+ "crates/shipper-output-sanitizer",
+ "crates/shipper-levels",
+ "crates/shipper-execution-core",
+ "crates/shipper-config-runtime",
]📝 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.
| "crates/shipper-output-sanitizer", | |
| "crates/shipper-levels", | |
| "crates/shipper-execution-core", | |
| "crates/shipper-config-runtime", | |
| "crates/shipper-output-sanitizer", | |
| "crates/shipper-levels", | |
| "crates/shipper-execution-core", | |
| "crates/shipper-config-runtime", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Cargo.toml` around lines 29 - 32, The workspace members list in Cargo.toml
has inconsistent indentation for the entries "crates/shipper-output-sanitizer",
"crates/shipper-levels", "crates/shipper-execution-core", and
"crates/shipper-config-runtime" (they use 4 spaces while others use 2); update
those four lines to use the same 2-space indentation as the rest of the members
list so the workspace members block is consistently formatted.
| # CLAUDE.md | ||
|
|
||
| This file provides agent-specific guidance for working in crate $name. | ||
|
|
||
| ## Scope | ||
|
|
||
| - Crate: $name | ||
| - Path: crates/shipper-auth | ||
| - Workspace root: h:\Code\Rust\shipper | ||
| - Primary entry: $entryPoint | ||
|
|
||
| ## Useful commands | ||
|
|
||
| `ash | ||
| cargo check -p shipper-auth | ||
| cargo test -p shipper-auth | ||
| cargo test -p shipper-auth --all-features | ||
| cargo fmt -p shipper-auth | ||
| cargo clippy -p shipper-auth --all-targets --all-features -- -D warnings | ||
| ` | ||
|
|
||
| ## Context | ||
|
|
||
| - Keep changes small and targeted to the crate’s existing abstractions. | ||
| - Preserve public API compatibility unless the request explicitly asks for breaking changes. | ||
| - When touching serialization or state formats, update tests and related snapshots in the same crate. | ||
| - Prefer using existing fixtures and helpers rather than introducing inline test data. | ||
|
|
||
| For full workspace guidance, see [$rootDocs\CLAUDE.md](H:\Code\Rust\shipper\CLAUDE.md). No newline at end of file |
There was a problem hiding this comment.
Same template and formatting issues as other CLAUDE.md files.
This file has the same issues identified in crates/shipper-chunking/CLAUDE.md:
- Unsubstituted placeholders (
$name,$entryPoint,$rootDocs) - Code block syntax (
`ashinstead of```bash) - Windows path separators
- Missing trailing newline
Please apply the same pattern of fixes as suggested for the other CLAUDE.md files, substituting shipper-auth for the crate name.
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 29-29: Files should end with a single newline character
(MD047, single-trailing-newline)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-auth/CLAUDE.md` around lines 1 - 29, Replace unsubstituted
placeholders ($name, $entryPoint, $rootDocs) with the crate-specific values for
shipper-auth (use "shipper-auth" for crate name and the crate's actual primary
entry point), fix the fenced code block marker from "`ash" to a proper
triple-backtick block for bash, convert Windows path separators in the
workspace/root path to POSIX-style or neutral notation, and ensure the file ends
with a trailing newline; update the header and context text accordingly so the
CLAUDE.md content matches the other corrected crate CLAUDE.md files.
| /// Mask a token for safe display. | ||
| /// | ||
| /// Shows the first 4 and last 4 characters, replacing the middle with | ||
| /// `****`. Tokens of 8 characters or fewer are fully masked. | ||
| pub fn mask_token(token: &str) -> String { | ||
| if token.len() <= 8 { | ||
| return "*".repeat(token.len()); | ||
| } | ||
| format!("{}****{}", &token[..4], &token[token.len() - 4..]) |
There was a problem hiding this comment.
Handle Unicode tokens without byte slicing.
Line 266 slices &str by byte offsets. Any token longer than 8 bytes whose 4th or (len - 4)th byte is not a UTF-8 boundary will panic, and the new Unicode-token coverage makes that input class reachable.
💡 Proposed fix
pub fn mask_token(token: &str) -> String {
- if token.len() <= 8 {
- return "*".repeat(token.len());
- }
- format!("{}****{}", &token[..4], &token[token.len() - 4..])
+ let chars: Vec<char> = token.chars().collect();
+ if chars.len() <= 8 {
+ return "*".repeat(chars.len());
+ }
+ let head: String = chars[..4].iter().collect();
+ let tail: String = chars[chars.len() - 4..].iter().collect();
+ format!("{head}****{tail}")
}Also applies to: 1352-1367
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-auth/src/lib.rs` around lines 258 - 266, The mask_token
function currently slices &str by byte offsets which can panic on Unicode;
update mask_token to operate on Unicode scalar values via .chars(): compute
char_count = token.chars().count(), if char_count <= 8 return "*" repeated
char_count, otherwise build the masked string by collecting the first four chars
(token.chars().take(4).collect::<String>()) and the last four chars
(token.chars().rev().take(4).collect::<String>() then reverse that collected
tail) and concatenate with "****"; apply the same char-safe approach to the
other occurrence noted (the block around symbols handling at the second
location).
| /// Detect whether trusted publishing (GitHub Actions OIDC) is available. | ||
| /// | ||
| /// Returns `true` when both `ACTIONS_ID_TOKEN_REQUEST_URL` and | ||
| /// `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables are set, | ||
| /// indicating a GitHub Actions environment with OIDC token support. | ||
| pub fn is_trusted_publishing_available() -> bool { | ||
| env::var_os("ACTIONS_ID_TOKEN_REQUEST_URL").is_some() | ||
| && env::var_os("ACTIONS_ID_TOKEN_REQUEST_TOKEN").is_some() |
There was a problem hiding this comment.
Reject empty OIDC env values.
Lines 275-276 currently return true when both GitHub Actions OIDC variables are present but empty. That produces a false positive for trusted publishing availability even though the token request cannot succeed.
Also applies to: 1531-1543
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-auth/src/lib.rs` around lines 269 - 276, The current
is_trusted_publishing_available function treats empty OIDC env vars as present;
change the check to ensure both ACTIONS_ID_TOKEN_REQUEST_URL and
ACTIONS_ID_TOKEN_REQUEST_TOKEN are non-empty strings (e.g., use
env::var_os(...).and_then(|s| if s.is_empty() { None } else { Some(s)
}).is_some() or use env::var(...) and verify non-empty), so the function only
returns true when both env vars exist and are not empty; apply the same
non-empty validation to the other identical check found later in the file (the
duplicate OIDC availability check around lines ~1531-1543).
| /// 3. Doctor detects CARGO_REGISTRY_TOKEN when set | ||
| #[test] | ||
| fn doctor_detects_token_when_set() { | ||
| let td = tempdir().expect("tempdir"); | ||
| create_workspace(td.path()); | ||
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | ||
|
|
||
| let registry = spawn_registry(1); | ||
|
|
||
| temp_env::with_vars( | ||
| [("CARGO_REGISTRY_TOKEN", Some("secret-test-token"))], | ||
| || { | ||
| shipper_cmd() | ||
| .arg("--manifest-path") | ||
| .arg(td.path().join("Cargo.toml")) | ||
| .arg("--api-base") | ||
| .arg(®istry.base_url) | ||
| .arg("doctor") | ||
| .env("CARGO_HOME", td.path().join("cargo-home")) | ||
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | ||
| .assert() | ||
| .success() | ||
| .stdout(contains("auth_type: token (detected)")); | ||
| }, | ||
| ); | ||
|
|
||
| registry.join(); | ||
| } |
There was a problem hiding this comment.
Missing #[serial] attribute, or simplify by using Command::env() instead of temp_env.
This test uses temp_env::with_vars which modifies the current process's environment, creating potential race conditions with parallel tests. Per coding guidelines: "Use #[serial] from serial_test to isolate tests that mutate environment variables."
However, since only the subprocess needs CARGO_REGISTRY_TOKEN, you can avoid global mutation entirely by using Command's .env() method:
Proposed fix using Command::env()
#[test]
fn doctor_detects_token_when_set() {
let td = tempdir().expect("tempdir");
create_workspace(td.path());
fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir");
let registry = spawn_registry(1);
- temp_env::with_vars(
- [("CARGO_REGISTRY_TOKEN", Some("secret-test-token"))],
- || {
- shipper_cmd()
- .arg("--manifest-path")
- .arg(td.path().join("Cargo.toml"))
- .arg("--api-base")
- .arg(®istry.base_url)
- .arg("doctor")
- .env("CARGO_HOME", td.path().join("cargo-home"))
- .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN")
- .assert()
- .success()
- .stdout(contains("auth_type: token (detected)"));
- },
- );
+ shipper_cmd()
+ .arg("--manifest-path")
+ .arg(td.path().join("Cargo.toml"))
+ .arg("--api-base")
+ .arg(®istry.base_url)
+ .arg("doctor")
+ .env("CARGO_HOME", td.path().join("cargo-home"))
+ .env("CARGO_REGISTRY_TOKEN", "secret-test-token")
+ .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN")
+ .assert()
+ .success()
+ .stdout(contains("auth_type: token (detected)"));
registry.join();
}As per coding guidelines: "Use #[serial] from serial_test to isolate tests that mutate environment variables."
📝 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.
| /// 3. Doctor detects CARGO_REGISTRY_TOKEN when set | |
| #[test] | |
| fn doctor_detects_token_when_set() { | |
| let td = tempdir().expect("tempdir"); | |
| create_workspace(td.path()); | |
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | |
| let registry = spawn_registry(1); | |
| temp_env::with_vars( | |
| [("CARGO_REGISTRY_TOKEN", Some("secret-test-token"))], | |
| || { | |
| shipper_cmd() | |
| .arg("--manifest-path") | |
| .arg(td.path().join("Cargo.toml")) | |
| .arg("--api-base") | |
| .arg(®istry.base_url) | |
| .arg("doctor") | |
| .env("CARGO_HOME", td.path().join("cargo-home")) | |
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | |
| .assert() | |
| .success() | |
| .stdout(contains("auth_type: token (detected)")); | |
| }, | |
| ); | |
| registry.join(); | |
| } | |
| /// 3. Doctor detects CARGO_REGISTRY_TOKEN when set | |
| #[test] | |
| fn doctor_detects_token_when_set() { | |
| let td = tempdir().expect("tempdir"); | |
| create_workspace(td.path()); | |
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | |
| let registry = spawn_registry(1); | |
| shipper_cmd() | |
| .arg("--manifest-path") | |
| .arg(td.path().join("Cargo.toml")) | |
| .arg("--api-base") | |
| .arg(®istry.base_url) | |
| .arg("doctor") | |
| .env("CARGO_HOME", td.path().join("cargo-home")) | |
| .env("CARGO_REGISTRY_TOKEN", "secret-test-token") | |
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | |
| .assert() | |
| .success() | |
| .stdout(contains("auth_type: token (detected)")); | |
| registry.join(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/tests/e2e_doctor.rs` around lines 146 - 173, The test
doctor_detects_token_when_set currently mutates the process environment via
temp_env::with_vars which can cause races; instead remove the
temp_env::with_vars wrapper and set the token only on the subprocess by calling
.env("CARGO_REGISTRY_TOKEN", "secret-test-token") on the Command returned by
shipper_cmd() (keep the existing .env("CARGO_HOME", ...) and
.env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") as needed), or alternatively add
#[serial] to the test if global env mutation is desired; update imports/usages
accordingly so the process-wide temp_env is not used.
| /// 4. Doctor reports missing token when not set | ||
| #[test] | ||
| fn doctor_reports_missing_token() { | ||
| let td = tempdir().expect("tempdir"); | ||
| create_workspace(td.path()); | ||
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | ||
|
|
||
| let registry = spawn_registry(1); | ||
|
|
||
| temp_env::with_vars( | ||
| [ | ||
| ("CARGO_REGISTRY_TOKEN", None::<&str>), | ||
| ("CARGO_REGISTRIES_CRATES_IO_TOKEN", None::<&str>), | ||
| ], | ||
| || { | ||
| shipper_cmd() | ||
| .arg("--manifest-path") | ||
| .arg(td.path().join("Cargo.toml")) | ||
| .arg("--api-base") | ||
| .arg(®istry.base_url) | ||
| .arg("doctor") | ||
| .env("CARGO_HOME", td.path().join("cargo-home")) | ||
| .env_remove("CARGO_REGISTRY_TOKEN") | ||
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | ||
| .assert() | ||
| .success() | ||
| .stdout(contains("NONE FOUND")); | ||
| }, | ||
| ); | ||
|
|
||
| registry.join(); | ||
| } |
There was a problem hiding this comment.
Same issue: temp_env::with_vars used unnecessarily.
This test also uses temp_env::with_vars but the closure only sets vars to None and then uses env_remove on the Command anyway. The temp_env wrapper is redundant here since env_remove already handles the subprocess environment.
Proposed fix to remove unnecessary temp_env wrapper
#[test]
fn doctor_reports_missing_token() {
let td = tempdir().expect("tempdir");
create_workspace(td.path());
fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir");
let registry = spawn_registry(1);
- temp_env::with_vars(
- [
- ("CARGO_REGISTRY_TOKEN", None::<&str>),
- ("CARGO_REGISTRIES_CRATES_IO_TOKEN", None::<&str>),
- ],
- || {
- shipper_cmd()
- .arg("--manifest-path")
- .arg(td.path().join("Cargo.toml"))
- .arg("--api-base")
- .arg(®istry.base_url)
- .arg("doctor")
- .env("CARGO_HOME", td.path().join("cargo-home"))
- .env_remove("CARGO_REGISTRY_TOKEN")
- .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN")
- .assert()
- .success()
- .stdout(contains("NONE FOUND"));
- },
- );
+ shipper_cmd()
+ .arg("--manifest-path")
+ .arg(td.path().join("Cargo.toml"))
+ .arg("--api-base")
+ .arg(®istry.base_url)
+ .arg("doctor")
+ .env("CARGO_HOME", td.path().join("cargo-home"))
+ .env_remove("CARGO_REGISTRY_TOKEN")
+ .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN")
+ .assert()
+ .success()
+ .stdout(contains("NONE FOUND"));
registry.join();
}📝 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.
| /// 4. Doctor reports missing token when not set | |
| #[test] | |
| fn doctor_reports_missing_token() { | |
| let td = tempdir().expect("tempdir"); | |
| create_workspace(td.path()); | |
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | |
| let registry = spawn_registry(1); | |
| temp_env::with_vars( | |
| [ | |
| ("CARGO_REGISTRY_TOKEN", None::<&str>), | |
| ("CARGO_REGISTRIES_CRATES_IO_TOKEN", None::<&str>), | |
| ], | |
| || { | |
| shipper_cmd() | |
| .arg("--manifest-path") | |
| .arg(td.path().join("Cargo.toml")) | |
| .arg("--api-base") | |
| .arg(®istry.base_url) | |
| .arg("doctor") | |
| .env("CARGO_HOME", td.path().join("cargo-home")) | |
| .env_remove("CARGO_REGISTRY_TOKEN") | |
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | |
| .assert() | |
| .success() | |
| .stdout(contains("NONE FOUND")); | |
| }, | |
| ); | |
| registry.join(); | |
| } | |
| /// 4. Doctor reports missing token when not set | |
| #[test] | |
| fn doctor_reports_missing_token() { | |
| let td = tempdir().expect("tempdir"); | |
| create_workspace(td.path()); | |
| fs::create_dir_all(td.path().join("cargo-home")).expect("mkdir"); | |
| let registry = spawn_registry(1); | |
| shipper_cmd() | |
| .arg("--manifest-path") | |
| .arg(td.path().join("Cargo.toml")) | |
| .arg("--api-base") | |
| .arg(®istry.base_url) | |
| .arg("doctor") | |
| .env("CARGO_HOME", td.path().join("cargo-home")) | |
| .env_remove("CARGO_REGISTRY_TOKEN") | |
| .env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") | |
| .assert() | |
| .success() | |
| .stdout(contains("NONE FOUND")); | |
| registry.join(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/tests/e2e_doctor.rs` around lines 175 - 206, The test
doctor_reports_missing_token unnecessarily wraps the command in
temp_env::with_vars while the closure only sets variables to None and the
Command already calls env_remove; remove the temp_env::with_vars wrapper and run
the shipper_cmd()... chain directly (keep the same args, .env("CARGO_HOME",
...), .env_remove("CARGO_REGISTRY_TOKEN") and
.env_remove("CARGO_REGISTRIES_CRATES_IO_TOKEN") calls) so the subprocess
environment is controlled only via the Command; update the test body accordingly
and leave spawn_registry(1) / registry.join() unchanged.
| - [ ] Publish to crates.io (dry-run first): | ||
| ```bash | ||
| cargo publish -p shipper-types --dry-run |
There was a problem hiding this comment.
Minor formatting: Add blank line before fenced code block.
Per Markdown best practices, fenced code blocks should be surrounded by blank lines for proper rendering across all Markdown parsers.
📝 Proposed fix
- [ ] Publish to crates.io (dry-run first):
+
```bash📝 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.
| - [ ] Publish to crates.io (dry-run first): | |
| ```bash | |
| cargo publish -p shipper-types --dry-run | |
| - [ ] Publish to crates.io (dry-run first): |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 22-22: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@RELEASE_CHECKLIST_v0.3.0.md` around lines 21 - 23, Add a blank line before
the fenced code block that starts with "```bash" so the Markdown renders
correctly; locate the checklist item line "- [ ] Publish to crates.io (dry-run
first):" and insert a single empty line between that paragraph and the following
"```bash" fence to separate the block from the preceding text.
| ```bash | ||
| shipper publish --resume-from my-critical-crate | ||
| ``` |
There was a problem hiding this comment.
Minor formatting: Add blank line before fenced code block.
Per Markdown best practices, fenced code blocks should be surrounded by blank lines.
📝 Proposed fix
Interrupted publishes can now be resumed from any specific package, not just the last failed one. This gives you total control over the recovery process.
+
```bash📝 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.
| ```bash | |
| shipper publish --resume-from my-critical-crate | |
| ``` | |
| Interrupted publishes can now be resumed from any specific package, not just the last failed one. This gives you total control over the recovery process. | |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 22-22: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@RELEASE_NOTES_v0.3.0.md` around lines 20 - 22, Add a single blank line
immediately before the fenced code block that begins with "```bash" containing
the command "shipper publish --resume-from my-critical-crate" so the code block
is surrounded by blank lines per Markdown best practices; locate the fenced
block in RELEASE_NOTES_v0.3.0.md and insert one empty line above the "```bash"
marker.
| /// Suppress informational output | ||
| #[arg(short, long, global = true)] | ||
| quiet: bool, |
There was a problem hiding this comment.
Keep warnings visible under --quiet.
Line 178 says --quiet suppresses informational output, but CliReporter::warn is gated on the same flag. With the new CI snippets using shipper publish --quiet, retry/auth/connectivity warnings disappear from logs too. Either keep warnings printing or rename/document the flag as silencing warnings as well.
Possible fix
- /// Suppress informational output
+ /// Suppress informational output; warnings and errors are still shown
#[arg(short, long, global = true)]
quiet: bool,
@@
fn warn(&mut self, msg: &str) {
- if !self.quiet {
- eprintln!("[warn] {msg}");
- }
+ eprintln!("[warn] {msg}");
}Also applies to: 256-270
| for reg in target_registries { | ||
| if opts.registries.len() > 1 { | ||
| println!( | ||
| "\n🚀 Publishing to registry: {} ({})", | ||
| reg.name, reg.api_base | ||
| ); | ||
| } | ||
|
|
||
| let receipt = engine::run_publish(&planned, &opts, &mut reporter)?; | ||
| let mut current_planned = planned.clone(); | ||
| current_planned.plan.registry = reg.clone(); | ||
|
|
||
| progress.finish(); | ||
| let mut current_opts = opts.clone(); | ||
| // Segregate state dir by registry name if multiple registries | ||
| if opts.registries.len() > 1 { | ||
| current_opts.state_dir = opts.state_dir.join(®.name); | ||
| } | ||
|
|
||
| print_receipt( | ||
| &receipt, | ||
| &planned.workspace_root, | ||
| &opts.state_dir, | ||
| &cli.format, | ||
| ); | ||
| let total_packages = current_planned.plan.packages.len(); | ||
| let mut progress = ProgressReporter::new(total_packages, cli.quiet); | ||
|
|
||
| // Show initial progress if we have packages | ||
| if total_packages > 0 { | ||
| let first_pkg = ¤t_planned.plan.packages[0]; | ||
| progress.set_package(1, &first_pkg.name, &first_pkg.version); | ||
| } | ||
|
|
||
| let receipt = engine::run_publish(¤t_planned, ¤t_opts, &mut reporter)?; | ||
|
|
||
| progress.finish(); | ||
|
|
||
| print_receipt( | ||
| &receipt, | ||
| ¤t_planned.workspace_root, | ||
| ¤t_opts.state_dir, | ||
| &cli.format, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Aggregate multi-registry receipts before printing JSON.
When --format json is combined with more than one target registry, these loops emit a human banner and then a separate pretty-printed receipt per registry. That is not a single valid JSON document, so machine consumers of the CLI output break. Please gate the banners to text mode and emit one JSON array/object for the multi-registry case. A regression test around publish/resume --registries a,b --format json would lock this down.
Also applies to: 495-530
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/src/main.rs` around lines 450 - 486, The loop over
target_registries currently prints per-registry banners/progress and calls
print_receipt per registry, which breaks JSON output when cli.format == "json";
instead collect each registry's receipt (from engine::run_publish) into a Vec
and, after the loop, if cli.format is json serialize and print a single JSON
array/object (suppress banners/progress during iteration), otherwise keep the
existing human-mode behavior (show banner/progress and call print_receipt per
registry). Apply the same change to the analogous publish/resume loop (the other
target_registries loop that also calls ProgressReporter, engine::run_publish and
print_receipt).
| let total_packages = current_planned.plan.packages.len(); | ||
| let mut progress = ProgressReporter::new(total_packages, cli.quiet); | ||
|
|
||
| // Show initial progress if we have packages | ||
| if total_packages > 0 { | ||
| let first_pkg = ¤t_planned.plan.packages[0]; | ||
| progress.set_package(1, &first_pkg.name, &first_pkg.version); | ||
| } | ||
|
|
||
| let receipt = engine::run_publish(¤t_planned, ¤t_opts, &mut reporter)?; | ||
|
|
||
| progress.finish(); | ||
|
|
There was a problem hiding this comment.
This progress reporter never advances past the first package.
The only update here is set_package(1, ...) before calling engine::run_publish / engine::run_resume. Because this ProgressReporter instance is never driven again in this function, multi-package runs will sit on package 1 until finish(). Wire it to real publish events/callbacks or drop the progress bar until it can reflect actual state.
Also applies to: 511-523
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/src/main.rs` around lines 467 - 479, The ProgressReporter
is being preset to package 1 but never updated, so multi-package runs stall;
either remove the pre-call to ProgressReporter::set_package(...) and let the
engine drive progress via the existing reporter, or explicitly wire
ProgressReporter into the publish/resume flow so engine::run_publish and
engine::run_resume (or the reporter callbacks they use) call
ProgressReporter::set_package/update as packages change; locate the
ProgressReporter instantiation and the set_package call near
current_planned/current_opts/reporter and either delete that initial
set_package(1, ...) or pass &mut progress (or register progress callbacks) into
engine::run_publish/run_resume so the progress bar advances for each package
before calling progress.finish().
| let target_registries = if opts.registries.is_empty() { | ||
| vec![planned.plan.registry.clone()] | ||
| } else { | ||
| opts.registries.clone() | ||
| }; | ||
|
|
||
| for reg in target_registries { | ||
| if opts.registries.len() > 1 { | ||
| println!( | ||
| "\n🩺 Diagnostics for registry: {} ({})", | ||
| reg.name, reg.api_base | ||
| ); | ||
| } | ||
| let mut current_planned = planned.clone(); | ||
| current_planned.plan.registry = reg; | ||
| run_doctor(¤t_planned, &opts, &mut reporter)?; | ||
| } |
There was a problem hiding this comment.
Pass registry-scoped RuntimeOptions into run_doctor.
publish and resume re-root state_dir under state_dir/<registry> when more than one registry is targeted, but Line 564 still calls run_doctor with the original opts. In multi-registry mode that means the permission/path checks run against the shared base directory, not the registry-specific directory the runtime actually uses.
Possible fix
for reg in target_registries {
if opts.registries.len() > 1 {
println!(
"\n🩺 Diagnostics for registry: {} ({})",
reg.name, reg.api_base
);
}
let mut current_planned = planned.clone();
current_planned.plan.registry = reg;
- run_doctor(¤t_planned, &opts, &mut reporter)?;
+ let mut current_opts = opts.clone();
+ if opts.registries.len() > 1 {
+ current_opts.state_dir =
+ opts.state_dir.join(¤t_planned.plan.registry.name);
+ }
+ run_doctor(¤t_planned, ¤t_opts, &mut reporter)?;
}📝 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 target_registries = if opts.registries.is_empty() { | |
| vec![planned.plan.registry.clone()] | |
| } else { | |
| opts.registries.clone() | |
| }; | |
| for reg in target_registries { | |
| if opts.registries.len() > 1 { | |
| println!( | |
| "\n🩺 Diagnostics for registry: {} ({})", | |
| reg.name, reg.api_base | |
| ); | |
| } | |
| let mut current_planned = planned.clone(); | |
| current_planned.plan.registry = reg; | |
| run_doctor(¤t_planned, &opts, &mut reporter)?; | |
| } | |
| let target_registries = if opts.registries.is_empty() { | |
| vec![planned.plan.registry.clone()] | |
| } else { | |
| opts.registries.clone() | |
| }; | |
| for reg in target_registries { | |
| if opts.registries.len() > 1 { | |
| println!( | |
| "\n🩺 Diagnostics for registry: {} ({})", | |
| reg.name, reg.api_base | |
| ); | |
| } | |
| let mut current_planned = planned.clone(); | |
| current_planned.plan.registry = reg; | |
| let mut current_opts = opts.clone(); | |
| if opts.registries.len() > 1 { | |
| current_opts.state_dir = | |
| opts.state_dir.join(¤t_planned.plan.registry.name); | |
| } | |
| run_doctor(¤t_planned, ¤t_opts, &mut reporter)?; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/src/main.rs` around lines 549 - 565, The loop uses the
original opts when calling run_doctor so checks run against the shared state_dir
instead of the registry-scoped directory; create a registry-scoped
RuntimeOptions (clone opts into a new variable, update its state_dir to re-root
under state_dir/<registry> or the same logic used by publish/resume) and pass
that into run_doctor (use the same scope where current_planned.plan.registry is
set). Ensure you reference and update the RuntimeOptions instance used for
runtime checks (opts -> registry_opts) before calling
run_doctor(¤t_planned, ®istry_opts, &mut reporter).
| // Identify all directories to clean (base + any registry subdirs) | ||
| let mut dirs_to_clean = vec![abs_state.clone()]; | ||
| if let Ok(entries) = std::fs::read_dir(&abs_state) { | ||
| for entry in entries.flatten() { | ||
| if let Ok(file_type) = entry.file_type() | ||
| && file_type.is_dir() | ||
| && entry.file_name() != "cache" | ||
| { | ||
| dirs_to_clean.push(entry.path()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for dir in dirs_to_clean { | ||
| clean_single_dir(&dir, workspace_root, keep_receipt, force)?; | ||
| } | ||
|
|
||
| println!("Clean complete"); | ||
| Ok(()) |
There was a problem hiding this comment.
Do a lock-validation pass before deleting anything.
run_clean starts mutating directories as it iterates dirs_to_clean. Because the base state directory is always processed first, a later active lock in a registry subdirectory will make the command fail after earlier state was already deleted. Please split this into a read-only lock scan first and a deletion pass second, so clean is all-or-nothing unless --force is set. A tempdir test with an unlocked root and a locked child directory would cover this path.
Also applies to: 1489-1516
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/src/main.rs` around lines 1457 - 1475, The cleanup
currently deletes directories as it iterates dirs_to_clean which can leave
partial state if a later subdirectory has an active lock; before calling
clean_single_dir, add a read-only lock-validation pass that scans the same
dirs_to_clean (derived from abs_state and registry subdirs) and checks for
active locks (unless force is true), returning an error and aborting before any
mutation if any lock is held; only if the validation passes (or force is set)
proceed to the existing deletion loop that calls
clean_single_dir(workspace_root, keep_receipt, force). Apply the same two-phase
validation-then-delete change for the other block noted around lines 1489-1516.
| fn bdd_ci_github_actions_produces_valid_yaml_steps() { | ||
| let td = tempdir().expect("tempdir"); | ||
| create_single_crate_workspace(td.path()); | ||
|
|
||
| let output = shipper_cmd() | ||
| .arg("--manifest-path") | ||
| .arg(td.path().join("Cargo.toml")) | ||
| .arg("ci") | ||
| .arg("github-actions") | ||
| .assert() | ||
| .success() | ||
| .get_output() | ||
| .stdout | ||
| .clone(); | ||
|
|
||
| let stdout = String::from_utf8(output).expect("utf8"); | ||
|
|
||
| // Then: output contains GitHub Actions YAML structure | ||
| assert!( | ||
| stdout.contains("- name:"), | ||
| "expected '- name:' YAML step marker, got: {stdout}" | ||
| ); | ||
| assert!( | ||
| stdout.contains("uses:"), | ||
| "expected 'uses:' action reference, got: {stdout}" | ||
| ); | ||
| assert!( | ||
| stdout.contains("shipper publish"), | ||
| "expected 'shipper publish' command reference, got: {stdout}" | ||
| ); | ||
| assert!( | ||
| stdout.contains("CARGO_REGISTRY_TOKEN"), | ||
| "expected CARGO_REGISTRY_TOKEN env var reference, got: {stdout}" | ||
| ); | ||
| // And: output starts with a comment header | ||
| assert!( | ||
| stdout.starts_with("# GitHub Actions"), | ||
| "expected output to start with '# GitHub Actions' comment, got: {stdout}" | ||
| ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and its size
wc -l crates/shipper-cli/tests/bdd_workflow.rsRepository: EffortlessMetrics/shipper
Length of output: 114
🏁 Script executed:
# Read the specific function mentioned in the review (lines 1996-2035)
sed -n '1996,2035p' crates/shipper-cli/tests/bdd_workflow.rsRepository: EffortlessMetrics/shipper
Length of output: 1435
🏁 Script executed:
# Check the other line ranges mentioned (2766-2802 and 2805-2836)
sed -n '2766,2802p' crates/shipper-cli/tests/bdd_workflow.rsRepository: EffortlessMetrics/shipper
Length of output: 1247
🏁 Script executed:
sed -n '2805,2836p' crates/shipper-cli/tests/bdd_workflow.rsRepository: EffortlessMetrics/shipper
Length of output: 1030
🏁 Script executed:
# Check if insta is already a dependency
fd Cargo.toml | xargs grep -l "insta" 2>/dev/nullRepository: EffortlessMetrics/shipper
Length of output: 1143
🏁 Script executed:
# Check the workspace root Cargo.toml and shipper-cli Cargo.toml for dependencies
cat crates/shipper-cli/Cargo.tomlRepository: EffortlessMetrics/shipper
Length of output: 1962
Replace substring assertions with snapshot testing using insta.
The current assertions will not catch structural regressions in CI templates. Validate YAML validity once, then snapshot the full output so CI template changes remain intentional.
Replace the multiple assert!(stdout.contains(...)) checks with a YAML validity check and insta::assert_snapshot!(). This aligns with the coding guideline to use snapshot testing with insta for test assertions.
Example refactor
let stdout = String::from_utf8(output).expect("utf8");
-assert!(stdout.contains("- name:"));
-assert!(stdout.contains("uses:"));
-assert!(stdout.contains("shipper publish"));
-assert!(stdout.contains("CARGO_REGISTRY_TOKEN"));
+let _: serde_yaml::Value = serde_yaml::from_str(&stdout).expect("valid YAML");
+insta::assert_snapshot!("github_actions_ci_template", stdout);Also applies to the GitLab (lines 2766-2802) and CircleCI (lines 2805-2836) template tests.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/shipper-cli/tests/bdd_workflow.rs` around lines 1996 - 2035, In
bdd_ci_github_actions_produces_valid_yaml_steps replace the multiple substring
asserts with a YAML validity check and an insta snapshot assertion: parse stdout
as YAML to ensure it's valid, then call insta::assert_snapshot!() on the full
stdout to capture the template; update the test to import/enable insta and any
YAML parser you already use (or serde_yaml) and apply the same refactor to the
GitLab and CircleCI tests (functions around lines noted in the review) so
structural regressions are caught by snapshots instead of simple contains()
checks.
…klist - Increase mock server recv_timeout from 5s to 30s across all test files to prevent flaky failures under cargo llvm-cov instrumentation - Add deterministic timeout test via fake cargo shim (shipper-cargo) - Normalize git_commit/git_branch/git_dirty in cli_e2e doctor snapshots so they don't break after every commit - Correct release checklist publish order with layered dependencies - Narrow .gitignore *.log to test_full.log
Summary
This PR prepares Shipper for its first major Release Candidate (v0.3.0-rc.1). It consolidates several months of architectural improvements, performance optimizations, and enterprise-grade feature build-outs.
🚀 Key Features
🛠️ Maintenance
and\ 0.10, \ oml\ 1.0, \dirs\ 6.0, \indicatif\ 0.18).
✅ Quality Assurance