Skip to content

feat(cargo-each): run a command per workspace member with cargo-style selection - #61

Open
martin-kolinek wants to merge 21 commits into
mainfrom
cargo-each
Open

feat(cargo-each): run a command per workspace member with cargo-style selection#61
martin-kolinek wants to merge 21 commits into
mainfrom
cargo-each

Conversation

@martin-kolinek

@martin-kolinek martin-kolinek commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Introduces cargo-each, a cargo-native subcommand that runs a command over a cargo-style selection of workspace members. It exists to collapse the hand-rolled impact-scoping PowerShell duplicated across ~26 cargo-anvil check recipes into a single line.

Draft. This PR is the tool + design doc + tests only. The cargo-anvil recipe refactor, crate logo, format/clippy/spellcheck, assert_cmd integration tests, and an anvil install recipe are follow-ups (see checklist).

What it does

  • Selection mirrors cargo build: -p/--package (globs + name@version), --workspace/--all, --exclude, plus --none (explicit empty set).
  • Filters — a small metadata predicate language: --filter / --exclude-filter over lib, bin, dep:<name>, metadata:<key>[=<value>].
  • Two execution modes — per-package (once per member, with {name}/{spec}/{version}/{manifest} substitution) or --once (a single invocation, with {packages} expanding to --workspace or an explicit --package name@version list).
  • --chdir runs each per-package command from that member's crate root (per-package only; a usage error with --once). --keep-going and --dry-run round out execution.
  • Empty selection = success (exit 0) — the key property that lets adopters drop the per-recipe --skip guards.

Why

The impact scoping in cargo-anvil re-spells the same four chores per recipe: the skip/default preamble, @version stripping, cargo metadata filtering (lib-only, coverage opt-outs, loom-dependents), and per-package iteration. cargo-each absorbs all four. Two recipes already carry a TODO(anvil-runner) anticipating exactly this helper.

The design doc (crates/cargo-each/docs/design/README.md §6) shows the before/after for clippy, external-types, loom, and llvm-cov, and lists the small anvil-impact adjustments the recipe refactor will make (emit --none instead of --skip; print one selector token per line; stop version-qualifying).

Design decisions worth a look

  • No --from-file / --from-env source. cargo-each stays flag-only; a computed selection (an impact tier) is fed in via ordinary shell expansion of anvil's existing _anvil-impact-include <tier> helper, keeping the ANVIL_IMPACT=off widening + tier-default policy in the one place it already lives. (§7 rejected alternatives.)
  • Placeholder-only per-package targeting by default, with --chdir as an opt-in for directory-oriented tools.

Tests

Unit tests (selection resolution incl. globs/exclude/none, filter predicates, placeholder substitution + mode misuse, plan build incl. --chdir) plus assert_cmd integration tests driving the binary against a fixture workspace. 100% line coverage per cargo-coverage-gate; cargo-mutants clean; fmt/clippy/spellcheck/external-types/readme-check all green.

Adoption

cargo-each can only be wired into the cargo-anvil recipes once it is merged and published to crates.io, so that work is intentionally not part of this PR. Post-publish, the anvil impact.just helpers and the ~26 check recipes collapse to single cargo each lines (see design doc §6), plus an anvil-tool-cargo-each-install recipe.

…to README.md

Add a workspace-wide 'Design Docs Are Part of the Source' section to the root
AGENTS.md: design docs live under crates/<crate>/docs/design/README.md, kept in
lockstep with the code; implementation plans are optional; code must not cite
design-doc sections; docs must not be deleted.

Remove the now-redundant design-docs section from cargo-coverage-gate/AGENTS.md
(its code citations are intentionally left as-is).

Rename the top-level design docs to README.md so they render as the docs/design/
landing page on GitHub and ADO, and update all cross-references in companion
docs, implementation plans, and rustdoc comments. Regenerate cargo-anvil's
crate README.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7b9a4bc0-14af-4dcd-96e7-0b9414a10387
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

⚠️ Breaking Changes Detected

error: failed to retrieve local crate data from git revision

