Files

88 lines
3.4 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# Build the installable plugin zip and check it is actually installable.
#
# Writes build/<slug>.zip, containing a single top-level <slug>/ directory —
# WordPress identifies an installed plugin by that folder name, so a zip that
# unpacks to anything else installs a second copy alongside the first instead
# of updating it.
set -euo pipefail
cd "$(dirname "$0")/.."
. ci/lib.sh
ci_load_conf
version=$(ci_plugin_version)
[ -n "$version" ] || { echo "Could not read Version: from $PLUGIN_MAIN." >&2; exit 1; }
rm -rf build
mkdir -p "build/$PLUGIN_SLUG"
# One list, shared with the hygiene checks and the test site, so a file cannot
# be excluded from the tests and included in the release.
./ci/dist-files.sh | while IFS= read -r f; do
mkdir -p "build/$PLUGIN_SLUG/$(dirname "$f")"
cp "$f" "build/$PLUGIN_SLUG/$f"
done
# `zip` is on GitHub's runners but not in every Gitea runner image, and a
# release that fails on the packaging step is the most annoying possible place
# to discover a missing tool. python3 is present in both.
(
cd build
if command -v zip >/dev/null 2>&1; then
zip -rq "$PLUGIN_SLUG.zip" "$PLUGIN_SLUG"
elif command -v python3 >/dev/null 2>&1; then
python3 -m zipfile -c "$PLUGIN_SLUG.zip" "$PLUGIN_SLUG"
else
echo "Neither zip nor python3 is available to build the archive." >&2
exit 1
fi
)
# Listing entries by name rather than parsing `unzip -l` output, with the same
# python3 fallback as above because unzip is not guaranteed on a runner either.
zip_entries() {
if command -v unzip >/dev/null 2>&1; then
unzip -Z1 "build/$PLUGIN_SLUG.zip"
else
python3 -c 'import sys,zipfile;print("\n".join(zipfile.ZipFile(sys.argv[1]).namelist()))' \
"build/$PLUGIN_SLUG.zip"
fi
}
ci_step "what the zip contains"
zip_entries | sed 's/^/ /'
# 🔴 A failed build must not leave a zip behind. Every exit below this point
# happens AFTER the archive is written, so without this the tree keeps a
# half-built artefact that looks exactly like a good one — and the release step
# takes `build/<slug>.zip` by name, as does anyone publishing by hand. The
# check that caught it: stubbing ci/dist-files.sh empty makes this script exit
# 1 with "The main plugin file is not in the zip", and the zip was still there
# afterwards.
trap 'code=$?; [ "$code" = 0 ] || { rm -f "build/$PLUGIN_SLUG.zip"; echo "Removed the incomplete build/$PLUGIN_SLUG.zip." >&2; }; exit $code' EXIT
ci_step "checks"
zip_entries | grep -qx "$PLUGIN_SLUG/$PLUGIN_MAIN" \
|| { echo "The main plugin file is not in the zip." >&2; exit 1; }
echo " the main file is present"
# A leaked ci/ or tests/ directory is not merely untidy: everything under
# wp-content/plugins/ can in principle be fetched over HTTP from a live shop.
for unwanted in ci tests docs .git .github .gitea; do
if zip_entries | grep -q "^$PLUGIN_SLUG/$unwanted/"; then
echo "The zip contains $unwanted/, which must never reach a shop." >&2
exit 1
fi
done
echo " no tooling, tests or git files leaked in"
# Every entry sits under exactly one top-level directory named for the slug.
strays=$(zip_entries | grep -v "^$PLUGIN_SLUG/" || true)
[ -z "$strays" ] || { echo "Files outside $PLUGIN_SLUG/ in the zip:" >&2; echo "$strays" >&2; exit 1; }
echo " everything unpacks into $PLUGIN_SLUG/"
size=$(du -h "build/$PLUGIN_SLUG.zip" | cut -f1)
echo
echo "build/$PLUGIN_SLUG.zip — version $version, $size"