From 09ff20366220b968fa9cb9c51c3286ce4eda8954 Mon Sep 17 00:00:00 2001
From: MacRimi <123239993+MacRimi@users.noreply.github.com>
Date: Fri, 17 Apr 2026 23:58:31 +0200
Subject: [PATCH 01/12] Update README with security note on VirusTotal
Added a security note regarding VirusTotal false positives for the installation script.
---
README.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 86fc5336..4ab3ec22 100644
--- a/README.md
+++ b/README.md
@@ -144,10 +144,13 @@ The following dependencies are installed automatically during setup:
| `python3` + `python3-venv` | Translation support *(Translation version only)* |
| `googletrans` | Google Translate library *(Translation version only)* |
+
+
+> **🛡️ Security Note / VirusTotal False Positive**
+> If you scan the raw installation URL on VirusTotal, you might see a 1/95 detection by heuristic engines like *Chong Lua Dao*. This is a **known false positive**. Because this script uses the standard `curl | bash` installation pattern and downloads legitimate binaries (like `jq` from its official GitHub release), overly aggressive scanners flag the *behavior*. The script is 100% open source and safe to review. You can read more about this in [Issue #162](https://github.com/MacRimi/ProxMenux/issues/162).
---
-
## ⭐ Support the Project!
If you find **ProxMenux** useful, consider giving it a ⭐ on GitHub to help others discover it!
@@ -160,7 +163,6 @@ Contributions, bug reports and feature suggestions are welcome!
- 💡 [Suggest a feature](https://github.com/MacRimi/ProxMenux/discussions)
- 🔀 [Submit a pull request](https://github.com/MacRimi/ProxMenux/pulls)
-If you find ProxMenux useful, consider giving it a ⭐ on GitHub — it helps others discover the project!
---
From 4b724904868ed4461ea23c69ab1080a76833f0d8 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 00:33:40 +0200
Subject: [PATCH 02/12] update menu
---
install_proxmenux.sh | 17 ++++-
install_proxmenux_beta.sh | 7 ++
menu | 139 ++++++++++++++++++++++----------------
3 files changed, 103 insertions(+), 60 deletions(-)
diff --git a/install_proxmenux.sh b/install_proxmenux.sh
index 1f34cc10..de98e245 100755
--- a/install_proxmenux.sh
+++ b/install_proxmenux.sh
@@ -821,6 +821,13 @@ install_normal_version() {
cp "./version.txt" "$LOCAL_VERSION_FILE"
cp "./install_proxmenux.sh" "$BASE_DIR/install_proxmenux.sh"
+ # Defensive: strip CRLF and reject a broken launcher before we declare success.
+ sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
+ if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
+ msg_error "Installed launcher failed syntax check. Installation aborted."
+ exit 1
+ fi
+
# Wipe the scripts tree before copying so any file removed upstream
# (renamed, consolidated, deprecated) disappears from the user install.
# Only $BASE_DIR/scripts/ is cleared; config.json, cache.json,
@@ -963,13 +970,19 @@ install_translation_version() {
cp "./menu" "$INSTALL_DIR/$MENU_SCRIPT"
cp "./version.txt" "$LOCAL_VERSION_FILE"
cp "./install_proxmenux.sh" "$BASE_DIR/install_proxmenux.sh"
-
+
+ sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
+ if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
+ msg_error "Installed launcher failed syntax check. Installation aborted."
+ exit 1
+ fi
+
mkdir -p "$BASE_DIR/scripts"
cp -r "./scripts/"* "$BASE_DIR/scripts/"
chmod -R +x "$BASE_DIR/scripts/"
chmod +x "$BASE_DIR/install_proxmenux.sh"
msg_ok "Necessary files created."
-
+
chmod +x "$INSTALL_DIR/$MENU_SCRIPT"
((current_step++))
diff --git a/install_proxmenux_beta.sh b/install_proxmenux_beta.sh
index 1a2a9fb5..f67bac3b 100644
--- a/install_proxmenux_beta.sh
+++ b/install_proxmenux_beta.sh
@@ -566,6 +566,13 @@ install_beta() {
cp "./menu" "$INSTALL_DIR/$MENU_SCRIPT"
cp "./version.txt" "$LOCAL_VERSION_FILE" 2>/dev/null || true
+ # Defensive: strip CRLF and reject a broken launcher before we declare success.
+ sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
+ if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
+ msg_error "Installed launcher failed syntax check. Installation aborted."
+ exit 1
+ fi
+
# Store beta version marker
if [ -f "$TEMP_DIR/beta_version.txt" ]; then
cp "$TEMP_DIR/beta_version.txt" "$BETA_VERSION_FILE"
diff --git a/menu b/menu
index 1f9f62b4..046c00e0 100644
--- a/menu
+++ b/menu
@@ -55,6 +55,57 @@ check_updates() {
fi
}
+# ── Safe self-update ──────────────────────────────────────
+# Validates the downloaded installer and rolls back /usr/local/bin/menu
+# if the post-install launcher fails to parse. Why: a corrupted download
+# or CRLF contamination has already left users with an unusable launcher;
+# without a rollback the only recovery is curl-reinstall by hand.
+run_installer_safely() {
+ local installer_url="$1"
+ local installer_dest="$2"
+ local launcher="/usr/local/bin/menu"
+ local tmp_installer="/tmp/proxmenux_installer_$$.sh"
+ local menu_backup="/tmp/proxmenux_menu_backup_$$"
+
+ if ! curl -fsSL "$installer_url" -o "$tmp_installer"; then
+ msg_error "Failed to download installer from $installer_url"
+ return 1
+ fi
+
+ sed -i 's/\r$//' "$tmp_installer" 2>/dev/null || true
+
+ if ! bash -n "$tmp_installer" 2>/dev/null; then
+ msg_error "Downloaded installer has invalid syntax. Update aborted."
+ rm -f "$tmp_installer"
+ return 1
+ fi
+
+ [[ -f "$launcher" ]] && cp -f "$launcher" "$menu_backup"
+
+ chmod +x "$tmp_installer"
+ if [[ -n "$installer_dest" ]]; then
+ cp -f "$tmp_installer" "$installer_dest"
+ chmod +x "$installer_dest"
+ bash "$installer_dest" --update
+ else
+ bash "$tmp_installer"
+ fi
+ local rc=$?
+
+ if [[ -f "$launcher" ]] && ! bash -n "$launcher" 2>/dev/null; then
+ msg_error "New launcher is broken. Restoring previous version."
+ if [[ -f "$menu_backup" ]]; then
+ cp -f "$menu_backup" "$launcher"
+ sed -i 's/\r$//' "$launcher" 2>/dev/null || true
+ chmod +x "$launcher"
+ fi
+ rc=1
+ fi
+
+ rm -f "$tmp_installer" "$menu_backup"
+ return $rc
+}
+
# ── Stable update check (main branch) ─────────────────────
check_updates_stable() {
local VERSION_URL="$REPO_MAIN/version.txt"
@@ -77,77 +128,49 @@ check_updates_stable() {
msg_warn "$(translate 'Starting ProxMenux update...')"
- if curl -fsSL "$INSTALL_URL" -o "$INSTALL_SCRIPT"; then
- chmod +x "$INSTALL_SCRIPT"
- bash "$INSTALL_SCRIPT" --update
- fi
+ run_installer_safely "$INSTALL_URL" "$INSTALL_SCRIPT"
fi
}
# ── Beta update check (develop branch) ────────────────────
+# Beta users track BOTH channels independently: a new stable on main is
+# still relevant to them (ends the beta cycle), and a new beta on develop
+# is their normal update path. We prompt for each one in turn — declining
+# stable doesn't skip the beta prompt.
check_updates_beta() {
- local BETA_VERSION_URL="$REPO_DEVELOP/beta_version.txt"
local STABLE_VERSION_URL="$REPO_MAIN/version.txt"
- local INSTALL_BETA_URL="$REPO_DEVELOP/install_proxmenux_beta.sh"
+ local BETA_VERSION_URL="$REPO_DEVELOP/beta_version.txt"
local INSTALL_STABLE_URL="$REPO_MAIN/install_proxmenux.sh"
- local INSTALL_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
+ local INSTALL_BETA_URL="$REPO_DEVELOP/install_proxmenux_beta.sh"
+ local INSTALL_STABLE_SCRIPT="$BASE_DIR/install_proxmenux.sh"
+ local INSTALL_BETA_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
- # ── 1. Check if a stable release has superseded the beta ──
- # If main's version.txt exists and is newer than local beta_version.txt,
- # the beta cycle is over and we invite the user to switch to stable.
- local STABLE_VERSION BETA_LOCAL_VERSION
- STABLE_VERSION="$(curl -fsSL "$STABLE_VERSION_URL" 2>/dev/null | head -n 1)"
- BETA_LOCAL_VERSION="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null)"
+ local REMOTE_STABLE LOCAL_STABLE REMOTE_BETA LOCAL_BETA
+ REMOTE_STABLE="$(curl -fsSL "$STABLE_VERSION_URL" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
+ LOCAL_STABLE="$(head -n 1 "$LOCAL_VERSION_FILE" 2>/dev/null | tr -d '[:space:]')"
+ REMOTE_BETA="$(curl -fsSL "$BETA_VERSION_URL" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
+ LOCAL_BETA="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null | tr -d '[:space:]')"
- if [[ -n "$STABLE_VERSION" && -n "$BETA_LOCAL_VERSION" ]]; then
- # Simple string comparison is enough if versions follow semver x.y.z
- if [[ "$STABLE_VERSION" != "$BETA_LOCAL_VERSION" ]] && \
- printf '%s\n' "$BETA_LOCAL_VERSION" "$STABLE_VERSION" | sort -V | tail -1 | grep -qx "$STABLE_VERSION"; then
-
- # Stable is newer — offer migration out of beta
- if whiptail --title "🎉 Stable Release Available" \
- --yesno "A stable release of ProxMenux is now available!\n\nStable version : $STABLE_VERSION\nYour beta : $BETA_LOCAL_VERSION\n\nThe beta program for this cycle is complete.\nWould you like to switch to the stable release now?\n\n(Choosing 'No' keeps you on the beta for now.)" \
- 16 68; then
-
- msg_warn "Switching to stable release $STABLE_VERSION ..."
-
- local tmp_installer="/tmp/install_proxmenux_stable_$$.sh"
- if curl -fsSL "$INSTALL_STABLE_URL" -o "$tmp_installer"; then
- chmod +x "$tmp_installer"
- bash "$tmp_installer"
- rm -f "$tmp_installer"
- else
- msg_error "Could not download the stable installer. Try manually:"
- echo
- echo " bash -c \"\$(wget -qLO - $INSTALL_STABLE_URL)\""
- echo
- fi
- return 0
- fi
- # User chose to stay on beta — continue normally
+ # ── 1. Stable release on main ──
+ if [[ -n "$LOCAL_STABLE" && -n "$REMOTE_STABLE" && "$LOCAL_STABLE" != "$REMOTE_STABLE" ]] && \
+ [[ "$(printf '%s\n%s\n' "$LOCAL_STABLE" "$REMOTE_STABLE" | sort -V | tail -1)" = "$REMOTE_STABLE" ]]; then
+ if whiptail --title "🎉 Stable Release Available" \
+ --yesno "A new ProxMenux stable release is available on main.\n\nInstalled stable : $LOCAL_STABLE\nNew stable : $REMOTE_STABLE\nYour beta : ${LOCAL_BETA:-n/a}\n\nInstall the stable release now?\n\n(Choosing 'No' keeps your beta — any beta update is offered next.)" \
+ 15 70 --defaultno; then
+ msg_warn "Installing stable release $REMOTE_STABLE ..."
+ run_installer_safely "$INSTALL_STABLE_URL" "$INSTALL_STABLE_SCRIPT"
return 0
fi
fi
- # ── 2. Check for a newer beta build on develop ─────────────
- [[ ! -f "$BETA_VERSION_FILE" ]] && return 0
-
- local REMOTE_BETA_VERSION
- REMOTE_BETA_VERSION="$(curl -fsSL "$BETA_VERSION_URL" 2>/dev/null | head -n 1)"
- [[ -z "$REMOTE_BETA_VERSION" ]] && return 0
- [[ "$BETA_LOCAL_VERSION" = "$REMOTE_BETA_VERSION" ]] && return 0
-
- if whiptail --title "Beta Update Available" \
- --yesno "A new beta build is available!\n\nInstalled beta : $BETA_LOCAL_VERSION\nNew beta build : $REMOTE_BETA_VERSION\n\nThis is a pre-release build from the develop branch.\nDo you want to update now?" \
- 13 64 --defaultno; then
-
- msg_warn "Updating to beta build $REMOTE_BETA_VERSION ..."
-
- if curl -fsSL "$INSTALL_BETA_URL" -o "$INSTALL_SCRIPT"; then
- chmod +x "$INSTALL_SCRIPT"
- bash "$INSTALL_SCRIPT" --update
- else
- msg_error "Could not download the beta installer from the develop branch."
+ # ── 2. Beta build on develop ──
+ if [[ -n "$LOCAL_BETA" && -n "$REMOTE_BETA" && "$LOCAL_BETA" != "$REMOTE_BETA" ]] && \
+ [[ "$(printf '%s\n%s\n' "$LOCAL_BETA" "$REMOTE_BETA" | sort -V | tail -1)" = "$REMOTE_BETA" ]]; then
+ if whiptail --title "Beta Update Available" \
+ --yesno "A new beta build is available on the develop branch.\n\nInstalled beta : $LOCAL_BETA\nNew beta build : $REMOTE_BETA\n\nUpdate the beta now?" \
+ 13 68 --defaultno; then
+ msg_warn "Updating to beta build $REMOTE_BETA ..."
+ run_installer_safely "$INSTALL_BETA_URL" "$INSTALL_BETA_SCRIPT"
fi
fi
}
From 94461120814f9ac28106573b3c8507a93a307228 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 00:38:32 +0200
Subject: [PATCH 03/12] Update beta_version.txt
---
beta_version.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/beta_version.txt b/beta_version.txt
index 02e184e7..867e5243 100644
--- a/beta_version.txt
+++ b/beta_version.txt
@@ -1 +1 @@
-1.1.9.5
\ No newline at end of file
+1.2.0
\ No newline at end of file
From 37c60cb82a5fa4f372e5000e0cf71671149ba4ce Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 00:58:25 +0200
Subject: [PATCH 04/12] Upate menu
---
install_proxmenux.sh | 13 ----
install_proxmenux_beta.sh | 7 --
menu | 139 ++++++++++++++++----------------------
3 files changed, 58 insertions(+), 101 deletions(-)
diff --git a/install_proxmenux.sh b/install_proxmenux.sh
index de98e245..be493cee 100755
--- a/install_proxmenux.sh
+++ b/install_proxmenux.sh
@@ -821,13 +821,6 @@ install_normal_version() {
cp "./version.txt" "$LOCAL_VERSION_FILE"
cp "./install_proxmenux.sh" "$BASE_DIR/install_proxmenux.sh"
- # Defensive: strip CRLF and reject a broken launcher before we declare success.
- sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
- if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
- msg_error "Installed launcher failed syntax check. Installation aborted."
- exit 1
- fi
-
# Wipe the scripts tree before copying so any file removed upstream
# (renamed, consolidated, deprecated) disappears from the user install.
# Only $BASE_DIR/scripts/ is cleared; config.json, cache.json,
@@ -971,12 +964,6 @@ install_translation_version() {
cp "./version.txt" "$LOCAL_VERSION_FILE"
cp "./install_proxmenux.sh" "$BASE_DIR/install_proxmenux.sh"
- sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
- if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
- msg_error "Installed launcher failed syntax check. Installation aborted."
- exit 1
- fi
-
mkdir -p "$BASE_DIR/scripts"
cp -r "./scripts/"* "$BASE_DIR/scripts/"
chmod -R +x "$BASE_DIR/scripts/"
diff --git a/install_proxmenux_beta.sh b/install_proxmenux_beta.sh
index f67bac3b..1a2a9fb5 100644
--- a/install_proxmenux_beta.sh
+++ b/install_proxmenux_beta.sh
@@ -566,13 +566,6 @@ install_beta() {
cp "./menu" "$INSTALL_DIR/$MENU_SCRIPT"
cp "./version.txt" "$LOCAL_VERSION_FILE" 2>/dev/null || true
- # Defensive: strip CRLF and reject a broken launcher before we declare success.
- sed -i 's/\r$//' "$INSTALL_DIR/$MENU_SCRIPT" "$UTILS_FILE" 2>/dev/null || true
- if ! bash -n "$INSTALL_DIR/$MENU_SCRIPT" 2>/dev/null; then
- msg_error "Installed launcher failed syntax check. Installation aborted."
- exit 1
- fi
-
# Store beta version marker
if [ -f "$TEMP_DIR/beta_version.txt" ]; then
cp "$TEMP_DIR/beta_version.txt" "$BETA_VERSION_FILE"
diff --git a/menu b/menu
index 046c00e0..1f9f62b4 100644
--- a/menu
+++ b/menu
@@ -55,57 +55,6 @@ check_updates() {
fi
}
-# ── Safe self-update ──────────────────────────────────────
-# Validates the downloaded installer and rolls back /usr/local/bin/menu
-# if the post-install launcher fails to parse. Why: a corrupted download
-# or CRLF contamination has already left users with an unusable launcher;
-# without a rollback the only recovery is curl-reinstall by hand.
-run_installer_safely() {
- local installer_url="$1"
- local installer_dest="$2"
- local launcher="/usr/local/bin/menu"
- local tmp_installer="/tmp/proxmenux_installer_$$.sh"
- local menu_backup="/tmp/proxmenux_menu_backup_$$"
-
- if ! curl -fsSL "$installer_url" -o "$tmp_installer"; then
- msg_error "Failed to download installer from $installer_url"
- return 1
- fi
-
- sed -i 's/\r$//' "$tmp_installer" 2>/dev/null || true
-
- if ! bash -n "$tmp_installer" 2>/dev/null; then
- msg_error "Downloaded installer has invalid syntax. Update aborted."
- rm -f "$tmp_installer"
- return 1
- fi
-
- [[ -f "$launcher" ]] && cp -f "$launcher" "$menu_backup"
-
- chmod +x "$tmp_installer"
- if [[ -n "$installer_dest" ]]; then
- cp -f "$tmp_installer" "$installer_dest"
- chmod +x "$installer_dest"
- bash "$installer_dest" --update
- else
- bash "$tmp_installer"
- fi
- local rc=$?
-
- if [[ -f "$launcher" ]] && ! bash -n "$launcher" 2>/dev/null; then
- msg_error "New launcher is broken. Restoring previous version."
- if [[ -f "$menu_backup" ]]; then
- cp -f "$menu_backup" "$launcher"
- sed -i 's/\r$//' "$launcher" 2>/dev/null || true
- chmod +x "$launcher"
- fi
- rc=1
- fi
-
- rm -f "$tmp_installer" "$menu_backup"
- return $rc
-}
-
# ── Stable update check (main branch) ─────────────────────
check_updates_stable() {
local VERSION_URL="$REPO_MAIN/version.txt"
@@ -128,49 +77,77 @@ check_updates_stable() {
msg_warn "$(translate 'Starting ProxMenux update...')"
- run_installer_safely "$INSTALL_URL" "$INSTALL_SCRIPT"
+ if curl -fsSL "$INSTALL_URL" -o "$INSTALL_SCRIPT"; then
+ chmod +x "$INSTALL_SCRIPT"
+ bash "$INSTALL_SCRIPT" --update
+ fi
fi
}
# ── Beta update check (develop branch) ────────────────────
-# Beta users track BOTH channels independently: a new stable on main is
-# still relevant to them (ends the beta cycle), and a new beta on develop
-# is their normal update path. We prompt for each one in turn — declining
-# stable doesn't skip the beta prompt.
check_updates_beta() {
- local STABLE_VERSION_URL="$REPO_MAIN/version.txt"
local BETA_VERSION_URL="$REPO_DEVELOP/beta_version.txt"
- local INSTALL_STABLE_URL="$REPO_MAIN/install_proxmenux.sh"
+ local STABLE_VERSION_URL="$REPO_MAIN/version.txt"
local INSTALL_BETA_URL="$REPO_DEVELOP/install_proxmenux_beta.sh"
- local INSTALL_STABLE_SCRIPT="$BASE_DIR/install_proxmenux.sh"
- local INSTALL_BETA_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
+ local INSTALL_STABLE_URL="$REPO_MAIN/install_proxmenux.sh"
+ local INSTALL_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
- local REMOTE_STABLE LOCAL_STABLE REMOTE_BETA LOCAL_BETA
- REMOTE_STABLE="$(curl -fsSL "$STABLE_VERSION_URL" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
- LOCAL_STABLE="$(head -n 1 "$LOCAL_VERSION_FILE" 2>/dev/null | tr -d '[:space:]')"
- REMOTE_BETA="$(curl -fsSL "$BETA_VERSION_URL" 2>/dev/null | head -n 1 | tr -d '[:space:]')"
- LOCAL_BETA="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null | tr -d '[:space:]')"
+ # ── 1. Check if a stable release has superseded the beta ──
+ # If main's version.txt exists and is newer than local beta_version.txt,
+ # the beta cycle is over and we invite the user to switch to stable.
+ local STABLE_VERSION BETA_LOCAL_VERSION
+ STABLE_VERSION="$(curl -fsSL "$STABLE_VERSION_URL" 2>/dev/null | head -n 1)"
+ BETA_LOCAL_VERSION="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null)"
- # ── 1. Stable release on main ──
- if [[ -n "$LOCAL_STABLE" && -n "$REMOTE_STABLE" && "$LOCAL_STABLE" != "$REMOTE_STABLE" ]] && \
- [[ "$(printf '%s\n%s\n' "$LOCAL_STABLE" "$REMOTE_STABLE" | sort -V | tail -1)" = "$REMOTE_STABLE" ]]; then
- if whiptail --title "🎉 Stable Release Available" \
- --yesno "A new ProxMenux stable release is available on main.\n\nInstalled stable : $LOCAL_STABLE\nNew stable : $REMOTE_STABLE\nYour beta : ${LOCAL_BETA:-n/a}\n\nInstall the stable release now?\n\n(Choosing 'No' keeps your beta — any beta update is offered next.)" \
- 15 70 --defaultno; then
- msg_warn "Installing stable release $REMOTE_STABLE ..."
- run_installer_safely "$INSTALL_STABLE_URL" "$INSTALL_STABLE_SCRIPT"
+ if [[ -n "$STABLE_VERSION" && -n "$BETA_LOCAL_VERSION" ]]; then
+ # Simple string comparison is enough if versions follow semver x.y.z
+ if [[ "$STABLE_VERSION" != "$BETA_LOCAL_VERSION" ]] && \
+ printf '%s\n' "$BETA_LOCAL_VERSION" "$STABLE_VERSION" | sort -V | tail -1 | grep -qx "$STABLE_VERSION"; then
+
+ # Stable is newer — offer migration out of beta
+ if whiptail --title "🎉 Stable Release Available" \
+ --yesno "A stable release of ProxMenux is now available!\n\nStable version : $STABLE_VERSION\nYour beta : $BETA_LOCAL_VERSION\n\nThe beta program for this cycle is complete.\nWould you like to switch to the stable release now?\n\n(Choosing 'No' keeps you on the beta for now.)" \
+ 16 68; then
+
+ msg_warn "Switching to stable release $STABLE_VERSION ..."
+
+ local tmp_installer="/tmp/install_proxmenux_stable_$$.sh"
+ if curl -fsSL "$INSTALL_STABLE_URL" -o "$tmp_installer"; then
+ chmod +x "$tmp_installer"
+ bash "$tmp_installer"
+ rm -f "$tmp_installer"
+ else
+ msg_error "Could not download the stable installer. Try manually:"
+ echo
+ echo " bash -c \"\$(wget -qLO - $INSTALL_STABLE_URL)\""
+ echo
+ fi
+ return 0
+ fi
+ # User chose to stay on beta — continue normally
return 0
fi
fi
- # ── 2. Beta build on develop ──
- if [[ -n "$LOCAL_BETA" && -n "$REMOTE_BETA" && "$LOCAL_BETA" != "$REMOTE_BETA" ]] && \
- [[ "$(printf '%s\n%s\n' "$LOCAL_BETA" "$REMOTE_BETA" | sort -V | tail -1)" = "$REMOTE_BETA" ]]; then
- if whiptail --title "Beta Update Available" \
- --yesno "A new beta build is available on the develop branch.\n\nInstalled beta : $LOCAL_BETA\nNew beta build : $REMOTE_BETA\n\nUpdate the beta now?" \
- 13 68 --defaultno; then
- msg_warn "Updating to beta build $REMOTE_BETA ..."
- run_installer_safely "$INSTALL_BETA_URL" "$INSTALL_BETA_SCRIPT"
+ # ── 2. Check for a newer beta build on develop ─────────────
+ [[ ! -f "$BETA_VERSION_FILE" ]] && return 0
+
+ local REMOTE_BETA_VERSION
+ REMOTE_BETA_VERSION="$(curl -fsSL "$BETA_VERSION_URL" 2>/dev/null | head -n 1)"
+ [[ -z "$REMOTE_BETA_VERSION" ]] && return 0
+ [[ "$BETA_LOCAL_VERSION" = "$REMOTE_BETA_VERSION" ]] && return 0
+
+ if whiptail --title "Beta Update Available" \
+ --yesno "A new beta build is available!\n\nInstalled beta : $BETA_LOCAL_VERSION\nNew beta build : $REMOTE_BETA_VERSION\n\nThis is a pre-release build from the develop branch.\nDo you want to update now?" \
+ 13 64 --defaultno; then
+
+ msg_warn "Updating to beta build $REMOTE_BETA_VERSION ..."
+
+ if curl -fsSL "$INSTALL_BETA_URL" -o "$INSTALL_SCRIPT"; then
+ chmod +x "$INSTALL_SCRIPT"
+ bash "$INSTALL_SCRIPT" --update
+ else
+ msg_error "Could not download the beta installer from the develop branch."
fi
fi
}
From 304629941482a484e5fc967f7f7d785418fc1f71 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 01:03:16 +0200
Subject: [PATCH 05/12] Update hw_grafics_menu.sh
---
scripts/menus/hw_grafics_menu.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/menus/hw_grafics_menu.sh b/scripts/menus/hw_grafics_menu.sh
index 2ea55817..0a6fb468 100644
--- a/scripts/menus/hw_grafics_menu.sh
+++ b/scripts/menus/hw_grafics_menu.sh
@@ -27,7 +27,7 @@ initialize_cache
while true; do
OPTION=$(dialog --colors --backtitle "ProxMenux" \
--title "$(translate "GPUs and Coral-TPU Menu")" \
- --menu "\n$(translate "Select an option:")" 24 78 16 \
+ --menu "\n$(translate "Select an option:")" 26 78 16 \
"" "\Z4──────────────────────── HOST ─────────────────────────\Zn" \
"1" "$(translate "Install/Update NVIDIA Drivers (Host + LXC)")" \
"2" "$(translate "Install/Update Coral TPU on Host")" \
From 3ca5a3624011da1d6095051a57569009252decad Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 01:13:13 +0200
Subject: [PATCH 06/12] Update config_menu.sh
---
scripts/menus/config_menu.sh | 38 +++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/scripts/menus/config_menu.sh b/scripts/menus/config_menu.sh
index 2a53e343..a2d24710 100644
--- a/scripts/menus/config_menu.sh
+++ b/scripts/menus/config_menu.sh
@@ -114,6 +114,33 @@ check_monitor_status() {
fi
}
+is_beta_program_active() {
+ [[ -f "$CONFIG_FILE" ]] || return 1
+ local flag
+ flag=$(jq -r '.beta_program.status // empty' "$CONFIG_FILE" 2>/dev/null)
+ [[ "$flag" == "active" ]]
+}
+
+deactivate_beta_program() {
+ if dialog --clear --backtitle "ProxMenux Configuration" \
+ --title "$(translate "Deactivate Beta Program")" \
+ --yesno "\n$(translate "You will stop receiving beta update prompts. Stable updates continue normally.\n\nTo rejoin the beta program later, run the beta installer again.\n\nDeactivate now?")" 14 64; then
+ local tmp
+ tmp=$(mktemp)
+ if jq '.beta_program.status = "inactive"' "$CONFIG_FILE" > "$tmp" 2>/dev/null; then
+ mv "$tmp" "$CONFIG_FILE"
+ dialog --clear --backtitle "ProxMenux Configuration" \
+ --title "$(translate "Beta Program Deactivated")" \
+ --msgbox "\n\n$(translate "Beta program deactivated. You will now receive stable updates only.")" 10 60
+ else
+ rm -f "$tmp"
+ dialog --clear --backtitle "ProxMenux Configuration" \
+ --title "$(translate "Error")" \
+ --msgbox "\n\n$(translate "Could not update config file.")" 10 50
+ fi
+ fi
+}
+
toggle_monitor_service() {
local status=$(check_monitor_status)
@@ -211,7 +238,13 @@ show_config_menu() {
option_actions[$option_num]="show_monitor_status"
((option_num++))
fi
-
+
+ if is_beta_program_active; then
+ menu_options+=("$option_num" "$(translate "Deactivate Beta Program")")
+ option_actions[$option_num]="deactivate_beta"
+ ((option_num++))
+ fi
+
# Build menu based on installation type
if [ "$install_type" = "translation" ]; then
menu_options+=("$option_num" "$(translate "Change Language")")
@@ -256,6 +289,9 @@ show_config_menu() {
"show_monitor_status")
show_monitor_status
;;
+ "deactivate_beta")
+ deactivate_beta_program
+ ;;
"change_language")
change_language
;;
From 802dc491f88862fdbc17fb6d2b1e707f1eb65e61 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 01:20:39 +0200
Subject: [PATCH 07/12] Update menu
---
menu | 80 ++++++++++++++++++++++++++----------------------------------
1 file changed, 34 insertions(+), 46 deletions(-)
diff --git a/menu b/menu
index 1f9f62b4..c0e6001c 100644
--- a/menu
+++ b/menu
@@ -84,68 +84,56 @@ check_updates_stable() {
fi
}
-# ── Beta update check (develop branch) ────────────────────
+# ── Beta-mode update check (main + develop) ───────────────
+# When the beta program is active, check BOTH channels:
+# 1. main → prompt if a newer stable is available
+# 2. develop → prompt if a newer beta build is available
+# Stable check runs first. Declining it falls through to the beta check.
check_updates_beta() {
- local BETA_VERSION_URL="$REPO_DEVELOP/beta_version.txt"
- local STABLE_VERSION_URL="$REPO_MAIN/version.txt"
- local INSTALL_BETA_URL="$REPO_DEVELOP/install_proxmenux_beta.sh"
- local INSTALL_STABLE_URL="$REPO_MAIN/install_proxmenux.sh"
- local INSTALL_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
+ # ── 1. Stable release on main ──
+ if [[ -f "$LOCAL_VERSION_FILE" ]]; then
+ local REMOTE_STABLE LOCAL_STABLE
+ REMOTE_STABLE="$(curl -fsSL "$REPO_MAIN/version.txt" 2>/dev/null | head -n 1)"
+ LOCAL_STABLE="$(head -n 1 "$LOCAL_VERSION_FILE" 2>/dev/null)"
- # ── 1. Check if a stable release has superseded the beta ──
- # If main's version.txt exists and is newer than local beta_version.txt,
- # the beta cycle is over and we invite the user to switch to stable.
- local STABLE_VERSION BETA_LOCAL_VERSION
- STABLE_VERSION="$(curl -fsSL "$STABLE_VERSION_URL" 2>/dev/null | head -n 1)"
- BETA_LOCAL_VERSION="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null)"
+ if [[ -n "$REMOTE_STABLE" && -n "$LOCAL_STABLE" && "$LOCAL_STABLE" != "$REMOTE_STABLE" ]] && \
+ [[ "$(printf '%s\n%s\n' "$LOCAL_STABLE" "$REMOTE_STABLE" | sort -V | tail -1)" = "$REMOTE_STABLE" ]]; then
- if [[ -n "$STABLE_VERSION" && -n "$BETA_LOCAL_VERSION" ]]; then
- # Simple string comparison is enough if versions follow semver x.y.z
- if [[ "$STABLE_VERSION" != "$BETA_LOCAL_VERSION" ]] && \
- printf '%s\n' "$BETA_LOCAL_VERSION" "$STABLE_VERSION" | sort -V | tail -1 | grep -qx "$STABLE_VERSION"; then
+ if whiptail --title "$(translate 'Update Available')" \
+ --yesno "$(translate 'New version available') ($REMOTE_STABLE)\n\n$(translate 'Do you want to update now?')" \
+ 10 60 --defaultno; then
- # Stable is newer — offer migration out of beta
- if whiptail --title "🎉 Stable Release Available" \
- --yesno "A stable release of ProxMenux is now available!\n\nStable version : $STABLE_VERSION\nYour beta : $BETA_LOCAL_VERSION\n\nThe beta program for this cycle is complete.\nWould you like to switch to the stable release now?\n\n(Choosing 'No' keeps you on the beta for now.)" \
- 16 68; then
+ msg_warn "$(translate 'Starting ProxMenux update...')"
- msg_warn "Switching to stable release $STABLE_VERSION ..."
-
- local tmp_installer="/tmp/install_proxmenux_stable_$$.sh"
- if curl -fsSL "$INSTALL_STABLE_URL" -o "$tmp_installer"; then
- chmod +x "$tmp_installer"
- bash "$tmp_installer"
- rm -f "$tmp_installer"
- else
- msg_error "Could not download the stable installer. Try manually:"
- echo
- echo " bash -c \"\$(wget -qLO - $INSTALL_STABLE_URL)\""
- echo
+ local INSTALL_STABLE_SCRIPT="$BASE_DIR/install_proxmenux.sh"
+ if curl -fsSL "$REPO_MAIN/install_proxmenux.sh" -o "$INSTALL_STABLE_SCRIPT"; then
+ chmod +x "$INSTALL_STABLE_SCRIPT"
+ bash "$INSTALL_STABLE_SCRIPT" --update
+ return 0
fi
- return 0
fi
- # User chose to stay on beta — continue normally
- return 0
fi
fi
- # ── 2. Check for a newer beta build on develop ─────────────
+ # ── 2. Beta build on develop ──
[[ ! -f "$BETA_VERSION_FILE" ]] && return 0
- local REMOTE_BETA_VERSION
- REMOTE_BETA_VERSION="$(curl -fsSL "$BETA_VERSION_URL" 2>/dev/null | head -n 1)"
- [[ -z "$REMOTE_BETA_VERSION" ]] && return 0
- [[ "$BETA_LOCAL_VERSION" = "$REMOTE_BETA_VERSION" ]] && return 0
+ local REMOTE_BETA LOCAL_BETA
+ REMOTE_BETA="$(curl -fsSL "$REPO_DEVELOP/beta_version.txt" 2>/dev/null | head -n 1)"
+ LOCAL_BETA="$(head -n 1 "$BETA_VERSION_FILE" 2>/dev/null)"
+ [[ -z "$REMOTE_BETA" || -z "$LOCAL_BETA" || "$LOCAL_BETA" = "$REMOTE_BETA" ]] && return 0
+ [[ "$(printf '%s\n%s\n' "$LOCAL_BETA" "$REMOTE_BETA" | sort -V | tail -1)" = "$REMOTE_BETA" ]] || return 0
if whiptail --title "Beta Update Available" \
- --yesno "A new beta build is available!\n\nInstalled beta : $BETA_LOCAL_VERSION\nNew beta build : $REMOTE_BETA_VERSION\n\nThis is a pre-release build from the develop branch.\nDo you want to update now?" \
- 13 64 --defaultno; then
+ --yesno "A new beta build is available!\n\nInstalled beta : $LOCAL_BETA\nNew beta build : $REMOTE_BETA\n\nDo you want to update now?" \
+ 12 64 --defaultno; then
- msg_warn "Updating to beta build $REMOTE_BETA_VERSION ..."
+ msg_warn "Updating to beta build $REMOTE_BETA ..."
- if curl -fsSL "$INSTALL_BETA_URL" -o "$INSTALL_SCRIPT"; then
- chmod +x "$INSTALL_SCRIPT"
- bash "$INSTALL_SCRIPT" --update
+ local INSTALL_BETA_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
+ if curl -fsSL "$REPO_DEVELOP/install_proxmenux_beta.sh" -o "$INSTALL_BETA_SCRIPT"; then
+ chmod +x "$INSTALL_BETA_SCRIPT"
+ bash "$INSTALL_BETA_SCRIPT" --update
else
msg_error "Could not download the beta installer from the develop branch."
fi
From f6e9497f1e38e23ca754a1b8250f27a904d4e6e0 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 01:23:47 +0200
Subject: [PATCH 08/12] Delete version.txt
---
version.txt | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 version.txt
diff --git a/version.txt b/version.txt
deleted file mode 100644
index 512a1faa..00000000
--- a/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-1.1.9
From c8b1cd0fab33c46503bd4080d4efed6ddaa49503 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 08:58:50 +0200
Subject: [PATCH 09/12] Update nvidia_installer.sh
---
scripts/gpu_tpu/nvidia_installer.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/gpu_tpu/nvidia_installer.sh b/scripts/gpu_tpu/nvidia_installer.sh
index a9337769..c5026dd7 100644
--- a/scripts/gpu_tpu/nvidia_installer.sh
+++ b/scripts/gpu_tpu/nvidia_installer.sh
@@ -529,7 +529,7 @@ complete_nvidia_uninstall() {
nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'NVIDIA uninstaller completed.')"
fi
-
+ msg_ok "$(translate 'NVIDIA uninstallation steps completed.')" | tee -a "$screen_capture"
cleanup_nvidia_dkms
msg_info "$(translate 'Removing NVIDIA packages...')"
From 67000f5ff1dc815c845d08e3e05f3dd272adc80f Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 09:13:52 +0200
Subject: [PATCH 10/12] Update nvidia_installer.sh
---
scripts/gpu_tpu/nvidia_installer.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/gpu_tpu/nvidia_installer.sh b/scripts/gpu_tpu/nvidia_installer.sh
index c5026dd7..b5b40ad2 100644
--- a/scripts/gpu_tpu/nvidia_installer.sh
+++ b/scripts/gpu_tpu/nvidia_installer.sh
@@ -428,6 +428,7 @@ EOF
# Attempt to unload nouveau if currently loaded
if lsmod | grep -q "^nouveau "; then
+ stop_spinner
msg_info "$(translate 'Nouveau module is loaded, attempting to unload...')"
modprobe -r nouveau 2>/dev/null || true
@@ -550,6 +551,7 @@ complete_nvidia_uninstall() {
update_component_status "nvidia_driver" "removed" "" "gpu" '{}'
msg_ok "$(translate 'Complete NVIDIA uninstallation finished.')" | tee -a "$screen_capture"
+ stop_spinner
}
cleanup_nvidia_dkms() {
From ff7b1e10a484583e881c1412e1246ca087b53840 Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 17:55:35 +0200
Subject: [PATCH 11/12] Create version.txt
---
version.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 version.txt
diff --git a/version.txt b/version.txt
new file mode 100644
index 00000000..26aaba0e
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+1.2.0
From 43959fc758c83092d95a52611fa32a93d45b04ba Mon Sep 17 00:00:00 2001
From: MacRimi
Date: Sat, 18 Apr 2026 17:58:02 +0200
Subject: [PATCH 12/12] Delete beta_version.txt
---
beta_version.txt | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 beta_version.txt
diff --git a/beta_version.txt b/beta_version.txt
deleted file mode 100644
index 867e5243..00000000
--- a/beta_version.txt
+++ /dev/null
@@ -1 +0,0 @@
-1.2.0
\ No newline at end of file