package-amo-source.sh
1#!/usr/bin/env bash2#3# package-amo-source.sh — produce peach-3.6.65-source.zip for AMO review.4#5# Reviewers must be able to reproduce the submitted peach-3.6.65.zip from6# the contents of this archive. The archive is staged into a temporary7# directory using rsync with .amoignore as the exclusion list, then zipped8# with sorted entry order so the archive is reproducible across runs.9#10# This script does not use `rm`. All deletions go through `trash` per the11# project convention; the only filesystem removal happens through `trash`12# acting on the staging directory.1314set -euo pipefail1516VERSION="3.6.65"17ARCHIVE_NAME="peach-${VERSION}-source.zip"1819REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"20cd "$REPO_ROOT"2122# ---- 1. Sanity checks ------------------------------------------------------2324# Working tree should be clean per koh — if it isn't, surface state and stop.25if command -v koh >/dev/null 2>&1; then26 KOH_STATUS_JSON="$(koh status --json 2>/dev/null || true)"27 if printf '%s' "$KOH_STATUS_JSON" | grep -q '"dirty":true'; then28 echo "error: working tree is dirty per koh status. Save or stash before packaging." >&229 echo "$KOH_STATUS_JSON" >&230 exit 131 fi32fi3334# Confirm manifest version matches what we're packaging.35MANIFEST_VERSION="$(node -e 'console.log(JSON.parse(require("fs").readFileSync("manifest.firefox.json","utf8")).version)')"36if [ "$MANIFEST_VERSION" != "$VERSION" ]; then37 echo "error: manifest.firefox.json version is $MANIFEST_VERSION, expected $VERSION" >&238 exit 139fi4041# Confirm .amoignore exists.42if [ ! -f .amoignore ]; then43 echo "error: .amoignore not found at repo root" >&244 exit 145fi4647# ---- 2. Stage into a temp directory ---------------------------------------4849STAGE_DIR="$(mktemp -d -t peach-amo-source.XXXXXX)"50STAGE_SUBDIR="$STAGE_DIR/peach-${VERSION}-source"51mkdir -p "$STAGE_SUBDIR"5253# Use rsync with --exclude-from to apply .amoignore. rsync interprets the54# patterns the same way for the source side, so the result is deterministic55# given the same input tree.56rsync -a \57 --exclude-from=.amoignore \58 --exclude='.amoignore' \59 ./ "$STAGE_SUBDIR/"6061# Drop README.amo.md and .amoignore explicitly at the archive root.62cp README.amo.md "$STAGE_SUBDIR/README.amo.md"63cp .amoignore "$STAGE_SUBDIR/.amoignore"6465# ---- 3. Produce a reproducible zip ----------------------------------------6667OUT_PATH="$REPO_ROOT/$ARCHIVE_NAME"6869# Remove any prior archive at the destination, via `trash` (never `rm`).70if [ -e "$OUT_PATH" ]; then71 trash "$OUT_PATH"72fi7374# Build a sorted file list and feed it to zip via -@ so entry order is75# stable across runs and across filesystems with different traversal orders.76(77 cd "$STAGE_SUBDIR"78 # Use find with -print0 / sort -z for safety, then translate back to79 # newline-terminated for `zip -@`. No filenames in this repo contain80 # newlines, but the NUL pipeline keeps the script robust.81 find . -type f -print0 \82 | LC_ALL=C sort -z \83 | tr '\0' '\n' \84 | zip -X -q -@ "$OUT_PATH"85)8687# ---- 4. Report -------------------------------------------------------------8889SIZE_BYTES="$(stat -f%z "$OUT_PATH" 2>/dev/null || stat -c%s "$OUT_PATH")"90SHA256="$(shasum -a 256 "$OUT_PATH" | awk '{print $1}')"9192echo ""93echo "AMO source archive: $OUT_PATH"94echo " size: $SIZE_BYTES bytes"95echo " sha256: $SHA256"96echo ""9798# ---- 5. Clean up staging via `trash` --------------------------------------99100trash "$STAGE_DIR"