Files
BelgianBitcoinEmbassy/scripts/deploy-frontend.sh
T
bbeandCursor ede8817583 fix: harden deploys and close top security holes after /events outage
Isolate next dev from production .next, add build-guard/atomic deploy/health
watchdog, error boundaries, and fix JWT startup, meetup leaks, media path
traversal, SVG/memory uploads, and JSON-LD escaping.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 19:14:57 +02:00

66 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Atomic frontend deploy for the Belgian Bitcoin Embassy site.
#
# Builds into a scratch dist dir, verifies the artifact, then swaps it into place
# as .next in a single mv (rename is atomic on the same filesystem). The running
# server therefore never sees a half-written .next, and a failed build leaves the
# current production build untouched.
#
# Usage:
# scripts/deploy-frontend.sh # build, verify, swap, restart service
# scripts/deploy-frontend.sh --no-restart # build + swap, skip systemctl
#
# Requires sudo only for the final `systemctl restart` (skip with --no-restart).
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
FRONTEND_DIR="${REPO_DIR}/frontend"
BUILD_DIR="${FRONTEND_DIR}/.next-build"
PROD_DIR="${FRONTEND_DIR}/.next"
BACKUP_DIR="${FRONTEND_DIR}/.next-prev"
MIN_FREE_MB="${MIN_FREE_MB:-600}"
RESTART=1
[ "${1:-}" = "--no-restart" ] && RESTART=0
log() { echo "deploy-frontend: $*"; }
die() { echo "deploy-frontend: ERROR: $*" >&2; exit 1; }
cd "${FRONTEND_DIR}"
# A production `next build` on this box needs headroom; bail early with a clear
# message rather than getting OOM-killed halfway through.
free_mb="$(free -m | awk '/^Mem:/ {print $7}')"
if [ -n "${free_mb}" ] && [ "${free_mb}" -lt "${MIN_FREE_MB}" ]; then
die "only ${free_mb}MB available memory (< ${MIN_FREE_MB}MB). Free memory or set MIN_FREE_MB=... to override."
fi
log "building into ${BUILD_DIR} ..."
rm -rf "${BUILD_DIR}"
NEXT_DIST_DIR=".next-build" npm run build
log "verifying fresh build ..."
NEXT_DIR="${BUILD_DIR}"
[ -f "${NEXT_DIR}/BUILD_ID" ] || die "build produced no BUILD_ID"
[ -f "${NEXT_DIR}/build-manifest.json" ] || die "build produced no build-manifest.json"
[ -f "${NEXT_DIR}/prerender-manifest.json" ] || die "build produced no prerender-manifest.json"
[ -d "${NEXT_DIR}/static/development" ] && die "fresh build contains static/development (dev contamination)"
grep -q "static/development" "${NEXT_DIR}/build-manifest.json" && die "build-manifest references static/development"
log "swapping ${BUILD_DIR} -> ${PROD_DIR} (atomic) ..."
rm -rf "${BACKUP_DIR}"
[ -d "${PROD_DIR}" ] && mv "${PROD_DIR}" "${BACKUP_DIR}"
mv "${BUILD_DIR}" "${PROD_DIR}"
log "deployed BUILD_ID=$(cat "${PROD_DIR}/BUILD_ID")"
if [ "${RESTART}" -eq 1 ]; then
log "restarting bbe-frontend.service ..."
sudo systemctl restart bbe-frontend
log "done. previous build kept at ${BACKUP_DIR}"
else
log "skipped restart (--no-restart). previous build kept at ${BACKUP_DIR}"
fi