#!/bin/bash
# Build the distributable archive, deterministically.
#
#   ./tools/pack.sh [output-path]
#
# Default output is ../os-memory-io-ipc-exercises.tar.gz, i.e. beside the bundle
# directory rather than inside it, so the archive never contains a copy of
# itself.
#
# DETERMINISM
# Running this twice on the same sources must produce byte-identical output, so
# the published SHA-256 means something. Four things would otherwise vary, and
# each is pinned:
#   * file order            -> the member list is sorted with LC_ALL=C
#   * modification times    -> every staged file is set to FIXED_MTIME
#   * owner / group names   -> forced to 0 / 0 with empty names
#   * the gzip header       -> `gzip -n` omits the name and timestamp
#
# CONTENT
# Sources, READMEs, checks and helpers only. Build products, compiled binaries,
# .dSYM bundles, editor droppings and macOS metadata are excluded, and the
# result is scanned for absolute paths before it is written.
set -eu
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
NAME="$(basename "$HERE")"
OUT="${1:-$HERE/../$NAME.tar.gz}"
FIXED_MTIME="202609230000.00"      # CCYYMMDDhhmm.SS — arbitrary but pinned

STAGE="$(mktemp -d "${TMPDIR:-/tmp}/tlxpackXXXXXX")"
trap 'rm -rf "$STAGE"' EXIT
mkdir -p "$STAGE/$NAME"

# ---- copy only what belongs in the archive --------------------------------
( cd "$HERE" && find . \
    -name '.DS_Store'   -prune -o \
    -name '__MACOSX'    -prune -o \
    -name '*.dSYM'      -prune -o \
    -name 'build'       -prune -o \
    -name '.git'        -prune -o \
    -name '*.o'         -prune -o \
    -name '*.tar.gz'    -prune -o \
    -name '*~'          -prune -o \
    -type f -print ) \
  | LC_ALL=C sort > "$STAGE/manifest.txt"

while IFS= read -r rel; do
  mkdir -p "$STAGE/$NAME/$(dirname "$rel")"
  cp "$HERE/$rel" "$STAGE/$NAME/$rel"
done < "$STAGE/manifest.txt"

# Executability is content: the scripts must still run from an extracted copy.
find "$STAGE/$NAME" -name '*.sh' -exec chmod 755 {} +
find "$STAGE/$NAME" -type f ! -name '*.sh' -exec chmod 644 {} +

# ---- refuse to ship a machine-specific absolute path ----------------------
# pack.sh itself is excluded because it is the scanner: the patterns below
# appear in it literally, by necessity, and nowhere else.
LEAKS="$(grep -rIl --exclude=pack.sh -e '/Users''/' -e '/private/var''/folders/' "$STAGE/$NAME" || true)"
if [ -n "$LEAKS" ]; then
  echo "pack.sh: REFUSING to build — an absolute path leaked into these files:" >&2
  printf '%s\n' "$LEAKS" | sed "s|$STAGE/|  |" >&2
  exit 1
fi

# ---- normalise every varying field ----------------------------------------
find "$STAGE/$NAME" -exec touch -t "$FIXED_MTIME" {} +

( cd "$STAGE" && LC_ALL=C find "$NAME" -type f | LC_ALL=C sort > files.txt )

( cd "$STAGE" && tar --uid 0 --gid 0 --uname "" --gname "" \
      --format ustar -cf archive.tar -T files.txt )
gzip -9 -n -c "$STAGE/archive.tar" > "$OUT.tmp"
mv "$OUT.tmp" "$OUT"

COUNT="$(wc -l < "$STAGE/files.txt" | tr -d ' ')"
printf 'wrote   %s\n' "$OUT"
printf 'files   %s\n' "$COUNT"
printf 'bytes   %s\n' "$(wc -c < "$OUT" | tr -d ' ')"
printf 'sha256  %s\n' "$(shasum -a 256 "$OUT" | cut -d' ' -f1)"
