#!/bin/sh # Single source of truth for "what ships inside the XPI", plus the version # derived from it. # # Three consumers used to hand-maintain their own copy of this list, and # keeping three copies of one fact in sync by hand is how issue #2397 happened: # # 1. web-ext's --ignore-files (extension/package.json's four scripts) # 2. the :(exclude) pathspec (ci.yml's extension-version guard) # 3. the rev-list pathspec (the derived version, below) # # They now all read from here. POSIX sh only — CI's run shell is busybox. # # -f (no pathname expansion) is set for the whole script and is load-bearing: # the lists below are iterated with deliberate word-splitting, and without -f # the shell would also GLOB them, expanding `test/**` into whatever files # happen to exist and corrupting the output. A caller's own `set -f` does not # help here — this runs as a separate sh process and does not inherit it. # Callers still need their own `set -f` for the substituted result; the two # guards protect different expansions. set -euf # Paths under extension/ that are NOT packaged into the XPI. # # Split by whether git tracks them: node_modules and web-ext-artifacts are # build/dependency output that never appears in a commit, so they belong in # web-ext's ignore list but would be meaningless in a git pathspec. NOT_PACKAGED_TRACKED='package.json package-lock.json README.md .gitignore vitest.config.js scripts/** test/**' NOT_PACKAGED_BUILD='web-ext-artifacts node_modules' usage() { echo "usage: packaging.sh {ignore|pathspec|version|major-minor|patch}" >&2 exit 2 } # web-ext --ignore-files values, space-separated. # # Callers MUST disable pathname expansion first (`set -f`), or the shell will # glob `test/**` against the working tree before web-ext ever sees the pattern # and silently narrow it to whatever happens to exist right now. cmd_ignore() { echo "$NOT_PACKAGED_TRACKED $NOT_PACKAGED_BUILD" } # git pathspec excluding the non-packaged tracked files, e.g. # :(exclude)extension/package.json :(exclude)extension/test/** # Same `set -f` requirement as above. cmd_pathspec() { for entry in $NOT_PACKAGED_TRACKED; do printf ':(exclude)extension/%s ' "$entry" done echo } # MAJOR.MINOR stays hand-set in manifest.json — it's the part that carries # deliberate meaning. Only the patch component is derived. cmd_major_minor() { root=$(git rev-parse --show-toplevel) grep -E '"version"' "$root/extension/manifest.json" \ | head -1 \ | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([0-9]+)\.([0-9]+).*/\1.\2/' } # Count of commits that touched a PACKAGED extension file. Monotonic on a # branch (the count only grows), which is a correctness requirement, not a # nicety: Firefox refuses to install a version lower than the one present. # # Merge commits need no special handling — git's history simplification already # prunes merges that don't change the pathspec, so --no-merges is a no-op here # (verified on main: both forms return the same count). cmd_patch() { root=$(git rev-parse --show-toplevel) # Unquoted on purpose: the pathspec must word-split into separate args. # Globbing is already off script-wide (set -euf above). # shellcheck disable=SC2046 count=$(cd "$root" && git rev-list --count HEAD -- extension/ $(cmd_pathspec)) echo "$count" } cmd_version() { echo "$(cmd_major_minor).$(cmd_patch)" } [ $# -ge 1 ] || usage case "$1" in ignore) cmd_ignore ;; pathspec) cmd_pathspec ;; version) cmd_version ;; major-minor) cmd_major_minor ;; patch) cmd_patch ;; *) usage ;; esac