Improve update-Nitrov3.sh parallelism, UX, and compatibility

- Run renderer and client builds in parallel (background + wait) for
  faster updates
- Cache detected git branches in interactive menu (avoid re-running
  git branch -r on every redraw); re-cache after switching branches
- Add health check after emulator restart (poll until active or retries
  exhausted)
- Add service_active() helper with systemd/service/pgrep fallback for
  non-systemd systems; replace all systemctl is-active calls
- Add 30s read timeout (-t 30) on all interactive prompts to prevent
  hanging on non-terminal stdin
- Add set -E for ERR trap inheritance in subshells
This commit is contained in:
openhands committed 2026-07-08 19:27:27 +02:00
1 parent f9b0ec78d1
commit 0abd490f67
1 file changed
+49 -28
+49 -28
View File
@@ -1,5 +1,5 @@
#!/bin/bash
set -o pipefail
set -Eeo pipefail
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ ║
@@ -156,6 +156,16 @@ cursor_show() { tput cnorm 2>/dev/null || true; }
clear_line() { printf "\r\033[K"; }
repeat() { local ch="${1:-=}" n="${2:-1}"; printf '%*s' "$n" '' | tr ' ' "$ch"; }
list_sorted() { local d="$1" p="$2" m="${3:-20}"; find "$d" -name "$p" -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -n "$m" | sed 's/^[0-9.]* //'; }
service_active() {
local svc="$1"
if command -v systemctl &>/dev/null; then
systemctl is-active "$svc" &>/dev/null
elif command -v service &>/dev/null; then
service "$svc" status &>/dev/null
else
pgrep -x "$svc" &>/dev/null
fi
}
format_size() {
local bytes="${1:-0}"
if command -v numfmt &>/dev/null; then
@@ -202,7 +212,7 @@ confirm() {
local msg="${1:-Continue?}" default="${2:-y}"
local hint="[Y/n]"; [ "$default" = "n" ] && hint="[y/N]"
echo -en " ${C_YELLOW}${E_ARROW}${C_RESET} ${C_BOLD}$msg${C_RESET} $hint "
local reply; read -r reply; reply="${reply:-$default}"
local reply; read -rt 30 reply; reply="${reply:-$default}"
[[ "$reply" =~ ^[Yy] ]]
}
@@ -491,6 +501,16 @@ do_restart() {
step 8 8 "Restart Services"
if systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then
sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" || warn "Restart failed"
local hc=0
while [ "$hc" -lt "$HEALTH_RETRIES" ]; do
sleep "$HEALTH_INTERVAL"
if service_active "$EMULATOR_SERVICE"; then
ok "Emulator health check passed"
break
fi
hc=$((hc+1))
done
[ "$hc" -eq "$HEALTH_RETRIES" ] && warn "Emulator not running after $HEALTH_RETRIES retries"
elif command -v pm2 &>/dev/null; then
pm2 restart all 2>/dev/null && ok "PM2 restarted" || warn "PM2 restart failed"
else
@@ -562,8 +582,9 @@ cmd_update() {
configs) sync_configs ;;
*)
update_emulator
update_renderer
update_client
update_renderer &
update_client &
wait
sync_configs
do_cleanup
do_permissions
@@ -605,7 +626,7 @@ cmd_restore() {
i=$((i+1))
done
echo -en "\n Select [1-$((i-1))]: "
local c; read -r c
local c; read -rt 30 c
[[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#backups[@]} ] || die "Invalid"
local sel="${backups[$((c-1))]}"
if ! confirm "Restore $(basename "$sel")? OVERWRITES database!"; then info "Cancelled"; return; fi
@@ -649,7 +670,7 @@ cmd_status() {
box_top "SERVICES" 56
for svc in nginx mariadb redis-server "$EMULATOR_SERVICE" php-fpm cron; do
local st="${C_DIM}N/A${C_RESET}"
systemctl is-active "$svc" &>/dev/null && st="${C_GREEN}ACTIVE${C_RESET}"
service_active "$svc" && st="${C_GREEN}ACTIVE${C_RESET}"
printf " ${C_CYAN}║${C_RESET} %-20s %b${C_CYAN} ║${C_RESET}\n" "$svc" "$st"
done; box_bottom 56
echo ""
@@ -679,7 +700,7 @@ cmd_health() {
echo -e "\n ${C_BOLD}Directories:${C_RESET}"
checkw "Emulator" test -d "$EMULATOR_DIR/Emulator"; checkw "Nitro-V3" test -d "$NITRO_CLIENT"; checkw "Renderer" test -d "$NITRO_RENDERER"
echo -e "\n ${C_BOLD}Services:${C_RESET}"
checkw "MariaDB" systemctl is-active mariadb; checkw "Nginx" systemctl is-active nginx; checkw "Redis" systemctl is-active redis-server
checkw "MariaDB" service_active mariadb; checkw "Nginx" service_active nginx; checkw "Redis" service_active redis-server
echo -e "\n ${C_BOLD}Application:${C_RESET}"
checkw "Next.js" test -f "$SCRIPT_DIR/next.config.js" -o -f "$SCRIPT_DIR/next.config.mjs" -o -f "$SCRIPT_DIR/next.config.ts"; checkw ".env" test -f "$SCRIPT_DIR/.env"
checkw "Site" timeout 5 curl -sf "${NITRO_SITE_URL:-}" 2>/dev/null
@@ -706,7 +727,7 @@ cmd_services() {
echo -e " ${C_BOLD}1)${C_RESET} Start Emulator ${C_BOLD}2)${C_RESET} Stop Emulator ${C_BOLD}3)${C_RESET} Restart Emulator"
echo -e " ${C_BOLD}4)${C_RESET} Start Nginx ${C_BOLD}5)${C_RESET} Restart Nginx ${C_BOLD}6)${C_RESET} Status All"
echo -e " ${C_BOLD}7)${C_RESET} Emulator Logs ${C_BOLD}0)${C_RESET} Return"
echo -en "\n Select: "; local c; read -r c
echo -en "\n Select: "; local c; read -rt 30 c
case "$c" in
1) sudo systemctl start "$EMULATOR_SERVICE" && ok "Started" || fail "Failed" ;;
2) sudo systemctl stop "$EMULATOR_SERVICE" && ok "Stopped" || fail "Failed" ;;
@@ -714,7 +735,7 @@ cmd_services() {
4) sudo systemctl start nginx && ok "Started" || fail "Failed" ;;
5) sudo systemctl restart nginx && ok "Restarted" || fail "Failed" ;;
6) for s in nginx mariadb redis-server "$EMULATOR_SERVICE" php-fpm cron; do
local st="${C_RED}DOWN${C_RESET}"; systemctl is-active "$s" &>/dev/null && st="${C_GREEN}UP${C_RESET}"
local st="${C_RED}DOWN${C_RESET}"; service_active "$s" && st="${C_GREEN}UP${C_RESET}"
printf " %-25s %b\n" "$s" "$st"
done ;;
7) sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 30 ;;
@@ -730,7 +751,7 @@ cmd_database() {
echo -e " ${C_BOLD}1)${C_RESET} DB Info ${C_BOLD}2)${C_RESET} List Tables ${C_BOLD}3)${C_RESET} Table Sizes"
echo -e " ${C_BOLD}4)${C_RESET} Optimize ${C_BOLD}5)${C_RESET} Custom Query ${C_BOLD}6)${C_RESET} Active Users"
echo -e " ${C_BOLD}0)${C_RESET} Return"
echo -en "\n Select: "; local c; read -r c
echo -en "\n Select: "; local c; read -rt 30 c
case "$c" in
1) box_top "DB INFO" 50
local dbs=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT ROUND(SUM(data_length+index_length)/1024/1024,1) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?")
@@ -743,7 +764,7 @@ cmd_database() {
for tbl in $($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT table_name FROM information_schema.tables WHERE table_schema='$DB_NAME' AND engine='InnoDB'" "$DB_NAME" 2>/dev/null); do
$MYSQL_CLIENT $MYSQL_CRED -e "OPTIMIZE TABLE $DB_NAME.\`$tbl\`" "$DB_NAME" &>/dev/null || true
done; spinner_stop ok; ok "Optimized" ;;
5) echo -en " SQL: "; local q; read -r q; [ -n "$q" ] && $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" -e "$q" 2>/dev/null ;;
5) echo -en " SQL: "; local q; read -rt 30 q; [ -n "$q" ] && $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" -e "$q" 2>/dev/null ;;
6) $MYSQL_CLIENT $MYSQL_CRED -e "SELECT name, motto FROM $DB_NAME.users WHERE online='1'" 2>/dev/null || warn "Could not query" ;;
esac
}
@@ -785,7 +806,7 @@ cmd_logs() {
printf " ${C_GREEN}${C_BOLD}%2d)${C_RESET} %-35s ${C_DIM}%s${C_RESET}\n" "$i" "$(basename "$l")" "$sz"
i=$((i+1))
done
echo -en "\n Select [1-$((i-1))]: "; local c; read -r c
echo -en "\n Select [1-$((i-1))]: "; local c; read -rt 30 c
if [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#logs[@]} ]; then
less -R "${logs[$((c-1))]}" 2>/dev/null || cat "${logs[$((c-1))]}"
fi
@@ -799,7 +820,7 @@ cmd_clean() {
echo ""
echo -e " ${C_BOLD}1)${C_RESET} Yarn ${C_BOLD}2)${C_RESET} Laravel ${C_BOLD}3)${C_RESET} Redis"
echo -e " ${C_BOLD}4)${C_RESET} Logs ${C_BOLD}5)${C_RESET} Everything ${C_BOLD}0)${C_RESET} Return"
echo -en "\n Select: "; local c; read -r c
echo -en "\n Select: "; local c; read -rt 30 c
case "$c" in
1) yarn cache clean 2>/dev/null && ok "Yarn cleaned" ;;
2) cd "$SCRIPT_DIR" && rm -rf .next 2>/dev/null && ok "Next.js cache cleared" ;;
@@ -816,7 +837,7 @@ cmd_gitlog() {
step 1 1 "Git Commit History"
echo ""
echo -e " ${C_BOLD}1)${C_RESET} Emulator ${C_BOLD}2)${C_RESET} Nitro-V3 ${C_BOLD}3)${C_RESET} Renderer"
echo -en "\n Select: "; local c; read -r c
echo -en "\n 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
[ ! -d "$repo/.git" ] && { warn "Not a git repo"; return; }
@@ -839,8 +860,8 @@ cmd_compare() {
echo -e " ${C_BOLD}Branches:${C_RESET}"
local i=1
for b in "${branches[@]}"; do echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b"; i=$((i+1)); done
echo -en "\n Branch A: "; local ba; read -r ba
echo -en " Branch B: "; local bb; read -r bb
echo -en "\n Branch A: "; local ba; read -rt 30 ba
echo -en " Branch B: "; local bb; read -rt 30 bb
[[ "$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 ""
@@ -867,13 +888,13 @@ cmd_schedule() {
echo ""
echo -e " ${C_BOLD}1)${C_RESET} Daily 03:00 ${C_BOLD}2)${C_RESET} Daily 04:00 ${C_BOLD}3)${C_RESET} Weekly Sun 03:00"
echo -e " ${C_BOLD}4)${C_RESET} Custom time ${C_BOLD}5)${C_RESET} Remove ${C_BOLD}0)${C_RESET} Return"
echo -en "\n Select: "; local c; read -r c
echo -en "\n Select: "; local c; read -rt 30 c
case "$c" in
1) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 03:00" ;;
2) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 4 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 04:00" ;;
3) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * 0 cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled weekly" ;;
4) echo -en " Minute [0-59]: "; local m; read -r m
echo -en " Hour [0-23]: "; local h; read -r h
4) echo -en " Minute [0-59]: "; local m; read -rt 30 m
echo -en " Hour [0-23]: "; local h; read -rt 30 h
(crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "$m $h * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled $m $h * * *" ;;
5) crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME" | crontab - && ok "Removed" ;;
esac
@@ -901,11 +922,11 @@ pick_branch_and_update() {
done
echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} Custom"
echo -en "\n Branch [1-$i]: "
local bc; read -r bc
local bc; read -rt 30 bc
if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then
NITRO_BRANCH="${branches[$((bc-1))]}"
elif [ "$bc" = "$i" ]; then
echo -en " Name: "; read -r NITRO_BRANCH; [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main"
echo -en " Name: "; read -rt 30 NITRO_BRANCH; [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main"
else
warn "Invalid, using: $NITRO_BRANCH"
fi
@@ -915,11 +936,10 @@ pick_branch_and_update() {
}
cmd_interactive() {
local branches=()
load_branches branches
while true; do
clear
local branches=()
load_branches branches
echo -e "${C_CYAN}"
echo ""
echo " ╔═══════════════════════════════════════════════════════════════════════════╗"
@@ -966,7 +986,7 @@ cmd_interactive() {
echo -e " ${C_RED}${C_BOLD}0)${C_RESET} Exit"
echo ""
echo -en " ${C_CYAN}${C_BOLD}Select [0-18]${C_RESET}: "
local choice; read -r choice
local choice; read -rt 30 choice
case "$choice" in
1) SELECTIVE_UPDATE=""; (cmd_update) || warn "Update had errors" ;;
@@ -983,11 +1003,12 @@ cmd_interactive() {
echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b$m"; i=$((i+1))
done
echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} Custom"
echo -en "\n Branch [1-$i]: "; local bc; read -r bc
echo -en "\n Branch [1-$i]: "; local bc; read -rt 30 bc
if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then
NITRO_BRANCH="${branches[$((bc-1))]}"
elif [ "$bc" = "$i" ]; then echo -en " Name: "; read -r NITRO_BRANCH
elif [ "$bc" = "$i" ]; then echo -en " Name: "; read -rt 30 NITRO_BRANCH
fi
load_branches branches
ok "Branch: $NITRO_BRANCH" ;;
8) (cmd_database) ;;
9) (cmd_backup); echo ""; (cmd_backups) ;;
@@ -1006,7 +1027,7 @@ cmd_interactive() {
echo ""
echo -en " ${C_DIM}Press Enter to continue...${C_RESET}"
read -r
read -rt 30
done
}