#!/usr/bin/env sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) PLUGIN_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd -P) DIST_DIR="$PLUGIN_DIR/dist" CONTRACT="$SCRIPT_DIR/production-contract.mjs" CANONICAL_STAGING='' COMPATIBILITY_STAGING='' TARGET_PARENT='' cleanup() { status=$? trap - EXIT HUP INT TERM for staging in "$CANONICAL_STAGING" "$COMPATIBILITY_STAGING"; do [ -n "$staging" ] || continue if [ -n "$TARGET_PARENT" ]; then case "$staging" in "$TARGET_PARENT"/.eln-record-filler.staging.* | "$TARGET_PARENT"/.eln-template-filler.staging.*) if [ -d "$staging" ] && ! rm -rf "$staging"; then printf '%s\n' "Failed to clean release staging: $staging" >&2 [ "$status" -ne 0 ] || status=1 fi ;; *) printf '%s\n' 'Refusing to clean an unverified release staging path' >&2 [ "$status" -ne 0 ] || status=1 ;; esac fi done exit "$status" } trap cleanup EXIT trap 'exit 129' HUP trap 'exit 130' INT trap 'exit 143' TERM fail() { printf '%s\n' "$1" >&2 exit "${2:-1}" } for command in diff mktemp node; do command -v "$command" >/dev/null 2>&1 || fail "$command is required to create a release" done [ -f "$CONTRACT" ] || fail "Missing production contract validator: $CONTRACT" [ -n "${ONLYOFFICE_RELEASE_ROOT:-}" ] || fail 'ONLYOFFICE_RELEASE_ROOT is required' RELEASE_ROOT=$(node -e ' const fs = require("node:fs"); const path = require("node:path"); let existing = path.resolve(process.argv[1]); const missing = []; while (!fs.existsSync(existing)) { missing.unshift(path.basename(existing)); const parent = path.dirname(existing); if (parent === existing) process.exit(2); existing = parent; } process.stdout.write(path.join(fs.realpathSync(existing), ...missing)); ' "$ONLYOFFICE_RELEASE_ROOT") [ "$RELEASE_ROOT" != '/' ] || fail 'ONLYOFFICE_RELEASE_ROOT must not be the filesystem root' [ "$RELEASE_ROOT" != "$PLUGIN_DIR" ] || fail 'ONLYOFFICE_RELEASE_ROOT must not be the plugin directory' case "$RELEASE_ROOT" in "$DIST_DIR" | "$DIST_DIR"/*) fail 'ONLYOFFICE_RELEASE_ROOT must not be dist or a directory inside dist' ;; esac [ -d "$DIST_DIR" ] || fail "Missing fresh production build directory: $DIST_DIR" node "$CONTRACT" validate-dist "$DIST_DIR" mkdir -p "$RELEASE_ROOT" CANONICAL_TARGET=$(node "$CONTRACT" prepare-release-path "$RELEASE_ROOT" eln-record-filler) COMPATIBILITY_TARGET=$(node "$CONTRACT" prepare-release-path "$RELEASE_ROOT" eln-template-filler) TARGET_PARENT=${CANONICAL_TARGET%/*} [ "${COMPATIBILITY_TARGET%/*}" = "$TARGET_PARENT" ] || fail 'Release targets must share one parent' CANONICAL_STAGING=$(mktemp -d "$TARGET_PARENT/.eln-record-filler.staging.XXXXXX") COMPATIBILITY_STAGING=$(mktemp -d "$TARGET_PARENT/.eln-template-filler.staging.XXXXXX") node "$CONTRACT" copy-release "$DIST_DIR" "$CANONICAL_STAGING" node "$CONTRACT" validate-release "$CANONICAL_STAGING" node "$CONTRACT" copy-release "$DIST_DIR" "$COMPATIBILITY_STAGING" node "$CONTRACT" validate-release "$COMPATIBILITY_STAGING" check_existing_target() { target=$1 staging=$2 if [ -e "$target" ] || [ -L "$target" ]; then [ ! -L "$target" ] && [ -d "$target" ] || fail "Immutable release target must be an ordinary directory: $target" set +e diff -qr "$target" "$staging" >/dev/null 2>&1 diff_status=$? set -e case "$diff_status" in 0) node "$CONTRACT" check-release "$target" ;; 1) fail "Immutable release already exists with different content: $target" ;; *) exit "$diff_status" ;; esac fi } check_existing_target "$CANONICAL_TARGET" "$CANONICAL_STAGING" check_existing_target "$COMPATIBILITY_TARGET" "$COMPATIBILITY_STAGING" if [ ! -e "$CANONICAL_TARGET" ]; then mv "$CANONICAL_STAGING" "$CANONICAL_TARGET" CANONICAL_STAGING='' fi if [ ! -e "$COMPATIBILITY_TARGET" ]; then mv "$COMPATIBILITY_STAGING" "$COMPATIBILITY_TARGET" COMPATIBILITY_STAGING='' fi node "$CONTRACT" check-release "$CANONICAL_TARGET" node "$CONTRACT" check-release "$COMPATIBILITY_TARGET" printf '%s\n' "$CANONICAL_TARGET"