fix: harden update pipeline and restore a clean production build
- update-Nitrov3.sh: build CMS into .next-staging and swap atomically so a failed build never takes the live site down; auto-merge new variables from .env.example; validate env for duplicates/broken lines; restart the emulator/CMS only when rebuilt or unhealthy; fix step renumbering - next.config.ts: support NEXT_DIST_DIR for staged production builds - fix all TS errors (unused imports, missing tryDownloadCandidates helper) so tsc and the production build pass clean - add Dockerfile/.dockerignore and switch docker-compose to a CMS container - include prevailing UI/refactor changes (SurfaceCard, ticketing, tsconfig)
This commit is contained in:
1 parent
750fcb2e5c
commit
944e8ff1d8
26 files changed
+427
-876
No files matched your search
+304
-21
@@ -56,6 +56,9 @@ SELECTIVE_UPDATE=""
|
||||
DRY_RUN=false
|
||||
HAD_UPDATES=false
|
||||
NITRO_BUILT=false
|
||||
EMULATOR_BUILT=false
|
||||
CMS_BUILT=false
|
||||
NGINX_CHANGED=false
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
UPDATED_REPOS=()
|
||||
@@ -116,6 +119,18 @@ BACKUP_DIR="${NITRO_BACKUP_DIR:-$EMULATOR_DIR/Database/Database Updates/backups}
|
||||
NITRO_CLIENT="${NITRO_CLIENT_SRC:-/var/www/Octane}"
|
||||
NITRO_RENDERER="${NITRO_RENDERER_SRC:-/var/www/Octane-Renderer}"
|
||||
NITRO_BRANCH="${NITRO_BRANCH:-}"
|
||||
# CMS (EpicNextCMS / atom-nexst) — the Next.js backend that serves the hotel.
|
||||
# It lives next to this script. We never git-stash it automatically (it may
|
||||
# carry uncommitted local work); see NITRO_CMS_PULL to allow opt-in pulls.
|
||||
CMS_DIR="${NITRO_CMS_DIR:-$SCRIPT_DIR}"
|
||||
CMS_PM2_NAME="${NITRO_CMS_PM2_NAME:-epic-next-app}"
|
||||
CMS_PORT="${NITRO_CMS_PORT:-3002}"
|
||||
CMS_INSTALL_CMD="${NITRO_CMS_INSTALL_CMD:-pnpm install --frozen-lockfile}"
|
||||
CMS_BUILD_CMD="${NITRO_CMS_BUILD_CMD:-pnpm build}"
|
||||
CMS_HEALTH_RETRIES="${NITRO_CMS_HEALTH_RETRIES:-18}"
|
||||
CMS_HEALTH_INTERVAL="${NITRO_CMS_HEALTH_INTERVAL:-5}"
|
||||
# 1 = also git-pull the CMS before building (stashes local work if dirty).
|
||||
CMS_PULL="${NITRO_CMS_PULL:-0}"
|
||||
# Dedicated per-repo yarn cache folders. The renderer + client installs run in
|
||||
# parallel and yarn 1.22 has no cache locking, so a single shared cache gets
|
||||
# corrupted and node_modules is left incomplete (vite/pixi.js missing) while
|
||||
@@ -464,6 +479,26 @@ load_language() {
|
||||
["Repos:"]="Repos:"
|
||||
["Yes"]="Ja"
|
||||
["No"]="Nee"
|
||||
# ── NEW (v8.1) ────────────────────────────────────────
|
||||
["Update & Build CMS"]="Update & Bouw CMS"
|
||||
["Environment %s is up to date"]="Omgeving %s is up-to-date"
|
||||
["%s new variable(s) added to %s"]="%s nieuwe variabele(n) toegevoegd aan %s"
|
||||
["No .env.example in %s — skipping env sync"]="Geen .env.example in %s — env-sync overgeslagen"
|
||||
["No .env file in %s"]="Geen .env-bestand in %s"
|
||||
["Duplicate key(s) in %s: %s"]="Dubbele sleutel(s) in %s: %s"
|
||||
["Missing variable(s) in %s: %s"]="Ontbrekende variabele(n) in %s: %s"
|
||||
["Invalid .env — fix before next startup"]="Ongeldig .env — corrigeer vóór de volgende start"
|
||||
["CMS build complete"]="CMS-build voltooid"
|
||||
["CMS: already up to date"]="CMS: al up-to-date"
|
||||
["CMS health check passed (port %s listening)"]="CMS gezondheidscheck geslaagd (poort %s luistert)"
|
||||
["CMS not healthy after %s retries (port %s)"]="CMS niet gezond na %s pogingen (poort %s)"
|
||||
["%s restarted"]="%s herstart"
|
||||
["%s already healthy — no restart needed"]="%s al gezond — herstart niet nodig"
|
||||
["CMS unhealthy — restarting %s"]="CMS niet gezond — %s opnieuw starten"
|
||||
["No %s/package.json — skipping CMS"]="Geen %s/package.json — CMS overgeslagen"
|
||||
["CMS git pull disabled (NITRO_CMS_PULL=0) — building from current tree"]="CMS git-pull uitgeschakeld (NITRO_CMS_PULL=0) — bouwen vanuit huidige boom"
|
||||
["Environment sync & validation..."]="Omgeving synchroniseren & valideren..."
|
||||
["No .env.example in"]="Geen .env.example in"
|
||||
) ;;
|
||||
fr) _T=(
|
||||
# ── MENU ──────────────────────────────────────────────
|
||||
@@ -1172,6 +1207,107 @@ validate_configs() {
|
||||
[ "$errors" -eq 0 ] && ok "All config files valid" || warn "$errors config(s) invalid"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# ENV FILE SYNC & VALIDATION
|
||||
# =============================================================================
|
||||
# Merge any keys documented in <dir>/.env.example but missing from the live
|
||||
# <dir>/.env so new options introduced upstream are filled in with their
|
||||
# default values on every update. Existing values are ALWAYS preserved.
|
||||
sync_env_file() {
|
||||
local base_dir="${1:-$SCRIPT_DIR}"
|
||||
local ex="$base_dir/.env.example" en="$base_dir/.env"
|
||||
[ -f "$ex" ] || { info "No .env.example in %s — skipping env sync" "$(basename "$base_dir")"; return 0; }
|
||||
local tmp; tmp=$(mktemp)
|
||||
env_example="$ex" env_file="$en" env_added="$tmp" python3 -c '
|
||||
import os, re
|
||||
ex, en, out = os.environ["env_example"], os.environ["env_file"], os.environ["env_added"]
|
||||
seen = set()
|
||||
if os.path.exists(en):
|
||||
with open(en, encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=", line)
|
||||
if m:
|
||||
seen.add(m.group(1))
|
||||
additions = []
|
||||
with open(ex, encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$", line)
|
||||
if not m:
|
||||
continue
|
||||
key = m.group(1)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
value = m.group(2).strip()
|
||||
if value and not (value[0] in "\"\x27" or value.startswith("${")):
|
||||
value = "\"" + value + "\""
|
||||
additions.append(f"{key}={value}\n")
|
||||
with open(out, "w", encoding="utf-8") as f:
|
||||
f.writelines(additions)
|
||||
' 2>/dev/null || true
|
||||
if [ -s "$tmp" ]; then
|
||||
if {
|
||||
printf "\n# --- Auto-added by %s on %s ---\n" "$SCRIPT_NAME" "$(date +%F)"
|
||||
cat "$tmp"
|
||||
} >> "$en" 2>/dev/null; then
|
||||
# Re-load new values so the rest of the run sees them.
|
||||
set -a; . "$en"; set +a
|
||||
ok "%s new variable(s) added to %s" "$(wc -l < "$tmp")" "$(basename "$en")"
|
||||
else
|
||||
warn "Cannot update %s (no write permission) — add the %s variable(s) manually" \
|
||||
"$(basename "$en")" "$(wc -l < "$tmp")"
|
||||
fi
|
||||
else
|
||||
ok "Environment %s is up to date" "$(basename "$en")"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Sanity-check a .env file for the failure modes that actually break a deploy:
|
||||
# duplicate keys, and a value concatenated onto another key (missing newline),
|
||||
# e.g. FLARESOLVERR_URL=http://localhost:8191AUTH_TRUST_HOST="..."
|
||||
validate_env_file() {
|
||||
local base_dir="${1:-$SCRIPT_DIR}"
|
||||
local en="$base_dir/.env"
|
||||
[ -f "$en" ] || { warn "No .env file in %s" "$(basename "$base_dir")"; return 1; }
|
||||
local issues; issues=$(python3 - "$en" <<'PY'
|
||||
import sys, re
|
||||
path = sys.argv[1]
|
||||
out = []
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
for i, line in enumerate(f, 1):
|
||||
s = line.strip()
|
||||
if not s or s.startswith("#") or s.startswith("export "):
|
||||
continue
|
||||
m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$", s)
|
||||
if not m:
|
||||
out.append(f"line {i}: unparseable {s[:60]}")
|
||||
continue
|
||||
val = m.group(2)
|
||||
# URL immediately followed by an uppercase KEY= (missing newline).
|
||||
if re.search(r"https?://[^\s\"']+?[A-Z][A-Z0-9_]{2,40}\s*=", val):
|
||||
out.append(f"line {i}: concatenated value (missing newline) {s[:90]}")
|
||||
PY
|
||||
)
|
||||
if [ -n "$issues" ]; then
|
||||
while IFS= read -r line; do warn "%s" "$line"; done <<< "$issues"
|
||||
fail "Invalid .env — fix before next startup"
|
||||
return 1
|
||||
fi
|
||||
local dup; dup=$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*=' "$en" 2>/dev/null | sort | uniq -d | tr '\n' ' ')
|
||||
if [ -n "$dup" ]; then
|
||||
warn "Duplicate key(s) in %s: %s" "$(basename "$en")" "$dup"
|
||||
fi
|
||||
# Missing critical vars commonly needed for a healthy boot.
|
||||
local req=(DATABASE_URL REDIS_URL PORT HOTEL_NAME)
|
||||
local miss=()
|
||||
for r in "${req[@]}"; do
|
||||
grep -qE "^[[:space:]]*${r}[[:space:]]*=" "$en" || miss+=("$r")
|
||||
done
|
||||
[ ${#miss[@]} -gt 0 ] && warn "Missing variable(s) in %s: %s" "$(basename "$en")" "${miss[*]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BINARY BACKUP & ROLLBACK
|
||||
# =============================================================================
|
||||
@@ -1557,20 +1693,32 @@ auto_setup_missing_repos() {
|
||||
}
|
||||
|
||||
# Copy example config files to their runtime names, handling renamed files
|
||||
# in the Octane repo (renderer-config..jsonc.example.json etc.)
|
||||
# in the Octane repo (renderer-config..jsonc.example.json etc.). Existing
|
||||
# runtime files are MERGED via merge-config.cjs (new keys filled in, local
|
||||
# values preserved) instead of being skipped — this is what keeps configs
|
||||
# "gecompleteerd" when upstream adds new options.
|
||||
_setup_config_files() {
|
||||
local src_dir="$NITRO_SRC_DIR"
|
||||
[ ! -d "$src_dir" ] && mkdir -p "$src_dir" 2>/dev/null
|
||||
local copied=0
|
||||
local ms="$SCRIPT_DIR/scripts/merge-config.cjs"
|
||||
local merge_or_copy
|
||||
merge_or_copy() {
|
||||
local ex="$1" tgt="$2"
|
||||
if [ -f "$tgt" ]; then
|
||||
node "$ms" "$ex" "$tgt" 2>/dev/null || true
|
||||
else
|
||||
cp "$ex" "$tgt" 2>/dev/null || return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Standard *.example files (ui-config, client-mode, etc.)
|
||||
for ef in "$src_dir"/*.example; do
|
||||
[ -f "$ef" ] || continue
|
||||
local b=$(basename "$ef"); local tn="${b%.example}"
|
||||
case "$tn" in *.*) ;; *) tn="$tn.json" ;; esac
|
||||
if [ ! -f "$src_dir/$tn" ]; then
|
||||
cp "$ef" "$src_dir/$tn" 2>/dev/null && copied=$((copied + 1))
|
||||
fi
|
||||
if merge_or_copy "$ef" "$src_dir/$tn"; then copied=$((copied + 1)); fi
|
||||
done
|
||||
|
||||
# Handle renamed renderer config files
|
||||
@@ -1578,21 +1726,21 @@ _setup_config_files() {
|
||||
# Runtime needs: renderer-config.jsonc (JSONC) or renderer-config.json
|
||||
local rc_jsonc="$src_dir/renderer-config..jsonc.example.json"
|
||||
local rc_json="$src_dir/renderer-config.json.example.json"
|
||||
if [ -f "$rc_jsonc" ] && [ ! -f "$src_dir/renderer-config.jsonc" ]; then
|
||||
cp "$rc_jsonc" "$src_dir/renderer-config.jsonc" 2>/dev/null && copied=$((copied + 1))
|
||||
if [ -f "$rc_jsonc" ]; then
|
||||
if merge_or_copy "$rc_jsonc" "$src_dir/renderer-config.jsonc"; then copied=$((copied + 1)); fi
|
||||
fi
|
||||
if [ -f "$rc_json" ] && [ ! -f "$src_dir/renderer-config.json" ]; then
|
||||
cp "$rc_json" "$src_dir/renderer-config.json" 2>/dev/null && copied=$((copied + 1))
|
||||
if [ -f "$rc_json" ]; then
|
||||
if merge_or_copy "$rc_json" "$src_dir/renderer-config.json"; then copied=$((copied + 1)); fi
|
||||
fi
|
||||
|
||||
# Copy to gamedata config dir too
|
||||
# Merge/copy to gamedata config dir too
|
||||
if [ -d "$GAMEDATA_CONF_DIR" ]; then
|
||||
for f in "$src_dir"/*.json "$src_dir"/*.jsonc; do
|
||||
[ -f "$f" ] || continue
|
||||
local fn=$(basename "$f")
|
||||
# Skip example/source files
|
||||
case "$fn" in *.example*|*.example) continue ;; esac
|
||||
[ ! -f "$GAMEDATA_CONF_DIR/$fn" ] && cp "$f" "$GAMEDATA_CONF_DIR/$fn" 2>/dev/null || true
|
||||
merge_or_copy "$f" "$GAMEDATA_CONF_DIR/$fn" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -1607,7 +1755,7 @@ _setup_config_files() {
|
||||
done
|
||||
fi
|
||||
|
||||
ok "%s config file(s) copied" "$copied"
|
||||
ok "%s config file(s) copied/synced" "$copied"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
@@ -1625,6 +1773,13 @@ preflight_check() {
|
||||
|
||||
ok "All commands available"
|
||||
|
||||
# Keep .env complete: fill in any new variables documented in
|
||||
# .env.example and validate the result before touching anything.
|
||||
info "Environment sync & validation..."
|
||||
sync_env_file "$SCRIPT_DIR" || true
|
||||
validate_env_file "$SCRIPT_DIR" || fatal=true
|
||||
$fatal && die "Invalid .env — fix before next startup"
|
||||
|
||||
# Auto-clone missing repos instead of dying
|
||||
local missing_repos=false
|
||||
for d in "$NITRO_RENDERER" "$NITRO_CLIENT"; do
|
||||
@@ -1657,7 +1812,7 @@ preflight_check() {
|
||||
# UPDATE FUNCTIONS
|
||||
# =============================================================================
|
||||
update_emulator() {
|
||||
step 2 8 "Update & Build Emulator"
|
||||
step 2 9 "Update & Build Emulator"
|
||||
local grc=1
|
||||
if git_update "$EMULATOR_REPO" "$NITRO_BRANCH"; then
|
||||
grc=0
|
||||
@@ -1687,6 +1842,7 @@ update_emulator() {
|
||||
fi
|
||||
local jar=$(find target -maxdepth 1 -name 'Polaris-*-jar-with-dependencies.jar' -printf '%T@ %p\n' 2>/dev/null | sort -rn | sed -n '1s/^[0-9.]* //p' | xargs -r basename 2>/dev/null || echo "")
|
||||
[ -z "$jar" ] && die "No jar found"
|
||||
EMULATOR_BUILT=true
|
||||
ok "Jar: %s" "$jar"
|
||||
cd target/
|
||||
cat > emulator << EOJ
|
||||
@@ -1705,7 +1861,7 @@ EOJ
|
||||
}
|
||||
|
||||
update_renderer() {
|
||||
step 3 8 "Update Octane-Renderer"
|
||||
step 3 9 "Update Octane-Renderer"
|
||||
local grc=1
|
||||
if git_update "$NITRO_RENDERER" "$NITRO_BRANCH"; then
|
||||
grc=0
|
||||
@@ -1746,7 +1902,7 @@ update_renderer() {
|
||||
}
|
||||
|
||||
update_client() {
|
||||
step 4 8 "Update & Build Octane"
|
||||
step 4 9 "Update & Build Octane"
|
||||
local grc=1
|
||||
if git_update "$NITRO_CLIENT" "$NITRO_BRANCH"; then
|
||||
grc=0
|
||||
@@ -1801,8 +1957,96 @@ update_client() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Does the CMS need a rebuild? True when the build output is missing or when
|
||||
# any source/package file is newer than the last successful build.
|
||||
needs_cms_build() {
|
||||
local bid="$CMS_DIR/.next/BUILD_ID"
|
||||
[ -f "$bid" ] || return 0
|
||||
local newer
|
||||
newer=$(find "$CMS_DIR/src" "$CMS_DIR/package.json" "$CMS_DIR/pnpm-lock.yaml" \
|
||||
"$CMS_DIR/next.config.ts" "$CMS_DIR/next.config.mjs" "$CMS_DIR/next.config.js" \
|
||||
"$CMS_DIR/tsconfig.json" -newer "$bid" 2>/dev/null | head -1)
|
||||
[ -n "$newer" ]
|
||||
}
|
||||
|
||||
# Build (and optionally pull) the CMS. Unlike the git repos this NEVER stashes
|
||||
# local work: the CMS may carry uncommitted files that are intentionally live.
|
||||
# Set NITRO_CMS_PULL=1 to also run a safe `git pull --ff-only` first.
|
||||
update_cms() {
|
||||
step 5 9 "Update & Build CMS"
|
||||
[ -f "$CMS_DIR/package.json" ] || { info "No %s/package.json — skipping CMS" "$(basename "$CMS_DIR")"; return 0; }
|
||||
local needs=false
|
||||
if [ "$CMS_PULL" = "1" ] && [ -d "$CMS_DIR/.git" ]; then
|
||||
local grc=1
|
||||
if git_update "$CMS_DIR" "$NITRO_BRANCH"; then
|
||||
grc=0
|
||||
else
|
||||
grc=$?
|
||||
fi
|
||||
if [ $grc -eq 2 ]; then
|
||||
warn "CMS git update failed"
|
||||
return 1
|
||||
fi
|
||||
[ $grc -eq 0 ] && needs=true
|
||||
elif [ "$CMS_PULL" != "1" ]; then
|
||||
info "CMS git pull disabled (NITRO_CMS_PULL=0) — building from current tree"
|
||||
fi
|
||||
needs_cms_build && needs=true
|
||||
if [ "$needs" = true ]; then
|
||||
# Build into a staging dir and swap atomically: the live CMS keeps
|
||||
# serving from .next the whole time, and a failed or interrupted
|
||||
# build can never break the running site.
|
||||
local staging="$CMS_DIR/.next-staging"
|
||||
rm -rf "$staging" 2>/dev/null || true
|
||||
spinner_start "Building CMS..."
|
||||
local rc=0
|
||||
( cd "$CMS_DIR" && NEXT_DIST_DIR=".next-staging" eval "$CMS_INSTALL_CMD" >> "$LOG_FILE" 2>&1 \
|
||||
&& NEXT_DIST_DIR=".next-staging" eval "$CMS_BUILD_CMD" >> "$LOG_FILE" 2>&1 ) || rc=$?
|
||||
if [ $rc -eq 0 ] && [ -s "$staging/BUILD_ID" ]; then
|
||||
rm -rf "$CMS_DIR/.next" 2>/dev/null || true
|
||||
mv "$staging" "$CMS_DIR/.next" 2>/dev/null
|
||||
if [ -s "$CMS_DIR/.next/BUILD_ID" ]; then
|
||||
spinner_stop ok
|
||||
ok "CMS build complete"
|
||||
CMS_BUILT=true
|
||||
else
|
||||
rm -rf "$staging" 2>/dev/null || true
|
||||
spinner_stop fail
|
||||
warn "CMS build swap failed — live build untouched"
|
||||
fi
|
||||
else
|
||||
rm -rf "$staging" 2>/dev/null || true
|
||||
spinner_stop fail
|
||||
tail -40 "$LOG_FILE" | grep -E '(error|Error|ERROR|Failed|BUILD)' || true
|
||||
warn "CMS build failed — live build untouched, see %s" "$LOG_FILE"
|
||||
fi
|
||||
else
|
||||
info "CMS: already up to date"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Wait for the CMS (Next.js on port $CMS_PORT) to accept connections.
|
||||
wait_for_cms_health() {
|
||||
local hc=0
|
||||
while [ "$hc" -lt "$CMS_HEALTH_RETRIES" ]; do
|
||||
if command -v nc &>/dev/null && nc -z 127.0.0.1 "$CMS_PORT" -w 3 2>/dev/null; then
|
||||
ok "CMS health check passed (port %s listening)" "$CMS_PORT"
|
||||
return 0
|
||||
fi
|
||||
if timeout 3 bash -c "exec 3<>/dev/tcp/127.0.0.1/$CMS_PORT" 2>/dev/null; then
|
||||
ok "CMS health check passed (port %s listening)" "$CMS_PORT"
|
||||
return 0
|
||||
fi
|
||||
hc=$((hc+1))
|
||||
[ "$hc" -lt "$CMS_HEALTH_RETRIES" ] && sleep "$CMS_HEALTH_INTERVAL"
|
||||
done
|
||||
warn "CMS not healthy after %s retries (port %s)" "$CMS_HEALTH_RETRIES" "$CMS_PORT"
|
||||
return 1
|
||||
}
|
||||
|
||||
sync_configs() {
|
||||
step 5 8 "Sync Configurations"
|
||||
step 6 9 "Sync Configurations"
|
||||
mkdir -p "$GAMEDATA_CONF_DIR"
|
||||
|
||||
# Backup current configs before overwriting
|
||||
@@ -1887,7 +2131,7 @@ sync_configs() {
|
||||
}
|
||||
|
||||
do_cleanup() {
|
||||
step 6 8 "Cleanup"
|
||||
step 7 9 "Cleanup"
|
||||
local lc=$(find "$EMULATOR_DIR" -name "*.log" -mtime +14 2>/dev/null | wc -l)
|
||||
find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null || true
|
||||
# Clean old config backups (keep 10 most recent)
|
||||
@@ -1926,7 +2170,7 @@ fix_perms() {
|
||||
}
|
||||
|
||||
do_permissions() {
|
||||
step 7 8 "Set Permissions"
|
||||
step 8 9 "Set Permissions"
|
||||
fix_perms
|
||||
}
|
||||
|
||||
@@ -2272,13 +2516,19 @@ EOF
|
||||
}
|
||||
|
||||
do_restart() {
|
||||
step 8 8 "Restart Services"
|
||||
step 9 9 "Restart Services"
|
||||
ensure_gamedata_redirect
|
||||
ensure_default_server
|
||||
ensure_ws_proxy
|
||||
ensure_api_proxy
|
||||
if systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then
|
||||
sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" || warn "Restart failed"
|
||||
# Only bounce the emulator when it was genuinely rebuilt this run, or
|
||||
# when it is not actually serving — "nothing changed" stays online.
|
||||
if [ "$EMULATOR_BUILT" = true ] || ! emulator_port_open; then
|
||||
sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" || warn "Restart failed"
|
||||
else
|
||||
ok "$EMULATOR_SERVICE already healthy — no restart needed"
|
||||
fi
|
||||
if ! wait_for_emulator_health; then
|
||||
warn "Emulator not healthy after %s retries" "$HEALTH_RETRIES"
|
||||
sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 20 2>/dev/null || true
|
||||
@@ -2312,6 +2562,30 @@ do_restart() {
|
||||
else
|
||||
warn "No service manager found"
|
||||
fi
|
||||
# CMS (Next.js under PM2): restart only when it was rebuilt this run, or
|
||||
# when it is not answering — keep an already-online CMS untouched.
|
||||
if command -v pm2 &>/dev/null && [ -f "$CMS_DIR/package.json" ]; then
|
||||
local cms_restarted=false
|
||||
if [ "$CMS_BUILT" = true ]; then
|
||||
if pm2 restart "$CMS_PM2_NAME" 2>/dev/null; then
|
||||
ok "$CMS_PM2_NAME restarted"
|
||||
cms_restarted=true
|
||||
elif pm2 restart all 2>/dev/null; then
|
||||
ok "PM2 restarted"
|
||||
cms_restarted=true
|
||||
else
|
||||
warn "PM2 restart failed for %s" "$CMS_PM2_NAME"
|
||||
fi
|
||||
elif ! wait_for_cms_health; then
|
||||
warn "CMS unhealthy — restarting %s" "$CMS_PM2_NAME"
|
||||
pm2 restart "$CMS_PM2_NAME" 2>/dev/null && ok "$CMS_PM2_NAME restarted" && cms_restarted=true || warn "PM2 restart failed"
|
||||
else
|
||||
ok "$CMS_PM2_NAME already healthy — no restart needed"
|
||||
fi
|
||||
if [ "$cms_restarted" = true ]; then
|
||||
wait_for_cms_health || ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
# Reload nginx. Prefer `nginx -s reload` (the running master picks it up
|
||||
# immediately); fall back to systemctl for hosts where nginx is a unit.
|
||||
if command -v nginx &>/dev/null && sudo nginx -t >/dev/null 2>&1; then
|
||||
@@ -2383,11 +2657,19 @@ cmd_update() {
|
||||
return
|
||||
fi
|
||||
|
||||
step 1 8 "System Diagnostics"
|
||||
step 1 9 "System Diagnostics"
|
||||
preflight_check
|
||||
|
||||
case "${SELECTIVE_UPDATE:-full}" in
|
||||
emulator) update_emulator ;;
|
||||
emulator)
|
||||
update_emulator
|
||||
# A freshly rebuilt jar must be loaded — bounce the service and
|
||||
# wait for it to come back healthy.
|
||||
if [ "$EMULATOR_BUILT" = true ] && systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then
|
||||
sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted"
|
||||
wait_for_emulator_health || warn "Emulator not healthy after %s retries" "$HEALTH_RETRIES"
|
||||
fi
|
||||
;;
|
||||
renderer) update_renderer ;;
|
||||
client) update_client ;;
|
||||
configs) sync_configs ;;
|
||||
@@ -2412,6 +2694,7 @@ cmd_update() {
|
||||
ROLLBACK_NEEDED=true
|
||||
die "Build failure in: %s" "${PARALLEL_FAILED[*]}"
|
||||
fi
|
||||
update_cms
|
||||
sync_configs
|
||||
do_cleanup
|
||||
do_permissions
|
||||
|
||||
Reference in new issue
Block a user