Files
boxmaker/scripts/check-dep-docs.sh
T
2026-09-17 10:14:22 -07:00

137 lines
4.6 KiB
Bash
Executable File

#!/bin/sh
# Fails if a workspace dependency declared in ROOT/Cargo.toml has no table row in
# ROOT/docs/dependencies.md, or if any dependency in a crate manifest lacks
# `workspace = true` (on its own line for the plain/dotted form, or as a
# `workspace = true` line inside a table-form dependency). A table header such as
# `[dependencies.x]` introduces dependency x; the lines that follow it until the
# next section header are settings of x, never dependencies themselves.
ROOT="${1:-.}"
status=0
# Fail closed.
if [ ! -d "$ROOT/crates" ]; then
echo "check-dep-docs: $ROOT/crates is not a directory" >&2
exit 1
fi
if [ ! -f "$ROOT/Cargo.toml" ]; then
echo "check-dep-docs: $ROOT/Cargo.toml is not a file" >&2
exit 1
fi
if [ ! -f "$ROOT/docs/dependencies.md" ]; then
echo "check-dep-docs: $ROOT/docs/dependencies.md is not a file" >&2
exit 1
fi
# Workspace crate names: directories under ROOT/crates that contain a Cargo.toml.
workspace=""
for d in "$ROOT"/crates/*/; do
[ -d "$d" ] || continue
[ -f "$d/Cargo.toml" ] || continue
workspace="$workspace $(basename "$d")"
done
root_toml="$ROOT/Cargo.toml"
docs="$ROOT/docs/dependencies.md"
# Part 1: non-workspace-crate workspace dependencies in ROOT/Cargo.toml must have a docs row.
dep_keys=$(awk '
BEGIN { in_deps = 0; in_table = 0; depth = 0 }
/^[ \t]*\[/ {
hdr = $0
sub(/^[ \t]*\[/, "", hdr)
sub(/[ \t]*\][ \t]*.*$/, "", hdr)
if (hdr ~ /dependencies/) {
n = split(hdr, parts, ".")
if (parts[n] ~ /dependencies$/) { in_deps = 1; in_table = 0 }
else { in_deps = 1; in_table = 1; print parts[n] }
} else { in_deps = 0; in_table = 0 }
depth = 0
next
}
in_table { next }
in_deps {
if (depth == 0) {
t = $0
sub(/^[ \t]+/, "", t)
if (match(t, /^[A-Za-z0-9][A-Za-z0-9_-]*(\.[A-Za-z0-9_-]+)*[ \t]*=/)) {
name = t
sub(/[ \t]*=.*/, "", name)
sub(/\..*/, "", name)
print name
}
}
s = $0
while (match(s, /[{}]/)) {
c = substr(s, RSTART, 1)
if (c == "{") depth++
else if (depth > 0) depth--
s = substr(s, RSTART + 1)
}
}
' "$root_toml")
for key in $dep_keys; do
hit=0
for w in $workspace; do
if [ "$key" = "$w" ]; then hit=1; break; fi
done
if [ "$hit" -eq 0 ]; then
if ! awk -v k="$key" 'index($0, "| `" k "` |") == 1 { found = 1; exit } END { exit !found }' "$docs"; then
echo "check-dep-docs: $key has no row in $docs" >&2
status=1
fi
fi
done
# Part 2: every dependency in a crate manifest must have `workspace = true`.
for crate in "$ROOT"/crates/*/; do
[ -d "$crate" ] || continue
crate_name=$(basename "$crate")
[ -f "$crate/Cargo.toml" ] || continue
bad=$(awk -v cn="$crate_name" '
BEGIN { in_deps = 0; in_table = 0; cur = ""; has_ws = 0; depth = 0 }
/^[ \t]*\[/ {
if (in_table && !has_ws) printf "check-dep-docs: %s table-form dependency %s has no `workspace = true` line\n", cn, cur
in_table = 0
hdr = $0
sub(/^[ \t]*\[/, "", hdr)
sub(/[ \t]*\][ \t]*.*$/, "", hdr)
if (hdr ~ /dependencies/) {
n = split(hdr, parts, ".")
if (parts[n] ~ /dependencies$/) { in_deps = 1 }
else { in_deps = 1; in_table = 1; cur = parts[n]; has_ws = 0 }
} else { in_deps = 0 }
depth = 0
next
}
in_table {
if ($0 ~ /workspace[ \t]*=[ \t]*true/) has_ws = 1
next
}
in_deps {
if (depth == 0) {
t = $0
sub(/^[ \t]+/, "", t)
if (match(t, /^[A-Za-z0-9][A-Za-z0-9_-]*(\.[A-Za-z0-9_-]+)*[ \t]*=/)) {
if ($0 !~ /workspace[ \t]*=[ \t]*true/)
printf "check-dep-docs: %s declares a dependency without `workspace = true`: %s\n", cn, $0
}
}
s = $0
while (match(s, /[{}]/)) {
c = substr(s, RSTART, 1)
if (c == "{") depth++
else if (depth > 0) depth--
s = substr(s, RSTART + 1)
}
}
END { if (in_table && !has_ws) printf "check-dep-docs: %s table-form dependency %s has no `workspace = true` line\n", cn, cur }
' "$crate/Cargo.toml")
if [ -n "$bad" ]; then
printf '%s\n' "$bad" >&2
status=1
fi
done
exit $status