#!/usr/bin/env bash # # Build an installable WordPress plugin ZIP for Reservair. # # Produces dist/reservair-.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: /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 /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