This commit is contained in:
2026-07-28 08:49:38 -05:00
parent 8c73cb7a53
commit 3ea33a17d8
4 changed files with 294 additions and 1 deletions
+14
View File
@@ -107,6 +107,20 @@ replacing `/bin/gzip`. APT remains dual-stack by default; its conditional IPv4
compatibility check is optional. Global vzdump bandwidth and I/O-priority compatibility check is optional. Global vzdump bandwidth and I/O-priority
changes are available through a separate explicit menu. changes are available through a separate explicit menu.
Pulse can also be deployed without installing TA-ProxMenu. Run the standalone
bootstrap as root on a Proxmox VE host:
```bash
bash <(curl -fsSL https://git.schroedercity.com/TAI/TA-ProxMenu/raw/branch/V2/install-pulse.sh)
```
The bootstrap downloads the Pulse deployment module and its LXC storage helper
from the same branch into a protected temporary directory, validates their Bash
syntax and expected entry points, runs the normal interactive Pulse workflow,
and removes the temporary files afterward. It does not clone or install
TA-ProxMenu. Set `TAPM_PULSE_SOURCE_BRANCH` before running it to use another
branch.
VirtIO downloads are managed from a dedicated submenu. The stable release is VirtIO downloads are managed from a dedicated submenu. The stable release is
checked only when that submenu is opened, and curated compatibility ISOs are checked only when that submenu is opened, and curated compatibility ISOs are
available for Windows Server 2008, 2008 R2, 2012/R2, and 2016. Downloads are available for Windows Server 2008, 2008 R2, 2012/R2, and 2016. Downloads are
+1 -1
View File
@@ -3,7 +3,7 @@
action="${1:-}" action="${1:-}"
FOLDER='/opt/idssys/ta-proxmenu' FOLDER='/opt/idssys/ta-proxmenu'
VERS='2026.7.26-9' VERS='2026.7.28-1'
noupdate=' ' noupdate=' '
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env bash
# Standalone TA-managed Pulse LXC deployment bootstrap for Proxmox VE.
set -u -o pipefail
TAPM_PULSE_SOURCE_BRANCH="${TAPM_PULSE_SOURCE_BRANCH:-V2}"
TAPM_PULSE_SOURCE_BASE="${TAPM_PULSE_SOURCE_BASE:-https://git.schroedercity.com/TAI/TA-ProxMenu/raw/branch/${TAPM_PULSE_SOURCE_BRANCH}}"
TAPM_PULSE_BOOTSTRAP_DIR=''
TAPM_TEMP_DIR=''
declare -a TAPM_TEMP_DIRS=()
declare -A idsCL=()
TAPM_PULSE_VALID_SOURCE_BRANCH() {
local branch="${1:-}"
[[ -n "$branch" &&
"$branch" =~ ^[A-Za-z0-9._/-]+$ &&
"$branch" != /* &&
"$branch" != */ &&
"$branch" != *..* ]]
}
TAPM_PULSE_VALID_SOURCE_BASE() {
local url="${1:-}"
[[ "$url" == https://* &&
"$url" != *$'\n'* &&
"$url" != *$'\r'* &&
"$url" != *'"'* &&
"$url" != *\\* &&
"$url" != *[[:space:]]* ]]
}
TAPM_PULSE_DEFINE_COLORS() {
if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
idsCL[Default]=$'\e[0m'
idsCL[Red]=$'\e[31m'
idsCL[Green]=$'\e[32m'
idsCL[White]=$'\e[97m'
idsCL[LightRed]=$'\e[91m'
idsCL[LightGreen]=$'\e[92m'
idsCL[LightYellow]=$'\e[93m'
idsCL[LightCyan]=$'\e[96m'
else
idsCL[Default]=''
idsCL[Red]=''
idsCL[Green]=''
idsCL[White]=''
idsCL[LightRed]=''
idsCL[LightGreen]=''
idsCL[LightYellow]=''
idsCL[LightCyan]=''
fi
}
TAPM_CLEAN_TEMP_DIR() {
local temp_dir="${1:-}"
if [[ "$temp_dir" == /tmp/ta-proxmenu-* && -d "$temp_dir" ]]; then
rm -rf -- "$temp_dir"
fi
}
TAPM_CLEAN_ALL_TEMP_DIRS() {
local temp_dir
for temp_dir in "${TAPM_TEMP_DIRS[@]}"; do
TAPM_CLEAN_TEMP_DIR "$temp_dir"
done
TAPM_CLEAN_TEMP_DIR "$TAPM_PULSE_BOOTSTRAP_DIR"
}
TAPM_CREATE_TEMP_DIR() {
local label="${1:-installer}"
TAPM_TEMP_DIR="$(mktemp -d "/tmp/ta-proxmenu-${label}.XXXXXX")" || {
echo -e "${idsCL[LightRed]}Unable to create a temporary installer directory.${idsCL[Default]}"
return 1
}
if ! chmod 0700 "$TAPM_TEMP_DIR"; then
TAPM_CLEAN_TEMP_DIR "$TAPM_TEMP_DIR"
echo -e "${idsCL[LightRed]}Unable to secure the temporary installer directory.${idsCL[Default]}"
return 1
fi
TAPM_TEMP_DIRS+=("$TAPM_TEMP_DIR")
}
TAPM_DOWNLOAD_HTTPS() {
local url="$1"
local destination="$2"
local label="${3:-Installer}"
if ! TAPM_PULSE_VALID_SOURCE_BASE "$url"; then
echo -e "${idsCL[LightRed]}${label} requires a valid HTTPS URL.${idsCL[Default]}"
return 1
fi
if ! printf 'url = "%s"\n' "$url" |
curl --fail --location --silent --show-error \
--proto '=https' --proto-redir '=https' \
--output "$destination" --config -; then
echo -e "${idsCL[LightRed]}${label} download failed.${idsCL[Default]}"
return 1
fi
if [[ ! -s "$destination" ]]; then
echo -e "${idsCL[LightRed]}${label} download was empty.${idsCL[Default]}"
return 1
fi
}
EXIT1() {
stty echo 2>/dev/null || true
printf '%s' "${idsCL[Default]}"
}
SELECT_MENU() {
local title="$1"
local labels_name="$2"
local values_name="$3"
local allow_back="${4:-1}"
local -n labels_ref="$labels_name"
local -n values_ref="$values_name"
local selected=0
local key sequence index
while true; do
if [[ -t 1 ]]; then
clear 2>/dev/null || printf '\e[H\e[2J'
fi
echo
echo -e " ${idsCL[LightCyan]}${title}${idsCL[Default]}"
echo
for index in "${!labels_ref[@]}"; do
if (( index == selected )); then
printf '\e[7m %d %-64s\e[0m\n' \
"$((index + 1))" "${labels_ref[$index]}"
else
printf ' %d %s\n' "$((index + 1))" "${labels_ref[$index]}"
fi
done
echo
if (( allow_back == 1 )); then
echo " ↑/↓ Navigate Enter Select Number Quick Select ←/Esc/B Back Q Quit"
else
echo " ↑/↓ Navigate Enter Select Number Quick Select Q Quit"
fi
IFS= read -rsn1 key
case "$key" in
"")
MENU_SELECTION="${values_ref[$selected]}"
return 0
;;
[1-9])
index=$((10#$key - 1))
if (( index < ${#values_ref[@]} )); then
MENU_SELECTION="${values_ref[$index]}"
return 0
fi
;;
[Qq])
MENU_SELECTION=quit
return 0
;;
[Bb])
if (( allow_back == 1 )); then
MENU_SELECTION=back
return 0
fi
;;
$'\e')
sequence=''
IFS= read -rsn2 -t 0.1 sequence || true
case "$sequence" in
"[A"|"OA")
((selected = (selected - 1 + ${#labels_ref[@]}) % ${#labels_ref[@]}))
;;
"[B"|"OB")
((selected = (selected + 1) % ${#labels_ref[@]}))
;;
"[C"|"OC")
MENU_SELECTION="${values_ref[$selected]}"
return 0
;;
"[D"|"OD"|"")
if (( allow_back == 1 )); then
MENU_SELECTION=back
return 0
fi
;;
esac
;;
esac
done
}
TAPM_PULSE_STANDALONE_MAIN() {
local iso_helpers pulse_module
TAPM_PULSE_DEFINE_COLORS
if (( BASH_VERSINFO[0] < 4 ||
(BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3) )); then
echo "Pulse deployment requires Bash 4.3 or newer." >&2
return 1
fi
if (( EUID != 0 )); then
echo "Run this installer as root on a Proxmox VE host." >&2
return 1
fi
for command in curl pct pvesh; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Required command '${command}' was not found." >&2
return 1
fi
done
if ! TAPM_PULSE_VALID_SOURCE_BRANCH "$TAPM_PULSE_SOURCE_BRANCH" ||
! TAPM_PULSE_VALID_SOURCE_BASE "$TAPM_PULSE_SOURCE_BASE"; then
echo "The configured TA-ProxMenu source branch or URL is invalid." >&2
return 1
fi
TAPM_PULSE_BOOTSTRAP_DIR="$(
mktemp -d /tmp/ta-proxmenu-pulse-bootstrap.XXXXXX
)" || return 1
chmod 0700 "$TAPM_PULSE_BOOTSTRAP_DIR" || return 1
iso_helpers="${TAPM_PULSE_BOOTSTRAP_DIR}/deploy-iso-nfs-lxc.sh"
pulse_module="${TAPM_PULSE_BOOTSTRAP_DIR}/deploy-pulse-lxc.sh"
echo "Downloading the TA-managed Pulse deployment components..."
TAPM_DOWNLOAD_HTTPS \
"${TAPM_PULSE_SOURCE_BASE}/inc/deploy-iso-nfs-lxc.sh" \
"$iso_helpers" "LXC storage helper" || return 1
TAPM_DOWNLOAD_HTTPS \
"${TAPM_PULSE_SOURCE_BASE}/inc/deploy-pulse-lxc.sh" \
"$pulse_module" "Pulse deployment module" || return 1
bash -n "$iso_helpers" "$pulse_module" || {
echo "A downloaded Pulse deployment component failed validation." >&2
return 1
}
grep -q '^TAPM_ISO_NFS_SELECT_STORAGE()' "$iso_helpers" &&
grep -q '^TAPM_DEPLOY_PULSE_LXC()' "$pulse_module" || {
echo "The downloaded files were not recognized as TAPM deployment components." >&2
return 1
}
# shellcheck disable=SC1090
source "$iso_helpers"
# shellcheck disable=SC1090
source "$pulse_module"
TAPM_DEPLOY_PULSE_LXC
}
if [[ "${TAPM_PULSE_STANDALONE_NO_MAIN:-0}" != 1 ]]; then
trap TAPM_CLEAN_ALL_TEMP_DIRS EXIT
TAPM_PULSE_STANDALONE_MAIN "$@"
fi
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -u -o pipefail
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "${TEST_ROOT}/tests/testlib.sh"
TAPM_PULSE_STANDALONE_NO_MAIN=1
source "${TEST_ROOT}/install-pulse.sh"
assert_success "default V2 source branch accepted" \
TAPM_PULSE_VALID_SOURCE_BRANCH V2
assert_success "nested release branch accepted" \
TAPM_PULSE_VALID_SOURCE_BRANCH releases/pulse-6
assert_failure "empty source branch rejected" \
TAPM_PULSE_VALID_SOURCE_BRANCH ''
assert_failure "source branch traversal rejected" \
TAPM_PULSE_VALID_SOURCE_BRANCH '../main'
assert_success "HTTPS source base accepted" \
TAPM_PULSE_VALID_SOURCE_BASE \
https://git.schroedercity.com/TAI/TA-ProxMenu/raw/branch/V2
assert_failure "HTTP source base rejected" \
TAPM_PULSE_VALID_SOURCE_BASE \
http://git.schroedercity.com/TAI/TA-ProxMenu/raw/branch/V2
finish_tests