- Add --chmod=D755,F644 to rsync (HestiaCP PHP-FPM needs world-readable) - Exclude scripts/.deploy.env from deploy (contains server credentials)
132 lines
4.2 KiB
Bash
Executable File
132 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Safe deploy: never delete server-side runtime data/ files.
|
|
#
|
|
# Configuration (required):
|
|
# DEPLOY_HOST e.g. root@example.com or deploy@example.com
|
|
# DEPLOY_TARGET e.g. /home/user/web/xmrpay.link/public_html
|
|
#
|
|
# Optional hardening:
|
|
# DEPLOY_BACKUP_ENABLE=1 # 1=backup data before deploy, 0=disable
|
|
# DEPLOY_BACKUP_KEEP=14 # number of backup archives to keep
|
|
# DEPLOY_BACKUP_DIR=... # remote backup folder
|
|
# DEPLOY_DRY_RUN=0 # 1=rsync dry-run
|
|
#
|
|
# Optional local config file (not committed):
|
|
# scripts/.deploy.env
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
ENV_FILE="$SCRIPT_DIR/.deploy.env"
|
|
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$ENV_FILE"
|
|
fi
|
|
|
|
HOST="${DEPLOY_HOST:-}"
|
|
TARGET="${DEPLOY_TARGET:-}"
|
|
BACKUP_ENABLE="${DEPLOY_BACKUP_ENABLE:-1}"
|
|
BACKUP_KEEP="${DEPLOY_BACKUP_KEEP:-14}"
|
|
BACKUP_DIR="${DEPLOY_BACKUP_DIR:-$TARGET/../backups/xmrpay-data}"
|
|
DRY_RUN="${DEPLOY_DRY_RUN:-0}"
|
|
|
|
if [[ -z "$HOST" || -z "$TARGET" ]]; then
|
|
echo "Missing deploy configuration." >&2
|
|
echo "Set DEPLOY_HOST and DEPLOY_TARGET (env vars or scripts/.deploy.env)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$BACKUP_ENABLE" == "1" ]]; then
|
|
echo "Creating remote pre-deploy data backup..."
|
|
ssh "$HOST" "
|
|
set -euo pipefail
|
|
TARGET='$TARGET'
|
|
DATA_DIR=\"\$TARGET/data\"
|
|
BACKUP_DIR='$BACKUP_DIR'
|
|
KEEP='$BACKUP_KEEP'
|
|
|
|
mkdir -p \"\$BACKUP_DIR\"
|
|
if [ -d \"\$DATA_DIR\" ]; then
|
|
TS=\$(date +%Y%m%d-%H%M%S)
|
|
ARCHIVE=\"\$BACKUP_DIR/data-\$TS.tgz\"
|
|
tar -C \"\$TARGET\" -czf \"\$ARCHIVE\" data
|
|
echo \"backup_created=\$ARCHIVE\"
|
|
|
|
COUNT=0
|
|
for FILE in \$(ls -1t \"\$BACKUP_DIR\"/data-*.tgz 2>/dev/null || true); do
|
|
COUNT=\$((COUNT + 1))
|
|
if [ \"\$COUNT\" -gt \"\$KEEP\" ]; then
|
|
rm -f \"\$FILE\"
|
|
fi
|
|
done
|
|
else
|
|
echo \"backup_skipped=no_data_dir\"
|
|
fi
|
|
"
|
|
else
|
|
echo "Skipping pre-deploy backup (DEPLOY_BACKUP_ENABLE=0)."
|
|
fi
|
|
|
|
# ── Minify & update SRI hashes ────────────────────────────────────────────────
|
|
echo "Minifying JS..."
|
|
TERSER="${TERSER:-terser}"
|
|
if ! command -v "$TERSER" &>/dev/null; then
|
|
echo "Error: terser not found. Install with: npm i -g terser" >&2
|
|
exit 1
|
|
fi
|
|
"$TERSER" app.js -c -m -o app.min.js
|
|
"$TERSER" i18n.js -c -m -o i18n.min.js
|
|
|
|
echo "Updating SRI hashes..."
|
|
sri_hash() { echo "sha384-$(openssl dgst -sha384 -binary "$1" | openssl base64 -A)"; }
|
|
|
|
HASH_STYLE=$(sri_hash style.css)
|
|
HASH_QRCODE=$(sri_hash lib/qrcode.min.js)
|
|
HASH_I18N=$(sri_hash i18n.min.js)
|
|
HASH_APP=$(sri_hash app.min.js)
|
|
HASH_JSPDF=$(sri_hash lib/jspdf.min.js)
|
|
HASH_CRYPTO=$(sri_hash lib/xmr-crypto.bundle.js)
|
|
|
|
# Update index.html SRI attributes
|
|
sed -i -E \
|
|
-e "s|(style\.css[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_STYLE}|" \
|
|
-e "s|(qrcode\.min\.js[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_QRCODE}|" \
|
|
-e "s|(i18n\.min\.js[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_I18N}|" \
|
|
-e "s|(app\.min\.js[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_APP}|" \
|
|
index.html
|
|
|
|
# Update privacy.html SRI attributes
|
|
sed -i -E \
|
|
-e "s|(style\.css[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_STYLE}|" \
|
|
privacy.html
|
|
|
|
# Update dynamic SRI hashes in app.js and re-minify if changed
|
|
sed -i -E \
|
|
-e "s|(jspdf\.min\.js.*integrity\s*=\s*')sha384-[A-Za-z0-9+/=]+|\1${HASH_JSPDF}|" \
|
|
-e "s|(xmr-crypto\.bundle\.js.*integrity\s*=\s*')sha384-[A-Za-z0-9+/=]+|\1${HASH_CRYPTO}|" \
|
|
app.js
|
|
|
|
# Re-minify app.js (dynamic SRI hashes may have changed)
|
|
"$TERSER" app.js -c -m -o app.min.js
|
|
HASH_APP_FINAL=$(sri_hash app.min.js)
|
|
sed -i -E "s|(app\.min\.js[^\"]*\"\s+integrity=\")sha384-[A-Za-z0-9+/=]+|\1${HASH_APP_FINAL}|" index.html
|
|
|
|
echo "SRI hashes updated."
|
|
|
|
RSYNC_DRY_RUN=""
|
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
RSYNC_DRY_RUN="--dry-run"
|
|
echo "Running in dry-run mode (DEPLOY_DRY_RUN=1)."
|
|
fi
|
|
|
|
rsync -avz --delete --chmod=D755,F644 \
|
|
${RSYNC_DRY_RUN} \
|
|
--exclude '.git' \
|
|
--exclude 'node_modules' \
|
|
--exclude 'data/' \
|
|
--exclude 'scripts/.deploy.env' \
|
|
./ "$HOST:$TARGET"
|
|
|
|
echo "Deploy complete (data/ preserved)."
|