Caused by:
    0: failed to retrieve manifest file from git revision source
    1: possibly due to errors: [
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo-anvil/tests/fixtures/migration/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/without_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_dev_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_self_dev_dep/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo-anvil/tests/fixtures/opt-outs/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e/crates/cargo-anvil/tests/fixtures/customized/Cargo.toml: no `package` table,
       ]
    2: package `cargo-each` not found in /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/629cd791ce88734a7164f4e95b2e0e57fdfa1f4e

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: cargo_semver_checks::rustdoc_gen::RustdocFromProjectRoot::get_crate_source
   2: cargo_semver_checks::rustdoc_gen::StatefulRustdocGenerator<cargo_semver_checks::rustdoc_gen::CoupledState>::prepare_generator
   3: cargo_semver_checks::Check::check_release::{{closure}}
   4: cargo_semver_checks::Check::check_release
   5: cargo_semver_checks::exit_on_error
   6: cargo_semver_checks::main
   7: std::sys::backtrace::__rust_begin_short_backtrace
   8: main

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

@martin-kolinek
martin-kolinek force-pushed the cargo-each branch 2 times, most recently from a51815b to e81ee81 Compare July 24, 2026 10:00
@codecov-commenter

codecov-commenter commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (beeaf82) to head (21d200d).

Additional details and impacted files
@@           Coverage Diff            @@
##             main      #61    +/-   ##
========================================
  Coverage   100.0%   100.0%            
========================================
  Files          50       57     +7     
  Lines        2652     3054   +402     
========================================
+ Hits         2652     3054   +402     
Flag Coverage Δ
linux 100.0% <100.0%> (ø)
linux-arm 100.0% <100.0%> (ø)
scheduled ?
windows 100.0% <100.0%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@martin-kolinek
martin-kolinek force-pushed the cargo-each branch 2 times, most recently from 0a1fc24 to a75f588 Compare July 24, 2026 12:27
@martin-kolinek
martin-kolinek marked this pull request as ready for review July 24, 2026 12:50
Copilot AI review requested due to automatic review settings July 24, 2026 12:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new cargo-each crate (library + cargo each subcommand) to run a command across a cargo-style workspace member selection with optional metadata-based filtering, and includes initial unit + CLI-style tests and design documentation. It also standardizes several crates’ design-doc layout by moving to docs/design/README.md and updates references accordingly.

Changes:

  • Add crates/cargo-each implementing workspace discovery (cargo metadata), selection (-p/--package, --workspace/--all, --exclude, --none), filtering predicates, placeholder substitution, and execution planning/modes.
  • Add unit tests plus end-to-end-ish CLI tests using a temporary fixture workspace.
  • Normalize “design doc as docs/design/README.md” documentation across crates and update repo-level contribution guidance and spelling dictionary.

Reviewed changes

Copilot reviewed 33 out of 36 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
crates/cargo-each/tests/cli.rs New CLI-driven tests using a temporary fixture workspace.
crates/cargo-each/src/workspace.rs Workspace/member discovery via cargo_metadata.
crates/cargo-each/src/substitute.rs Placeholder substitution ({name}, {spec}, {manifest}, {packages}, etc.).
crates/cargo-each/src/select.rs Cargo-style selector parsing/resolution and glob matching.
crates/cargo-each/src/plan.rs Build per-invocation execution plan (per-package vs --once).
crates/cargo-each/src/lib.rs Public crate docs and re-exports (library surface).
crates/cargo-each/src/filter.rs Predicate parsing/evaluation for --filter / --exclude-filter.
crates/cargo-each/src/error.rs Error taxonomy and umbrella EachError.
crates/cargo-each/src/bin/cargo-each/run.rs cargo each implementation: resolve, filter, plan, execute.
crates/cargo-each/src/bin/cargo-each/main.rs Binary entrypoint and exit-code mapping.
crates/cargo-each/src/bin/cargo-each/cli.rs Clap CLI definitions for cargo each.
crates/cargo-each/README.md Generated crate README mirroring rustdoc content.
crates/cargo-each/docs/design/README.md New design doc for cargo-each.
crates/cargo-each/Cargo.toml New crate manifest with deps, bin target, and dev-deps.
crates/cargo-coverage-gate/docs/implementation-plans/0000.md Update design-doc path reference to docs/design/README.md.
crates/cargo-coverage-gate/docs/design/README.md Add (or move to) top-level design doc as README.md.
crates/cargo-coverage-gate/AGENTS.md Remove duplicated design-doc guidance now covered at repo root.
crates/cargo-anvil/src/workspace.rs Update design-doc references (now docs/design/README.md).
crates/cargo-anvil/src/lib.rs Update design-doc filename references in rustdoc list.
crates/cargo-anvil/src/backend.rs Update design-doc references (now docs/design/README.md).
crates/cargo-anvil/README.md Refresh generated README content/links.
crates/cargo-anvil/docs/verification.md Update design-doc link references.
crates/cargo-anvil/docs/implementation-plans/0002.md Update design-doc link references.
crates/cargo-anvil/docs/implementation-plans/0001.md Update design-doc link references.
crates/cargo-anvil/docs/implementation-plans/0000.md Update design-doc link references.
crates/cargo-anvil/docs/design/updates.md Update cross-links to the design doc filename.
crates/cargo-anvil/docs/design/README.md Add (or move to) top-level design doc as README.md.
crates/cargo-anvil/docs/design/local.md Update cross-links to the design doc filename.
crates/cargo-anvil/docs/design/github.md Update cross-links to the design doc filename.
crates/cargo-anvil/docs/design/extensibility.md Update cross-links to the design doc filename.
crates/cargo-anvil/docs/design/checks.md Update cross-links to the design doc filename.
crates/cargo-anvil/docs/design/ado.md Update cross-links to the design doc filename.
crates/automation/README.md Refresh generated README formatting/header block.
Cargo.lock Add cargo-each package entry.
AGENTS.md Add repo-wide guidance about design docs and section-linking policy.
.spelling Add new allowed words relevant to new CLI/docs.
Comments suppressed due to low confidence (2)

