#!/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