#!/usr/bin/env bash # # package-amo-source.sh — produce peach-3.6.65-source.zip for AMO review. # # Reviewers must be able to reproduce the submitted peach-3.6.65.zip from # the contents of this archive. The archive is staged into a temporary # directory using rsync with .amoignore as the exclusion list, then zipped # with sorted entry order so the archive is reproducible across runs. # # This script does not use `rm`. All deletions go through `trash` per the # project convention; the only filesystem removal happens through `trash` # acting on the staging directory. set -euo pipefail VERSION="3.6.65" ARCHIVE_NAME="peach-${VERSION}-source.zip" REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" # ---- 1. Sanity checks ------------------------------------------------------ # Working tree should be clean per koh — if it isn't, surface state and stop. if command -v koh >/dev/null 2>&1; then KOH_STATUS_JSON="$(koh status --json 2>/dev/null || true)" if printf '%s' "$KOH_STATUS_JSON" | grep -q '"dirty":true'; then echo "error: working tree is dirty per koh status. Save or stash before packaging." >&2 echo "$KOH_STATUS_JSON" >&2 exit 1 fi fi # Confirm manifest version matches what we're packaging. MANIFEST_VERSION="$(node -e 'console.log(JSON.parse(require("fs").readFileSync("manifest.firefox.json","utf8")).version)')" if [ "$MANIFEST_VERSION" != "$VERSION" ]; then echo "error: manifest.firefox.json version is $MANIFEST_VERSION, expected $VERSION" >&2 exit 1 fi # Confirm .amoignore exists. if [ ! -f .amoignore ]; then echo "error: .amoignore not found at repo root" >&2 exit 1 fi # ---- 2. Stage into a temp directory --------------------------------------- STAGE_DIR="$(mktemp -d -t peach-amo-source.XXXXXX)" STAGE_SUBDIR="$STAGE_DIR/peach-${VERSION}-source" mkdir -p "$STAGE_SUBDIR" # Use rsync with --exclude-from to apply .amoignore. rsync interprets the # patterns the same way for the source side, so the result is deterministic # given the same input tree. rsync -a \ --exclude-from=.amoignore \ --exclude='.amoignore' \ ./ "$STAGE_SUBDIR/" # Drop README.amo.md and .amoignore explicitly at the archive root. cp README.amo.md "$STAGE_SUBDIR/README.amo.md" cp .amoignore "$STAGE_SUBDIR/.amoignore" # ---- 3. Produce a reproducible zip ---------------------------------------- OUT_PATH="$REPO_ROOT/$ARCHIVE_NAME" # Remove any prior archive at the destination, via `trash` (never `rm`). if [ -e "$OUT_PATH" ]; then trash "$OUT_PATH" fi # Build a sorted file list and feed it to zip via -@ so entry order is # stable across runs and across filesystems with different traversal orders. ( cd "$STAGE_SUBDIR" # Use find with -print0 / sort -z for safety, then translate back to # newline-terminated for `zip -@`. No filenames in this repo contain # newlines, but the NUL pipeline keeps the script robust. find . -type f -print0 \ | LC_ALL=C sort -z \ | tr '\0' '\n' \ | zip -X -q -@ "$OUT_PATH" ) # ---- 4. Report ------------------------------------------------------------- SIZE_BYTES="$(stat -f%z "$OUT_PATH" 2>/dev/null || stat -c%s "$OUT_PATH")" SHA256="$(shasum -a 256 "$OUT_PATH" | awk '{print $1}')" echo "" echo "AMO source archive: $OUT_PATH" echo " size: $SIZE_BYTES bytes" echo " sha256: $SHA256" echo "" # ---- 5. Clean up staging via `trash` -------------------------------------- trash "$STAGE_DIR"