crates/cargo-each/src/bin/cargo-each/cli.rs:46

  • --exclude is documented as requiring --workspace, but clap currently allows --exclude without it (and Selection::resolve will apply the excludes anyway). Enforcing the cargo-style constraint at the CLI layer prevents surprising combinations like -p foo --exclude foo silently producing an empty set.
    /// Exclude a member from the selection (requires --workspace). Repeatable.
    #[arg(long, value_name = "SPEC")]
    pub(crate) exclude: Vec<String>,

crates/cargo-anvil/src/workspace.rs:33

  • Repository guidance says not to cite design doc sections from code comments (AGENTS.md:67-74), because section numbers/anchors drift as docs are reorganized. These doc comments reference README.md §6; please reword to avoid embedding a section reference (you can still link to the design doc generally).
    /// Used by the emitter for [`README.md §6`]: multi-crate workspaces get
    /// `[workspace.lints]` with the catalog plus `[lints] workspace = true`
    /// in each member; single-crate repos get the catalog directly in
    /// `[lints]`.
    ///

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/cargo-each/src/bin/cargo-each/run.rs Outdated
Comment thread crates/cargo-each/src/plan.rs Outdated
Comment thread crates/cargo-anvil/src/workspace.rs Outdated
Comment thread crates/cargo-anvil/src/backend.rs Outdated
Comment thread crates/cargo-each/src/bin/cargo-each/cli.rs
Comment thread crates/cargo-coverage-gate/docs/implementation-plans/0000.md
martin-kolinek and others added 2 commits July 24, 2026 17:30
… member

Introduce `cargo-each`: a cargo-native tool that resolves a cargo-style
package selection (-p/--package with globs, --workspace, --exclude, plus
--none), optionally narrows it with a small metadata predicate language
(--filter/--exclude-filter: lib, bin, dep:<name>, metadata:<key>[=<value>]),
and runs a command over the result — once per member with
{name}/{spec}/{version}/{manifest} substitution, or once for the whole set
(--once, with {packages}). --chdir runs each per-package command from that
member's crate root; --keep-going and --dry-run round out execution.

An empty resolved selection is a successful no-op (exit 0), which lets adopters
drop the per-recipe skip guards. Purpose: collapse the hand-rolled impact
skip/splat/@version-strip/metadata-filter/per-package-iteration PowerShell in
the cargo-anvil check recipes to a single line.

