From 613b7502e130744b7ffaa9813273aa846a5f8012 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 3 Aug 2026 17:43:19 +0200 Subject: [PATCH] fix: repair updater and migrate configs to JSONC only - fix emulator git path (repo root vs Maven submodule) and SQL/backup dirs - auto-detect branch; track real parallel job exit status - verify vite presence and clean+full install when node_modules is incomplete - fix health-check label handling and skip jsonc/example files in JSON scan - stop hardcoding the Nitro client path in chown - drop JSON5: sync config URLs to .jsonc, remove legacy .json5 files - bump to v8.0.2 --- update-Nitrov3.sh | 147 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 45 deletions(-) diff --git a/update-Nitrov3.sh b/update-Nitrov3.sh index 3667cb6a..2de20ef0 100755 --- a/update-Nitrov3.sh +++ b/update-Nitrov3.sh @@ -9,8 +9,8 @@ set -Eeo pipefail # ============================================================================= # CONSTANTS # ============================================================================= -readonly SCRIPT_VERSION="8.0.0" -readonly SCRIPT_BUILD="20260720" +readonly SCRIPT_VERSION="8.0.2" +readonly SCRIPT_BUILD="20260803" readonly SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly START_TIME=$(date +%s) @@ -105,19 +105,24 @@ DB_USER="${NITRO_DB_USER:-${DB_USER:-root}}" DB_PASS="${NITRO_DB_PASS:-${DB_PASS:-}}" EMULATOR_SERVICE="${NITRO_EMULATOR_SERVICE:-emulator}" EMULATOR_DIR="${NITRO_EMULATOR_PATH:-/var/www/emulator}" -SQL_DIR="${NITRO_SQL_DIR:-$EMULATOR_DIR/Database Updates}" +# Polaris-Emulator: the git repository root is $EMULATOR_DIR itself, while the +# Maven project (pom.xml + target/) lives in the $EMULATOR_DIR/Emulator submodule. +EMULATOR_REPO="${NITRO_EMULATOR_REPO:-$EMULATOR_DIR}" +EMULATOR_MODULE="${NITRO_EMULATOR_MODULE:-$EMULATOR_DIR/Emulator}" +SQL_DIR="${NITRO_SQL_DIR:-$EMULATOR_DIR/Database/Database Updates}" GAMEDATA_CONF_DIR="${NITRO_GAMEDATA_DIR:-/var/www/Gamedata/config}" NITRO_SRC_DIR="${NITRO_CLIENT_DIR:-/var/www/Nitro-V3/public/configuration}" -BACKUP_DIR="${NITRO_BACKUP_DIR:-$EMULATOR_DIR/Database Updates/backups}" +BACKUP_DIR="${NITRO_BACKUP_DIR:-$EMULATOR_DIR/Database/Database Updates/backups}" NITRO_CLIENT="${NITRO_CLIENT_SRC:-/var/www/Nitro-V3}" NITRO_RENDERER="${NITRO_RENDERER_SRC:-/var/www/Nitro_Render_V3}" -NITRO_BRANCH="${NITRO_BRANCH:-main}" +NITRO_BRANCH="${NITRO_BRANCH:-}" MIN_DISK_GB="${NITRO_MIN_DISK_GB:-5}" HEALTH_RETRIES="${NITRO_HEALTH_RETRIES:-12}" HEALTH_INTERVAL="${NITRO_HEALTH_INTERVAL:-5}" NITRO_SSL_VERIFY="${NITRO_SSL_VERIFY:-OFF}" CONFIG_BACKUP_DIR="${SCRIPT_DIR}/storage/config-backups" mkdir -p "$CONFIG_BACKUP_DIR" 2>/dev/null || true +mkdir -p "$BACKUP_DIR" 2>/dev/null || true NITRO_LANG="${NITRO_LANG:-en}" MYSQL_CRED="-h $DB_HOST -P $DB_PORT -u $DB_USER --ssl-verify-server-cert=${NITRO_SSL_VERIFY}" @@ -857,7 +862,7 @@ trap 'release_lock; cursor_show; exit 1' SIGINT SIGTERM # GIT HELPERS # ============================================================================= detect_branches() { - local repos=("$EMULATOR_DIR/Emulator" "$NITRO_CLIENT" "$NITRO_RENDERER") + local repos=("$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER") for repo in "${repos[@]}"; do [ -d "$repo/.git" ] || continue ( @@ -876,10 +881,11 @@ detect_best_branch() { local repo="$1" preferred="$2" ( cd "$repo" 2>/dev/null || { echo "main"; exit 0; } - for v in "$preferred" "${preferred^}" "main" "master"; do - git rev-parse --verify "$v" &>/dev/null && { echo "$v"; exit 0; } + local cur; cur=$(git branch --show-current 2>/dev/null || echo "") + for v in "$preferred" "${preferred^}" "$cur" "main" "master"; do + [ -n "$v" ] && git rev-parse --verify "$v" &>/dev/null && { echo "$v"; exit 0; } done - echo "$(git branch --show-current 2>/dev/null || echo "main")" + echo "${cur:-main}" ) } @@ -924,33 +930,36 @@ clean_node_modules() { # ============================================================================= PARALLEL_PIDS=() +PARALLEL_NAMES=() PARALLEL_FAILED=() PARALLEL_RESULTS=() parallel_run() { local name="$1"; shift + PARALLEL_NAMES+=("$name") ( - if "$@" >/dev/null 2>&1; then - echo "__PARALLEL_OK__${name}" - else - echo "__PARALLEL_FAIL__${name}" - fi + "$@" >/dev/null 2>&1 ) & PARALLEL_PIDS+=($!) } parallel_wait() { - # Collect per-job pass/fail markers from stdout of each backgrounded job. + # Collect per-job pass/fail based on the real exit status of each job. PARALLEL_RESULTS=() + PARALLEL_FAILED=() + local i=0 for pid in "${PARALLEL_PIDS[@]}"; do - local line - line=$(wait "$pid" 2>/dev/null; true) - PARALLEL_RESULTS+=("$line") - if [ "$line" = "__PARALLEL_FAIL__"* ]; then - PARALLEL_FAILED+=("${line#__PARALLEL_FAIL__}") + local name="${PARALLEL_NAMES[$i]}" + if wait "$pid" 2>/dev/null; then + PARALLEL_RESULTS+=("__PARALLEL_OK__${name}") + else + PARALLEL_RESULTS+=("__PARALLEL_FAIL__${name}") + PARALLEL_FAILED+=("$name") fi + i=$((i+1)) done PARALLEL_PIDS=() + PARALLEL_NAMES=() } parallel_failed() { @@ -987,6 +996,9 @@ validate_configs() { local errors=0 for f in "$dir"/*.json; do [ -f "$f" ] || continue + case "$(basename "$f")" in + *jsonc*|*.example*) continue ;; + esac if ! validate_json "$f"; then warn "Invalid JSON: %s" "$(basename "$f")" errors=$((errors + 1)) @@ -1003,7 +1015,7 @@ backup_binaries() { BINARY_BACKUP_DIR="$CONFIG_BACKUP_DIR/binaries-$(date +%Y%m%d-%H%M%S)" mkdir -p "$BINARY_BACKUP_DIR" - local jar_dir="$EMULATOR_DIR/Emulator/target" + local jar_dir="$EMULATOR_MODULE/target" if [ -d "$jar_dir" ] && ls "$jar_dir"/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then cp "$jar_dir"/Polaris-*-jar-with-dependencies.jar "$BINARY_BACKUP_DIR/" 2>/dev/null || true ok "Emulator JAR backed up" @@ -1023,7 +1035,7 @@ restore_binaries() { echo -e " ${C_BG_RED}${C_WHITE}${C_BOLD} $(_t "RESTORE BINARIES") ${C_RESET}" if ls "$BINARY_BACKUP_DIR"/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then - local jar_dir="$EMULATOR_DIR/Emulator/target" + local jar_dir="$EMULATOR_MODULE/target" mkdir -p "$jar_dir" cp "$BINARY_BACKUP_DIR"/Polaris-*-jar-with-dependencies.jar "$jar_dir/" 2>/dev/null || true ok "Emulator JAR restored" @@ -1052,7 +1064,7 @@ preflight_check() { ok "All commands available" - for d in "$EMULATOR_DIR/Emulator" "$NITRO_RENDERER" "$NITRO_CLIENT" "$NITRO_SRC_DIR"; do + for d in "$EMULATOR_REPO" "$EMULATOR_MODULE" "$NITRO_RENDERER" "$NITRO_CLIENT" "$NITRO_SRC_DIR"; do [ -d "$d" ] || die "Missing dir: $d" done ok "All directories exist" @@ -1075,9 +1087,9 @@ preflight_check() { # ============================================================================= update_emulator() { step 2 8 "Update & Build Emulator" - if git_update "$EMULATOR_DIR/Emulator" "$NITRO_BRANCH"; then + if git_update "$EMULATOR_REPO" "$NITRO_BRANCH"; then HAD_UPDATES=true; UPDATED_REPOS+=("Emulator") - cd "$EMULATOR_DIR/Emulator" + cd "$EMULATOR_MODULE" backup_binaries ROLLBACK_NEEDED=true spinner_start "Building emulator (parallel)..." @@ -1118,11 +1130,21 @@ update_renderer() { HAD_UPDATES=true; UPDATED_REPOS+=("Nitro_Render_V3") cd "$NITRO_RENDERER" spinner_start "Installing deps..." - yarn install --frozen-lockfile --prefer-offline 2>&1 || { clean_node_modules && yarn install 2>&1; } || { spinner_stop fail; die "yarn install failed"; } + if ! yarn install --frozen-lockfile --prefer-offline >> "$LOG_FILE" 2>&1; then + warn "Renderer: yarn install failed — cleaning node_modules and retrying" + clean_node_modules + yarn install >> "$LOG_FILE" 2>&1 || { spinner_stop fail; die "yarn install failed (see: %s)" "$LOG_FILE"; } + fi + if [ ! -d node_modules/pixi.js ]; then + warn "Renderer: node_modules incomplete — cleaning and doing full install" + clean_node_modules + yarn install >> "$LOG_FILE" 2>&1 || { spinner_stop fail; die "yarn install failed (see: %s)" "$LOG_FILE"; } + fi spinner_stop ok; ok "Renderer dependencies installed" else info "Renderer: already up to date" fi + return 0 } update_client() { @@ -1133,14 +1155,25 @@ update_client() { backup_binaries ROLLBACK_NEEDED=true spinner_start "Installing deps..." - yarn install --frozen-lockfile --prefer-offline 2>&1 || { clean_node_modules && yarn install 2>&1; } || { spinner_stop fail; die "yarn install failed"; } + # yarn 1 can "succeed" while leaving node_modules incomplete, so verify + # that the vite build binary actually exists before attempting a build. + if ! yarn install --frozen-lockfile --prefer-offline >> "$LOG_FILE" 2>&1; then + warn "Nitro-V3: yarn install failed — cleaning node_modules and retrying" + clean_node_modules + yarn install >> "$LOG_FILE" 2>&1 || { spinner_stop fail; die "yarn install failed (see: %s)" "$LOG_FILE"; } + fi + if [ ! -x node_modules/.bin/vite ]; then + warn "Nitro-V3: vite missing after install — cleaning node_modules and doing full install" + clean_node_modules + yarn install >> "$LOG_FILE" 2>&1 || { spinner_stop fail; die "yarn install failed (see: %s)" "$LOG_FILE"; } + fi spinner_stop ok spinner_start "Building frontend..." if yarn build >> "$LOG_FILE" 2>&1; then spinner_stop ok else spinner_stop fail - tail -10 "$LOG_FILE" || true + tail -30 "$LOG_FILE" || true die "yarn build failed (see: %s)" "$LOG_FILE" fi else @@ -1148,6 +1181,7 @@ update_client() { fi mkdir -p "$NITRO_CLIENT/dist/custom-themes" [ ! -f "$NITRO_CLIENT/dist/custom-themes/index.json" ] && echo '{"themes":[]}' > "$NITRO_CLIENT/dist/custom-themes/index.json" + return 0 } sync_configs() { @@ -1178,6 +1212,12 @@ sync_configs() { # Validate the generated configs validate_configs "$NITRO_SRC_DIR" + # Remove legacy .json5 leftovers — the client only supports JSON/JSONC + for d in "$NITRO_SRC_DIR" "$dcd" "$GAMEDATA_CONF_DIR"; do + [ -d "$d" ] && find "$d" -maxdepth 1 -name '*.json5' -type f -delete 2>/dev/null || true + done + ok "Legacy .json5 files removed" + # Pre-flight: warn if NITRO_IMAGE_LIBRARY_URL looks wrong before applying configs if [ -n "$NITRO_IMAGE_LIBRARY_URL" ]; then case "$NITRO_IMAGE_LIBRARY_URL" in @@ -1199,6 +1239,11 @@ sync_configs() { NITRO_FURNI_ASSET_ICON_URL="$NITRO_FURNI_ASSET_ICON_URL" \ python3 -c ' import json, os +def to_jsonc(value): + if isinstance(value, dict): return {k: to_jsonc(v) for k, v in value.items()} + if isinstance(value, list): return [to_jsonc(v) for v in value] + if isinstance(value, str): return value.replace(".json5", ".jsonc") + return value paths = [ os.path.join(os.environ.get("NITRO_SRC_DIR", ""), "renderer-config.json"), os.path.join(os.environ.get("NITRO_DIST_CONFIG_DIR", ""), "renderer-config.json"), @@ -1208,6 +1253,7 @@ paths = [ for path in paths: if not os.path.exists(path): continue with open(path, "r") as f: data = json.load(f) + data = to_jsonc(data) for key in ["image.library.url", "hof.furni.url", "gamedata.url", "asset.url"]: env_val = os.environ.get(f"NITRO_{key.upper().replace(chr(46), chr(95))}") if env_val: data[key] = env_val @@ -1216,8 +1262,8 @@ for path in paths: env_val = os.environ.get(f"NITRO_{key.upper().replace(chr(46), chr(95))}") if env_val: data[key] = env_val if "gamedata.url" in data: - data["radio.url"] = data["gamedata.url"] + "/config/radio-stations.json5?t=%timestamp%" - data["soundboard.url"] = data["gamedata.url"] + "/config/soundboard-sounds.json5?t=%timestamp%" + data["radio.url"] = data["gamedata.url"] + "/config/radio-stations.jsonc?t=%timestamp%" + data["soundboard.url"] = data["gamedata.url"] + "/config/soundboard-sounds.jsonc?t=%timestamp%" if "show.google.ads" in data: data["show.google.ads"] = False with open(path, "w") as f: json.dump(data, f, indent=(4 if "dist" not in path else None), separators=(",", ":") if "dist" in path else (",", ": ")) print(f" [OK] Fixed: {os.path.basename(path)}") @@ -1243,7 +1289,7 @@ do_cleanup() { yarn cache clean 2>/dev/null || true rm -rf /tmp/nitro-* /tmp/epicnext-* 2>/dev/null || true # Clean old Maven build artifacts (older than 7 days) - find "$EMULATOR_DIR/Emulator" -maxdepth 3 -name '*.jar' -mtime +7 2>/dev/null -exec rm -f {} \; || true + find "$EMULATOR_MODULE" -maxdepth 3 -name '*.jar' -mtime +7 2>/dev/null -exec rm -f {} \; || true ok "Cleanup done (%s logs removed)" "$lc" } @@ -1288,7 +1334,7 @@ do_restart() { emulator_port_open() { local port="${NITRO_EMULATOR_PORT:-}" if [ -z "$port" ]; then - port=$(grep -m1 '^game\.port=' "$EMULATOR_DIR/Emulator/target/config.ini" 2>/dev/null | cut -d= -f2) + port=$(grep -m1 '^game\.port=' "$EMULATOR_MODULE/target/config.ini" 2>/dev/null | cut -d= -f2) fi if [ -z "$port" ]; then port=30000 @@ -1382,7 +1428,7 @@ cmd_update() { esac show_summary - sudo chown -R www-data:www-data /var/www/Nitro-V3/ + sudo chown -R www-data:www-data "$NITRO_CLIENT/" release_lock } @@ -1466,7 +1512,7 @@ cmd_status() { done; box_bottom 56 echo "" box_top "GIT REPOS" 56 - for re in "Emulator:$EMULATOR_DIR/Emulator" "Nitro-V3:$NITRO_CLIENT" "Render:$NITRO_RENDERER"; do + for re in "Emulator:$EMULATOR_REPO" "Nitro-V3:$NITRO_CLIENT" "Render:$NITRO_RENDERER"; do local nm="${re%%:*}" pa="${re##*:}" [ -d "$pa/.git" ] || continue local br=$(cd "$pa" && git branch --show-current 2>/dev/null || echo "?") @@ -1487,8 +1533,8 @@ cmd_status() { cmd_health() { step 1 1 "Health Check" local t=0 p=0 f=0 w=0 - check() { t=$((t+1)); "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$1")"; } || { f=$((f+1)); fail "$(_t "$1")"; }; } - checkw() { t=$((t+1)); "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$1")"; } || { w=$((w+1)); warn "$(_t "$1")"; }; } + check() { t=$((t+1)); local lbl="$1"; shift; "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$lbl")"; } || { f=$((f+1)); fail "$(_t "$lbl")"; }; } + checkw() { t=$((t+1)); local lbl="$1"; shift; "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$lbl")"; } || { w=$((w+1)); warn "$(_t "$lbl")"; }; } echo "" echo -e " ${C_BOLD}$(_t "Commands:")${C_RESET}" checkw "Git" command -v git; checkw "Node.js" command -v node; checkw "Yarn" command -v yarn @@ -1503,6 +1549,9 @@ cmd_health() { local cerr=0 for cf in "$NITRO_SRC_DIR"/*.json; do [ -f "$cf" ] || continue + case "$(basename "$cf")" in + *jsonc*|*.example*) continue ;; + esac python3 -m json.tool "$cf" >/dev/null 2>&1 || cerr=$((cerr+1)) done [ "$cerr" -eq 0 ] && ok "$(_t "All JSON valid")" || warn "$(_t "JSON errors:") $cerr" @@ -1528,7 +1577,7 @@ cmd_health() { fi done echo -e "\n ${C_BOLD}$(_t "Directories:")${C_RESET}" - checkw "Emulator" test -d "$EMULATOR_DIR/Emulator"; checkw "Nitro-V3" test -d "$NITRO_CLIENT"; checkw "Renderer" test -d "$NITRO_RENDERER" + checkw "Emulator" test -d "$EMULATOR_MODULE"; checkw "Nitro-V3" test -d "$NITRO_CLIENT"; checkw "Renderer" test -d "$NITRO_RENDERER" checkw "$(_t "Gamedata config")" test -d "$GAMEDATA_CONF_DIR"; checkw "$(_t "Backup dir")" test -d "$BACKUP_DIR" echo -e "\n ${C_BOLD}$(_t "Services:")${C_RESET}" checkw "MariaDB" service_active mariadb; checkw "Nginx" service_active nginx; checkw "Redis" service_active redis-server @@ -1583,7 +1632,7 @@ cmd_flyaway_repair() { step 2 6 "$(_t "Flyaway Repair")" local repaired=0 - for repo in "$EMULATOR_DIR/Emulator" "$NITRO_CLIENT" "$NITRO_RENDERER"; do + for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do [ -d "$repo/.git" ] || continue local name; name=$(basename "$repo") info "Checking %s..." "$name" @@ -1649,9 +1698,9 @@ cmd_flyaway_repair() { fi # Rebuild emulator if jar is missing - if [ ! -d "$EMULATOR_DIR/Emulator/target" ] || ! ls "$EMULATOR_DIR/Emulator/target"/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then + if [ ! -d "$EMULATOR_MODULE/target" ] || ! ls "$EMULATOR_MODULE"/target/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then step 6 6 "$(_t "Rebuild Emulator")" - cd "$EMULATOR_DIR/Emulator" + cd "$EMULATOR_MODULE" backup_binaries ROLLBACK_NEEDED=true spinner_start "$(_t "Building emulator (parallel)...")" @@ -1672,7 +1721,7 @@ cmd_flyaway_repair() { do_restart show_summary - sudo chown -R www-data:www-data /var/www/Nitro-V3/ + sudo chown -R www-data:www-data "$NITRO_CLIENT/" release_lock } @@ -1789,7 +1838,7 @@ cmd_clean() { 4) find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null; ok "Logs cleaned" ;; 5) yarn cache clean 2>/dev/null; find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null; rm -rf /tmp/nitro-* /tmp/epicnext-* 2>/dev/null; ok "Full cleanup" ;; 6) command -v docker &>/dev/null && docker system prune -f 2>/dev/null && ok "Docker pruned" || warn "Docker N/A" ;; - 7) find "$EMULATOR_DIR/Emulator" -maxdepth 3 -name '*.jar' -mtime +7 -exec rm -f {} \; 2>/dev/null; ok "Old Maven artifacts cleaned" ;; + 7) find "$EMULATOR_MODULE" -maxdepth 3 -name '*.jar' -mtime +7 -exec rm -f {} \; 2>/dev/null; ok "Old Maven artifacts cleaned" ;; esac } @@ -1802,7 +1851,7 @@ cmd_gitlog() { echo -e " ${C_BOLD}1)${C_RESET} $(_t "Emulator") ${C_BOLD}2)${C_RESET} $(_t "Nitro-V3") ${C_BOLD}3)${C_RESET} $(_t "Renderer")" echo -en "\n $(_t "Select:") "; local c; read -rt 30 c local repo="" - case "$c" in 1) repo="$EMULATOR_DIR/Emulator" ;; 2) repo="$NITRO_CLIENT" ;; 3) repo="$NITRO_RENDERER" ;; *) return ;; esac + case "$c" in 1) repo="$EMULATOR_REPO" ;; 2) repo="$NITRO_CLIENT" ;; 3) repo="$NITRO_RENDERER" ;; *) return ;; esac [ ! -d "$repo/.git" ] && { warn "Not a git repo"; return; } echo "" local branch @@ -1832,7 +1881,7 @@ cmd_compare() { [[ "$ba" =~ ^[0-9]+$ ]] && [[ "$bb" =~ ^[0-9]+$ ]] && [ "$ba" -ge 1 ] && [ "$bb" -ge 1 ] && [ "$ba" -le ${#branches[@]} ] && [ "$bb" -le ${#branches[@]} ] || { warn "Invalid"; return; } local bA="${branches[$((ba-1))]}" bB="${branches[$((bb-1))]}" echo "" - for repo in "$EMULATOR_DIR/Emulator" "$NITRO_CLIENT" "$NITRO_RENDERER"; do + for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do [ -d "$repo/.git" ] || continue echo -e " ${C_BOLD}$(basename "$repo"):${C_RESET}" cd "$repo" 2>/dev/null || continue @@ -1873,7 +1922,7 @@ cmd_schedule() { cmd_stash() { step 1 1 "Git Stashes" local found=false - for repo in "$EMULATOR_DIR/Emulator" "$NITRO_CLIENT" "$NITRO_RENDERER"; do + for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do [ -d "$repo/.git" ] || continue local list list=$(cd "$repo" && git stash list 2>/dev/null) @@ -2103,6 +2152,14 @@ main() { done [ -z "$cmd" ] && cmd="interactive" + # Auto-detect the branch from the emulator repo when none was configured, + # so "Quick Update — current branch" actually updates the current branch. + if [ -z "${NITRO_BRANCH:-}" ]; then + NITRO_BRANCH="$(cd "$EMULATOR_REPO" 2>/dev/null && git branch --show-current 2>/dev/null || echo "main")" + [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main" + info "Auto-detected branch: %s" "$NITRO_BRANCH" + fi + if [ -z "${NITRO_SITE_URL:-}" ] && [ -n "${APP_URL:-}" ]; then NITRO_SITE_URL="$APP_URL"; fi case "${NITRO_SITE_URL:-}" in https://*) NITRO_WS_PROTO="wss://" ; NITRO_DOMAIN="${NITRO_SITE_URL#https://}" ;;