Files
Reservair/bin/build-zip.sh
Martin Slachta 0d829845c4 initial
2026-06-11 19:03:29 +02:00

111 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Build an installable WordPress plugin ZIP for Reservair.
#
# Produces dist/reservair-<version>.zip containing a single top-level
# `reservair/` directory with ONLY the runtime files: the compiled Gutenberg
# block assets and production-only Composer dependencies.
#
# The build runs in an isolated temp directory, so it never touches your
# working-tree vendor/ or node_modules/ — safe to run locally or in CI.
#
# Usage:
# ./bin/build-zip.sh
#
# Environment overrides:
# OUTPUT_DIR directory the final zip is written to (default: <repo>/dist)
# SKIP_JS "1" to reuse the existing build/ dir (default: build fresh)
# KEEP_TMP "1" to keep the temp build dir for debug (default: removed)
#
set -euo pipefail
# --- locate repo root (this script lives in <root>/bin) ----------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT_DIR"
SLUG="reservair"
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/dist}"
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }
# --- prerequisites -----------------------------------------------------------
need() { command -v "$1" >/dev/null 2>&1 || die "'$1' is required but not installed"; }
need composer
need zip
need rsync
[ "${SKIP_JS:-0}" = "1" ] || need npm
# --- read version from the plugin header (single source of truth) ------------
# `read` consumes only the first match and avoids a `| head` pipe (which would
# SIGPIPE the upstream sed under `pipefail` and abort the script with code 141).
VERSION=""
read -r VERSION < <(sed -n 's/^[[:space:]]*\*\{0,1\}[[:space:]]*Version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "$ROOT_DIR/$SLUG.php") || true
[ -n "$VERSION" ] || die "could not read 'Version:' header from $SLUG.php"
log "Packaging $SLUG version $VERSION"
# --- isolated build dir (never touches your working vendor/ or node_modules) -
TMP_DIR="$(mktemp -d)"
BUILD_DIR="$TMP_DIR/src"
STAGE_DIR="$TMP_DIR/stage/$SLUG"
cleanup() { [ "${KEEP_TMP:-0}" = "1" ] || rm -rf "$TMP_DIR"; }
trap cleanup EXIT
mkdir -p "$BUILD_DIR" "$STAGE_DIR"
# --- copy the source tree into the isolated build dir ------------------------
log "Copying source into isolated build dir"
rsync -a \
--exclude='.git/' \
--exclude='node_modules/' \
--exclude='vendor/' \
--exclude='build/' \
--exclude='dist/' \
--exclude='.wp-env.json' \
./ "$BUILD_DIR/"
# --- compile the Gutenberg block (src/ -> build/) ----------------------------
if [ "${SKIP_JS:-0}" = "1" ]; then
[ -d "$ROOT_DIR/build" ] || die "SKIP_JS=1 but no build/ exists — run 'npm run build' first"
log "SKIP_JS=1 — reusing existing build/"
cp -R "$ROOT_DIR/build" "$BUILD_DIR/build"
else
log "Installing JS deps and building block assets"
( cd "$BUILD_DIR" && npm ci && npm run build )
fi
# --- install production-only PHP deps + regenerate autoloader ----------------
# This strips psalm/phpstan/wordpress-core etc. and rebuilds the classmap that
# loads the plugin's own includes/ classes — vendor/ is required at runtime.
log "Installing production Composer dependencies"
( cd "$BUILD_DIR" && composer install \
--no-dev --optimize-autoloader --classmap-authoritative \
--no-interaction --no-progress )
# --- stage only the runtime files -------------------------------------------
log "Staging runtime files"
RUNTIME=( "$SLUG.php" admin.php uninstall.php readme.txt includes modules build assets vendor )
for item in "${RUNTIME[@]}"; do
if [ -e "$BUILD_DIR/$item" ]; then
cp -R "$BUILD_DIR/$item" "$STAGE_DIR/"
else
log " (skipping missing $item)"
fi
done
# --- fail loudly if anything essential is missing ----------------------------
for required in "$SLUG.php" vendor/autoload.php build/block.json; do
[ -e "$STAGE_DIR/$required" ] || die "packaging incomplete: missing $required"
done
# --- zip it ------------------------------------------------------------------
mkdir -p "$OUTPUT_DIR"
ZIP_PATH="$OUTPUT_DIR/${SLUG}-${VERSION}.zip"
rm -f "$ZIP_PATH"
log "Creating $ZIP_PATH"
( cd "$TMP_DIR/stage" && zip -rq "$ZIP_PATH" "$SLUG" -x '*.DS_Store' )
log "Done → $ZIP_PATH"
# `|| true` keeps an early-closing `head` (SIGPIPE) from failing the script.
unzip -l "$ZIP_PATH" | tail -n +2 | head -n 12 || true