625c11319a
The unreached-package arm exits 2 and names the package's Cargo.toml, so the usage text's "NO file is named" was false for it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
172 lines
6.5 KiB
Bash
Executable File
172 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Format-checks every Cargo workspace root in the repo, from any directory.
|
|
# `cargo fmt --all` reaches only ONE of them. Reasoning: context/test-evidence/
|
|
# tooling.md, "The two-workspace fmt gate" -- including why `-p` scoped by `--manifest-path` is the only
|
|
# spelling that is neither blind nor boundary-crossing (improvise-b4ks).
|
|
set -uo pipefail
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
usage: fmt-workspaces.sh [--list] [--fix] [repo-root]
|
|
|
|
repo-root the tree to scan; defaults to the directory above this script
|
|
--list print each workspace root and its members, then exit
|
|
--fix rewrite the offending files instead of reporting them
|
|
|
|
exit 0 every workspace root is formatted
|
|
exit 1 rustfmt did not certify a root clean: a file differs from rustfmt's
|
|
output, or rustfmt refused to parse one. Either way a FILE IS NAMED
|
|
and the tree is what is wrong.
|
|
exit 2 the gate did not run: usage error, no cargo, no rustfmt, cargo
|
|
metadata failed, no [workspace] root found, or a [package] that no
|
|
root has as a member. NO verdict was produced. No SOURCE file is
|
|
named; the unreached-package case names its Cargo.toml.
|
|
|
|
The split is "did the gate run", not "is every exit-1 cause a diff". That is
|
|
narrower than it sounds and it is deliberate -- see the note on rc=1 below.
|
|
|
|
Exit 2 exists because `cargo fmt -p opfs-contract -- --check` from the repo
|
|
root exits 1 with `not a member of the workspace`, having checked nothing and
|
|
named nothing, which a caller reading $? cannot tell from a real failure.
|
|
Never collapse 2 into 1.
|
|
EOF
|
|
}
|
|
|
|
# `[workspace]` at the start of a line, in a Cargo.toml that is not vendored
|
|
# build output. `harness/opfs-contract` carries an empty one, so it is its own
|
|
# root and the outer `--all` never reaches it.
|
|
#
|
|
# The find is RELATIVE to the tree being scanned: an absolute `-path` filter
|
|
# matches the ancestors too, and this repo is itself checked out under
|
|
# `.claude/worktrees/` in every agent worktree, where that excluded everything.
|
|
manifests_with() {
|
|
local root="$1" section="$2"
|
|
( cd "$root" && find . -name Cargo.toml \
|
|
-not -path './target/*' -not -path '*/target/*' \
|
|
-not -path './.git/*' \
|
|
-not -path '*/node_modules/*' \
|
|
-not -path './.claude/*' ) \
|
|
| sort \
|
|
| while IFS= read -r rel; do
|
|
if grep -q "^\\[$section\\]" "$root/${rel#./}"; then
|
|
printf '%s\n' "$root/${rel#./}"
|
|
fi
|
|
done
|
|
}
|
|
workspace_roots() { manifests_with "$1" workspace; }
|
|
|
|
# The members of ONE workspace, named by cargo rather than parsed out of the
|
|
# manifest: a `members = [...]` list may carry globs and path dependencies.
|
|
members_of() {
|
|
local manifest="$1" meta
|
|
meta="$(cargo metadata --manifest-path "$manifest" --no-deps --format-version 1 2>/dev/null)" || return 1
|
|
printf '%s' "$meta" | python3 -c '
|
|
import json, sys
|
|
doc = json.load(sys.stdin)
|
|
rows = [p["name"] + "\t" + p["manifest_path"] for p in doc["packages"]]
|
|
if not rows:
|
|
sys.exit(1)
|
|
print("\n".join(rows))
|
|
' || return 1
|
|
}
|
|
|
|
main() {
|
|
local list=0 mode=--check root=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--list) list=1 ;;
|
|
--fix) mode=--fix ;;
|
|
-h|--help) usage; return 2 ;;
|
|
-*) printf 'fmt-workspaces: unknown option %s\n' "$1" >&2; usage; return 2 ;;
|
|
*)
|
|
if [ -n "$root" ]; then
|
|
printf 'fmt-workspaces: at most one repo-root, got %s and %s\n' "$root" "$1" >&2
|
|
return 2
|
|
fi
|
|
root="$1" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "$root" ]; then root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; fi
|
|
if [ ! -d "$root" ]; then
|
|
printf 'fmt-workspaces: not a directory: %s\n' "$root" >&2
|
|
return 2
|
|
fi
|
|
command -v cargo >/dev/null 2>&1 || {
|
|
echo 'fmt-workspaces: cargo is not on PATH' >&2; return 2; }
|
|
# Without this, a missing rustfmt component exits 1 from `cargo fmt` and
|
|
# reads as "your code is misformatted" -- the very confusion this exists for.
|
|
cargo fmt --version >/dev/null 2>&1 || {
|
|
echo 'fmt-workspaces: cargo fmt is unavailable (rustfmt component missing?)' >&2
|
|
return 2; }
|
|
|
|
local -a roots=()
|
|
while IFS= read -r m; do [ -n "$m" ] && roots+=("$m"); done < <(workspace_roots "$root")
|
|
if [ ${#roots[@]} -eq 0 ]; then
|
|
printf 'fmt-workspaces: no [workspace] root under %s\n' "$root" >&2
|
|
return 2
|
|
fi
|
|
|
|
# Pass 1: every root's members, before anything is formatted, so that a
|
|
# package NO root reaches is exit 2 rather than silently never checked.
|
|
local rc=0 manifest member i m
|
|
local -a members=() covered=()
|
|
for i in "${!roots[@]}"; do
|
|
local rows
|
|
rows="$(members_of "${roots[$i]}")" || rows=""
|
|
if [ -z "$rows" ]; then
|
|
printf 'fmt-workspaces: cargo metadata failed for %s\n' "${roots[$i]}" >&2
|
|
return 2
|
|
fi
|
|
members[$i]="$(printf '%s\n' "$rows" | cut -f1 | tr '\n' ' ')"
|
|
while IFS= read -r m; do covered+=("$m"); done < <(printf '%s\n' "$rows" | cut -f2)
|
|
done
|
|
local orphans
|
|
orphans="$(manifests_with "$root" package | python3 -c '
|
|
import os, sys
|
|
covered = {os.path.realpath(p) for p in sys.argv[1:]}
|
|
for line in sys.stdin:
|
|
m = line.rstrip("\n")
|
|
if m and os.path.realpath(m) not in covered:
|
|
print(m)
|
|
' "${covered[@]}")"
|
|
if [ -n "$orphans" ]; then
|
|
printf 'fmt-workspaces: no workspace root reaches these packages, so nothing checks them:\n%s\n' \
|
|
"$orphans" >&2
|
|
return 2
|
|
fi
|
|
|
|
for i in "${!roots[@]}"; do
|
|
manifest="${roots[$i]}"
|
|
local -a pkgs=()
|
|
read -r -a pkgs <<< "${members[$i]}"
|
|
|
|
if [ "$list" = 1 ]; then
|
|
printf '%s: %s\n' "$manifest" "${pkgs[*]}"
|
|
continue
|
|
fi
|
|
|
|
# `-p` for every member, scoped by this root's own manifest. NOT `--all`,
|
|
# which walks PAST a nested workspace boundary and reports the outer tree's
|
|
# files under the inner root's name; and not the bare manifest-path form,
|
|
# which silently checks only the root package and misses every member.
|
|
# `|| rc=1` deliberately does NOT try to separate a diff from a source
|
|
# rustfmt could not parse: both exit 1 and both NAME A FILE, so both are
|
|
# facts about the tree. Measured: an unclosed delimiter exits 1, and an
|
|
# unknown rustfmt.toml option is only a warning (exit 0). What exit 2 is
|
|
# reserved for is "the gate did not run", and every such cause is checked
|
|
# ABOVE this loop. Reasoning: context/test-evidence/tooling.md.
|
|
local -a flags=()
|
|
for member in "${pkgs[@]}"; do flags+=(-p "$member"); done
|
|
if [ "$mode" = --fix ]; then
|
|
cargo fmt --manifest-path "$manifest" "${flags[@]}" || rc=1
|
|
else
|
|
cargo fmt --manifest-path "$manifest" "${flags[@]}" -- --check || rc=1
|
|
fi
|
|
done
|
|
return $rc
|
|
}
|
|
|
|
main "$@"
|