148 lines
6.6 KiB
Bash
Executable File
148 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# verify-clean-clone.sh -- prove the COMMITTED artifact runs, from a clean clone.
|
|
#
|
|
# ================== THIS FILE IS THE SECOND VERSION. THE FIRST HAD B1. ========
|
|
# B1 was: the delivered scripts were committed mode 100644, so the artifact
|
|
# returned 126 Permission denied for anyone who cloned it. Two of us ran 32/32
|
|
# because our local working copies had the exec bit set by hand at creation.
|
|
#
|
|
# I wrote v1 of this file to prevent exactly that. V1 COPIED THE WORKING-TREE
|
|
# FILES INTO A SCRATCH REPOSITORY AND ASSERTED THE SCRATCH REPOSITORY'S INDEX.
|
|
# cp preserves the local exec bit, so `git add` in the scratch repo recorded
|
|
# 100755 NO MATTER WHAT THE REAL REPOSITORY STORED. Reviewer reproduction:
|
|
# git update-index --chmod=-x push-guard.sh test-push-guard.sh
|
|
# ./verify-clean-clone.sh -> EXIT 0, "CLEAN CLONE: suite ran and passed"
|
|
# while the real index read 100644 -- the precise contaminated state B1 was.
|
|
#
|
|
# THE CONTROL FOR THE DEFECT INHERITED THE DEFECT, BECAUSE IT MEASURED A COPY
|
|
# INSTEAD OF THE SUBJECT. cp LAUNDERS MODE.
|
|
#
|
|
# Both of v1's mechanisms were right -- assert the INDEX not the disk, execute
|
|
# DIRECTLY not via `bash script`. They were applied to the wrong repository.
|
|
# So v2 changes what is measured, not how:
|
|
# * mode is read from the SOURCE repository's COMMITTED TREE (git ls-tree),
|
|
# which no local chmod can influence;
|
|
# * the tree under test is produced by CLONING THAT COMMIT, so every mode
|
|
# comes out of the object store rather than off my filesystem.
|
|
# There is no cp anywhere in this file, and that is deliberate.
|
|
#
|
|
# It also REFUSES rather than passes when the artifacts are untracked: an
|
|
# artifact that is not committed has no mode to verify, and a verifier that
|
|
# silently succeeds on nothing is the vacuous-absence failure all over again.
|
|
|
|
set -euo pipefail
|
|
|
|
readonly EX_FAIL=1
|
|
readonly EX_USAGE=64
|
|
|
|
REPO=""
|
|
REV="HEAD"
|
|
|
|
die() { printf 'usage error: %s\n' "$1" >&2; exit "$EX_USAGE"; }
|
|
|
|
while (( $# )); do
|
|
case "$1" in
|
|
--repo) REPO="${2:-}"; shift 2 ;;
|
|
--rev) REV="${2:-}"; shift 2 ;;
|
|
*) die "unknown argument: $1" ;;
|
|
esac
|
|
done
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
[[ -n "$REPO" ]] || REPO="$HERE"
|
|
|
|
ARTIFACTS=(push-guard.sh test-push-guard.sh verify-clean-clone.sh mutate-push-guard.sh
|
|
test-mutate-push-guard.sh
|
|
test-verify-clean-clone.sh)
|
|
SUITE="test-push-guard.sh"
|
|
|
|
# --- the subject must be a real repository, or there is nothing to verify -----
|
|
if ! ROOT="$(git -C "$REPO" rev-parse --show-toplevel 2>/dev/null)"; then
|
|
printf 'REFUSING: %s is not inside a git repository.\n' "$REPO" >&2
|
|
printf 'This verifier checks the COMMITTED mode of the artifact. An uncommitted\n' >&2
|
|
printf 'artifact has no committed mode, and passing here would prove nothing.\n' >&2
|
|
exit "$EX_FAIL"
|
|
fi
|
|
if ! REV_SHA="$(git -C "$ROOT" rev-parse --verify --quiet "${REV}^{commit}")"; then
|
|
printf 'REFUSING: %s does not resolve to a commit in %s\n' "$REV" "$ROOT" >&2
|
|
exit "$EX_FAIL"
|
|
fi
|
|
|
|
# --- THE ARTIFACT'S COMMITTED PATH, NOT ITS BASENAME -------------------------
|
|
# v2 asserted `git ls-tree HEAD -- push-guard.sh`, which is a ROOT-RELATIVE
|
|
# pathspec. These files really live several directories down. Run in place and
|
|
# every artifact came back "NOT TRACKED" -- the verifier built to stop packaging
|
|
# false-greens could not verify the tree that ships it.
|
|
#
|
|
# The tests missed it because EVERY FIXTURE INSTALLED THE ARTIFACTS AT THE
|
|
# FIXTURE ROOT. 6/6 green proved flat-layout operation and nothing about the
|
|
# deployed path. THAT IS THE THIRD TIME ON THIS TOOL THAT A CONTROL VALIDATED A
|
|
# MODEL INSTEAD OF THE SUBJECT: v1 measured a cp'd scratch repo, and then v2's
|
|
# own tests measured a layout that does not exist. A fixture is a claim about
|
|
# the world, and an untested fixture is an unreviewed one.
|
|
#
|
|
# --show-prefix answers "where is this directory inside its repository", so the
|
|
# same prefix drives ls-tree, the cloned stat, and the suite's working dir.
|
|
PREFIX="$(git -C "$REPO" rev-parse --show-prefix)"
|
|
|
|
printf '=== subject ===\n'
|
|
printf ' repo %s\n rev %s (%s)\n prefix %s\n\n' \
|
|
"$ROOT" "$REV" "$REV_SHA" "${PREFIX:-<repository root>}"
|
|
|
|
# --- 1. MODE, FROM THE COMMITTED TREE OF THE SUBJECT -------------------------
|
|
# git ls-tree reports what the COMMIT records. Nothing on my filesystem can
|
|
# move this number -- which is the whole point, and what v1 got wrong.
|
|
printf '=== committed modes (git ls-tree %s) ===\n' "$REV"
|
|
rc=0
|
|
for f in "${ARTIFACTS[@]}"; do
|
|
entry="$(git -C "$ROOT" ls-tree "$REV_SHA" -- "${PREFIX}${f}")"
|
|
if [[ -z "$entry" ]]; then
|
|
printf ' FAIL %s is NOT TRACKED at %s -- nothing committed to verify\n' "$f" "$REV"
|
|
rc=1; continue
|
|
fi
|
|
mode="${entry%% *}"; rest="${entry#* }"; type="${rest%% *}"
|
|
if [[ "$type" != blob ]]; then
|
|
printf ' FAIL %s is a %s at %s, not a regular file\n' "$f" "$type" "$REV"
|
|
rc=1; continue
|
|
fi
|
|
case "$mode" in
|
|
100755) printf ' ok %s committed %s\n' "$f" "$mode" ;;
|
|
120000) printf ' FAIL %s is a SYMLINK (%s) -- the reviewed blob is a path, not the code\n' "$f" "$mode"; rc=1 ;;
|
|
*) printf ' FAIL %s committed %s, needs 100755\n' "$f" "$mode"
|
|
printf ' fix with: git update-index --chmod=+x %s && commit\n' "$f"
|
|
rc=1 ;;
|
|
esac
|
|
done
|
|
if (( rc != 0 )); then
|
|
printf '\nREFUSING TO CONTINUE: the COMMITTED modes are wrong, whatever the disk says.\n'
|
|
printf 'A clone of this commit would exit 126 for the next person.\n'
|
|
exit "$EX_FAIL"
|
|
fi
|
|
|
|
# --- 2. RUN FROM A CLONE OF THAT COMMIT --------------------------------------
|
|
# Cloning materialises every file from the object store, so the modes on disk
|
|
# are the COMMITTED modes by construction. No cp, no local state, nothing this
|
|
# machine can contribute.
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
printf '\n=== cloning %s (no local mode bits survive this) ===\n' "$REV_SHA"
|
|
git clone -q --no-local --no-hardlinks "$ROOT" "$WORK/clone"
|
|
git -C "$WORK/clone" -c advice.detachedHead=false checkout -q "$REV_SHA"
|
|
|
|
for f in "${ARTIFACTS[@]}"; do
|
|
printf ' clone disk mode: %s %s\n' "$(stat -c '%a' "$WORK/clone/${PREFIX}${f}")" "${PREFIX}${f}"
|
|
done
|
|
|
|
printf '\n=== executing DIRECTLY (./%s), not via "bash %s" ===\n' "$SUITE" "$SUITE"
|
|
# Invoking the interpreter explicitly masks a missing exec bit completely -- it
|
|
# is how the original green was obtained. Direct execution is the thing under
|
|
# test, so the mode has to be real for this line to succeed.
|
|
cd "$WORK/clone/${PREFIX}"
|
|
if ! "./$SUITE"; then
|
|
printf '\nCLEAN-CLONE RUN FAILED.\n'
|
|
exit "$EX_FAIL"
|
|
fi
|
|
|
|
printf '\n=== CLEAN CLONE: the COMMITTED artifact ran and passed ===\n'
|