morphit/scripts/build-cleanup-script.sh

152 lines
5.1 KiB
Bash

#!/usr/bin/env bash
#
# scripts/build-cleanup-script.sh
#
# Memory #30 reinforcement. Delta tarballs cannot communicate
# file DELETIONS — extracting `morphit-delta-cpNN.tar.gz` over an
# existing checkout adds + overwrites, never removes. cp82's
# sw.js (deleted in cp81) appeared in operator checkouts as a
# stale file until Ken's manual delete-and-re-extract workflow.
#
# This script generates a `cpNN-cleanup.sh` helper that operators
# run BEFORE extracting a delta tarball: it `rm -f`s every file
# that was deleted in the new commits. If no deletions occurred,
# the helper is not emitted (and the script exits 0 with a
# message saying "delta is safe; no cleanup needed").
#
# Usage:
#
# bash scripts/build-cleanup-script.sh [<from-ref>] [<to-ref>]
#
# Defaults: `HEAD~1` and `HEAD`. Override for multi-commit
# spans, e.g.:
#
# bash scripts/build-cleanup-script.sh v1.0.0-beta.1 v1.0.0-beta.2
#
# Output: writes `cp<NN>-cleanup.sh` to the current directory (or
# the directory named by `MORPHIT_CLEANUP_OUT_DIR`), where `<NN>`
# is taken from the most recent `Part 122 cp<NN>` mention in
# `TARBALL.md` if available, else from `git rev-parse --short
# <to-ref>`.
#
# Idempotent: emitted cleanup script uses `rm -f` (no-op if file
# already absent) so re-running cleanup after extraction is safe.
#
# Exit codes:
# 0 — cleanup emitted, OR no deletions (helper not emitted but
# run was successful — operator's delta is safe to extract
# directly)
# 1 — git not available, or refs invalid
set -euo pipefail
FROM_REF="${1:-HEAD~1}"
TO_REF="${2:-HEAD}"
OUT_DIR="${MORPHIT_CLEANUP_OUT_DIR:-.}"
# Pre-flight: git available?
if ! command -v git >/dev/null 2>&1; then
echo "ERROR: git not found in PATH. This script generates a cleanup" >&2
echo " helper from \`git diff --diff-filter=D\` output — it" >&2
echo " cannot run without git." >&2
exit 1
fi
# Pre-flight: are we inside a git work tree?
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "ERROR: not inside a git work tree. cd to a Morphit clone first." >&2
exit 1
fi
# Pre-flight: refs valid?
if ! git rev-parse --verify "$FROM_REF" >/dev/null 2>&1; then
echo "ERROR: <from-ref> \`$FROM_REF\` is not a valid git ref." >&2
exit 1
fi
if ! git rev-parse --verify "$TO_REF" >/dev/null 2>&1; then
echo "ERROR: <to-ref> \`$TO_REF\` is not a valid git ref." >&2
exit 1
fi
# Determine checkpoint label. Prefer the most-recent `cp<NN>`
# mention in TARBALL.md (top of file is newest checkpoint per
# the standing "tarball at meaningful checkpoints" rule). Fall
# back to git short-SHA.
CP_LABEL=""
if [[ -f TARBALL.md ]]; then
CP_LABEL=$(grep -oE 'cp[0-9]+' TARBALL.md | head -1 || true)
fi
if [[ -z "$CP_LABEL" ]]; then
CP_LABEL="cp$(git rev-parse --short "$TO_REF")"
fi
# Collect deletions. `--diff-filter=D` keeps only entries whose
# status is "Deleted" between the two refs. `-z` would null-
# delimit the output for paths with spaces; we accept the
# limitation that Morphit paths don't contain spaces (verified
# at repo init).
DELETIONS=$(git diff --diff-filter=D --name-only "$FROM_REF" "$TO_REF")
if [[ -z "$DELETIONS" ]]; then
echo "No deletions between $FROM_REF..$TO_REF — delta is safe to extract directly."
echo "(No cleanup helper emitted.)"
exit 0
fi
DELETION_COUNT=$(echo "$DELETIONS" | wc -l | tr -d ' ')
OUT_FILE="$OUT_DIR/$CP_LABEL-cleanup.sh"
# Emit cleanup script. Uses `rm -f` (idempotent — no error if
# the file is already absent on the operator's box). Header
# documents what + why.
cat > "$OUT_FILE" <<HEADER
#!/usr/bin/env bash
#
# $CP_LABEL-cleanup.sh
#
# Autogenerated by \`scripts/build-cleanup-script.sh\` from
# \`git diff --diff-filter=D $FROM_REF..$TO_REF\`.
#
# Memory #30 (delta tarballs cannot communicate deletions):
# extracting the matching \`morphit-delta-$CP_LABEL.tar.gz\` over
# an existing checkout adds + overwrites files but never removes
# them. This helper removes the $DELETION_COUNT file(s) that
# were DELETED in $FROM_REF..$TO_REF, so the operator's checkout
# matches the upstream tree post-extract.
#
# Run BEFORE extracting the delta tarball:
#
# cd /path/to/morphit-clone
# bash $CP_LABEL-cleanup.sh
# tar xzf morphit-delta-$CP_LABEL.tar.gz
#
# Idempotent: safe to run multiple times. \`rm -f\` no-ops if
# the file is already absent.
set -eu
HEADER
while IFS= read -r path; do
# Defensive: skip empty lines (shouldn't happen but cheap).
[[ -z "$path" ]] && continue
# Hard-fail if a path contains a single-quote (would break
# the rm -f shell-quoting); the repo invariant is no such
# paths, so this is belt-and-braces.
if [[ "$path" == *"'"* ]]; then
echo "ERROR: path contains a single quote, cleanup script generation aborted:" >&2
echo " $path" >&2
rm -f "$OUT_FILE"
exit 1
fi
printf "rm -f '%s'\n" "$path" >> "$OUT_FILE"
done <<< "$DELETIONS"
cat >> "$OUT_FILE" <<FOOTER
echo "$CP_LABEL cleanup: $DELETION_COUNT file(s) removed."
FOOTER
chmod +x "$OUT_FILE"
echo "Emitted: $OUT_FILE ($DELETION_COUNT deletion(s) listed)"