Merge nucleic/eager-ancient-heron-p5em into dev
This commit is contained in:
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Tool-inventory parity sweep (NAROS.md §10.3): nothing agents rely on may silently
|
||||
# vanish when the default sandbox image moves from nucleic-sandbox:v7 to naros-agent.
|
||||
#
|
||||
# parity-sweep.sh <candidate-image> <reference-image> [--allow FILE]
|
||||
#
|
||||
# Compares (a) the set of executables on PATH and (b) the npm global package set, in
|
||||
# each image. Anything present in the REFERENCE but missing from the CANDIDATE fails
|
||||
# the sweep unless listed in the allowlist (deliberate, understood removals — one name
|
||||
# per line, `#` comments). Additions are reported informationally. Python module parity
|
||||
# is not swept: neither image bakes site-packages beyond pip's own.
|
||||
#
|
||||
# Exit: 0 parity holds, 1 unexplained removals, 2 usage/docker errors.
|
||||
set -euo pipefail
|
||||
|
||||
CAND="${1:?usage: parity-sweep.sh <candidate-image> <reference-image> [--allow FILE]}"
|
||||
REF="${2:?missing reference image}"
|
||||
shift 2
|
||||
ALLOW="$(cd "$(dirname "$0")" && pwd)/parity-allowlist.txt"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--allow) ALLOW="$2"; shift 2 ;;
|
||||
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Every executable name reachable on the image's default PATH. --entrypoint /bin/sh is
|
||||
# nash in naros images and dash in the v7 base — the script is portable to both.
|
||||
path_inventory() {
|
||||
docker run --rm --entrypoint /bin/sh "$1" -c \
|
||||
'for d in $(echo "$PATH" | tr : " "); do ls -1 "$d" 2>/dev/null; done | sort -u'
|
||||
}
|
||||
|
||||
npm_inventory() {
|
||||
docker run --rm --entrypoint /bin/sh "$1" -c \
|
||||
'npm ls -g --depth=0 --parseable 2>/dev/null | tail -n +2' \
|
||||
| awk -F/ 'NF { if ($(NF-1) ~ /^@/) print $(NF-1)"/"$NF; else print $NF }' | sort -u
|
||||
}
|
||||
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
echo "inventorying candidate $CAND …"
|
||||
path_inventory "$CAND" > "$tmp/cand.path"
|
||||
npm_inventory "$CAND" > "$tmp/cand.npm"
|
||||
echo "inventorying reference $REF …"
|
||||
path_inventory "$REF" > "$tmp/ref.path"
|
||||
npm_inventory "$REF" > "$tmp/ref.npm"
|
||||
|
||||
allow_filter() {
|
||||
if [ -f "$ALLOW" ]; then
|
||||
grep -vE '^\s*(#|$)' "$ALLOW" | grep -vxF -f /dev/stdin "$1" || true
|
||||
else
|
||||
cat "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
comm -23 "$tmp/ref.path" "$tmp/cand.path" > "$tmp/removed.path"
|
||||
comm -23 "$tmp/ref.npm" "$tmp/cand.npm" > "$tmp/removed.npm"
|
||||
added_path=$(comm -13 "$tmp/ref.path" "$tmp/cand.path" | wc -l)
|
||||
|
||||
cat "$tmp/removed.path" "$tmp/removed.npm" | sort -u > "$tmp/removed.all"
|
||||
allow_filter "$tmp/removed.all" > "$tmp/unexplained" || true
|
||||
|
||||
echo "PATH: $(wc -l < "$tmp/ref.path") reference, $(wc -l < "$tmp/cand.path") candidate," \
|
||||
"$(wc -l < "$tmp/removed.path") removed, $added_path added"
|
||||
echo "npm globals removed: $(wc -l < "$tmp/removed.npm")"
|
||||
if [ -s "$tmp/removed.all" ]; then
|
||||
echo "--- removed (before allowlist) ---"
|
||||
cat "$tmp/removed.all"
|
||||
fi
|
||||
|
||||
if [ -s "$tmp/unexplained" ]; then
|
||||
echo "PARITY FAILURE — present in $REF, missing from $CAND, not allowlisted:" >&2
|
||||
cat "$tmp/unexplained" >&2
|
||||
echo "(fix the image, or add to $ALLOW with a comment explaining the removal)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "parity OK"
|
||||
Reference in New Issue
Block a user