Includes the crate, design doc, and unit + assert_cmd integration tests (100%
line coverage per cargo-coverage-gate; cargo-mutants clean). Adoption in the
cargo-anvil recipes follows once this crate is published to crates.io.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
The design-docs-policy rebase (renaming docs/design/design.md to README.md
and touching cargo-anvil's lib.rs) combined with main's cargo-anvil 0.3.0
bump left these auto-generated crate READMEs stale. Regenerated via
cargo-doc2readme 0.6.4 (the anvil-pinned version).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
- run.rs: honor the documented --keep-going contract by exiting with a
  flat 1 when any invocation fails, instead of the first failing child's
  exit code (which contradicted lib.rs and design doc). Tighten the
  keep-going integration test to assert exit code 1.
- plan.rs: drop the doc reference to a {manifest_dir} placeholder that
  cargo-each does not implement; describe the --chdir target as the
  crate's Cargo.toml directory instead.
- cargo-anvil workspace.rs/backend.rs: remove design-doc section anchors
  (README.md §N) from rustdoc per the hoisted AGENTS.md policy; link the
  design doc without a section number.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 27, 2026 16:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 33 out of 36 changed files in this pull request and generated 4 comments.

Comment thread crates/cargo-each/src/bin/cargo-each/cli.rs
Comment thread crates/cargo-each/src/bin/cargo-each/run.rs
Comment thread crates/cargo-each/README.md
Comment thread crates/automation/README.md
- cli.rs: make --exclude require --workspace via clap (matches the
  documented contract and cargo's own semantics); add integration tests.
- run.rs: on fail-fast, reduce a wide/Windows exit code to its low byte
  (a closer approximation of the child's code than collapsing to 1),
  mapping the ambiguous zero-low-byte and signal cases to 1. Extracted as
  a unit-tested exit_byte helper.
- Add the shared project logo.png to cargo-each, cargo-anvil, and
  automation so their generated READMEs no longer show a broken image.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 27, 2026 16:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated 1 comment.

Comment thread crates/cargo-each/src/select.rs Outdated
resolve() ignores -p/--package selectors when --workspace/--all is set
(--workspace wins, matching cargo build), so the resolved set is the
whole workspace. is_whole_workspace() now agrees instead of returning
false on that combination, keeping the {packages} expansion (--workspace
vs explicit --package list) consistent with what actually runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 27, 2026 16:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated 5 comments.

Comment thread crates/cargo-each/src/filter.rs
Comment thread crates/cargo-each/src/select.rs Outdated
Comment thread crates/cargo-each/logo.png
Comment thread crates/cargo-anvil/logo.png
Comment thread crates/automation/logo.png
- filter.rs: reject metadata keys with empty path segments (a..b, .role,
  role.), which parse but can never match (Value::get("") is always None)
  and would silently yield an empty set instead of a loud usage error.
- select.rs: replace the recursive glob backtracker (exponential worst
  case on inputs like *a*a*a...) with the standard iterative two-pointer
  matcher (O(pattern*name)); add coverage for the new cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 27, 2026 18:42
Comment thread crates/cargo-each/src/workspace.rs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

crates/cargo-anvil/docs/design/extensibility.md:333

  • The link text says “README.md §5.2”, but the link target is just ./README.md (no section anchor), so it won’t actually navigate to the referenced section. Point it at the correct header anchor to avoid a misleading/broken link.
Selection is unchanged from [README.md §5.2](./README.md): the engine resolves a backend set from

Reject empty prerelease/build suffixes (1.2.3-, 1.2.3+) and leading-zero
release components (01.2.3) in version_matches, matching cargo's grammar.

Add end-to-end tests for the cargo metadata to Member projection: a path
dev-dependency selected via --filter dep:<name>, and default-members
resolution on a bare invocation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 28, 2026 19:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

crates/cargo-each/src/workspace.rs:97

  • has_lib is computed by checking for a target kind of exactly "lib". In cargo metadata, proc-macro crates report kind "proc-macro" (not "lib"), so --filter lib will incorrectly exclude proc-macro crates even though they are library targets in practice.
    crates/cargo-each/src/select.rs:36
  • The field doc says exclude is “only meaningful with all”, but Selection::resolve applies exclude even when all is false (e.g. it will exclude from a -p selection). Since the CLI already enforces --exclude requires --workspace, the doc here should reflect that rather than implying the library ignores it.
    crates/cargo-each/src/plan.rs:75
  • Plan::build validates placeholders up front, but substitute() also calls validate_placeholders() for every invocation. For large workspaces this doubles the placeholder-scan work (and repeats it per member in per-package mode). Consider adding an internal substitute_unchecked (or passing a pre-validated flag) so Plan::build can validate once and then substitute without re-validating.

@sandersaares sandersaares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[Copilot speaking]

Multi-agent code review — overview

Automated review of the cargo-each crate introduction (plus the cross-crate doc renames, root AGENTS.md, .spelling, and logos) performed by a swarm of subagents across a file-group × focus-area matrix (maintainability, API docs, module structure, testing, present-tense, design docs, Rust errors/panics, naming/API, correctness, tooling/packaging).

Scope & prior state: all 30 pre-existing review threads on this PR are resolved; this pass adds only new findings.

Outcome: all substantive feedback is delivered as individual line comments (no blockers). Findings are low-to-medium severity. Four cross-cutting synthesis findings are anchored on their most relevant files:

  • Public-library contract (is this a consumable API?) — on crates/cargo-each/src/lib.rs.
  • Recurring primitive-obsession / stringly-typed pattern — on crates/cargo-each/src/plan.rs.
  • Documentation ↔ code contract drift — on crates/cargo-each/docs/design/README.md.
  • Orphaned code→design section reference exposed by a deletion — on the deleted line in crates/cargo-coverage-gate/AGENTS.md.

No approve / request-changes status is set — comments only.

Comment thread crates/cargo-each/src/bin/cargo-each/run.rs Outdated
Comment thread crates/cargo-each/src/substitute.rs
Comment thread crates/cargo-each/src/filter.rs
Comment thread crates/cargo-each/src/error.rs
Comment thread crates/cargo-each/src/substitute.rs Outdated
Comment thread crates/cargo-each/docs/design/README.md Outdated
Comment thread crates/cargo-each/docs/design/README.md Outdated
Comment thread crates/cargo-each/src/bin/cargo-each/cli.rs Outdated
Comment thread crates/cargo-each/tests/cli.rs
Comment thread crates/cargo-each/tests/cli.rs
- run.rs: under --keep-going, treat a spawn failure as a failed invocation
  (log, continue, exit 1) instead of aborting with exit 2.
- filter.rs: name MetadataEquals fields; fix NaN string-equality; match target
  kinds via the typed enum.
- plan.rs: replace the whole_workspace bool with a PackagesExpansion enum.
- substitute.rs: make validate_placeholders pub(crate); document the
  unknown-placeholder passthrough contract.
- error.rs: document the intentional app-style opaque umbrella.
- workspace.rs: enum-based lib/bin target detection; contract-first load docs.
- select.rs: document index panic-safety and defensive guard.
- docs: {packages} filter caveat; lib = plain lib kind only.
- tests: spawn-failure coverage; specific-code / stderr assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 29, 2026 08:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 36 out of 39 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

crates/cargo-each/src/plan.rs:90

  • Plan::build accepts an empty command slice and will then emit Invocation { argv: vec![] } for non-empty members (because substitute() returns an empty vec). The binary later assumes argv is non-empty (split_first().expect("Plan::build never emits an empty argv") in run.rs), so library callers (or a future CLI regression) could trigger a panic instead of a usage error.

Consider validating command is non-empty in Plan::build and returning an EachError (likely via a new pub(crate) error variant included in the EachError umbrella) rather than allowing an empty argv to escape.
crates/cargo-each/src/bin/cargo-each/run.rs:143

  • shell_join() does not quote empty-string arguments. In --dry-run output, an empty arg currently renders as nothing, and the joined output loses the fact that an empty argument was present (it will just appear as extra whitespace). Quoting empty args as "" would make the dry-run output unambiguous.

Comment thread crates/cargo-each/src/lib.rs
Comment thread crates/cargo-each/src/plan.rs
Comment thread crates/cargo-each/docs/design/README.md
Comment thread crates/cargo-coverage-gate/AGENTS.md
- lib.rs/substitute.rs: settle the library as binary-facing tooling — narrow
  substitute/Placeholders to pub(crate) and state the intent in the spine docs.
- substitute.rs/plan.rs: validate_placeholders now takes Mode, not a bare bool.
- cargo-coverage-gate: drop the orphaned code->design section reference in
  render/markdown.rs left behind by the design-doc rename.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 29, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 37 out of 40 changed files in this pull request and generated no new comments.

Comment thread crates/cargo-each/docs/design/README.md
Add a Prior art section to the design doc answering why cargo-workspaces'
exec subcommand doesn't cover cargo-each's cargo-native selection, metadata
filtering, selection-aware placeholder injection, or empty-set-as-success.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 29, 2026 09:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 37 out of 40 changed files in this pull request and generated no new comments.

Comment thread crates/cargo-each/README.md
Broaden the Prior art section to cover cargo-hack: it shares package
selectors but is built for feature/version combinatorics over cargo
subcommands, and cannot run arbitrary per-manifest commands, inject
{manifest}/{name}, or filter by package.metadata.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d4a5d3ad-e273-4730-8c0f-d1cb806148e0
Copilot AI review requested due to automatic review settings July 29, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 37 out of 40 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

crates/cargo-each/src/bin/cargo-each/run.rs:99

  • execute() panics if an invocation ever contains an empty argv because of the unconditional split_first().expect(...). Even if the CLI normally prevents this, treating it as a runtime error (exit 2) is more robust than crashing the process.

@@ -0,0 +1,337 @@
# cargo-each — Design

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the design doc misses explanation why do we need to support the library surface. do we have any plans to use it as a library?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants