Merge branch 'community-scripts:main' into pr-update-lxcs-services
This commit is contained in:
197
misc/build.func
197
misc/build.func
@@ -186,92 +186,92 @@ ssh_check() {
|
||||
fi
|
||||
}
|
||||
|
||||
select_storage() {
|
||||
local CLASS=$1 CONTENT CONTENT_LABEL
|
||||
case $CLASS in
|
||||
container)
|
||||
CONTENT='rootdir'
|
||||
CONTENT_LABEL='Container'
|
||||
;;
|
||||
template)
|
||||
CONTENT='vztmpl'
|
||||
CONTENT_LABEL='Template'
|
||||
;;
|
||||
iso)
|
||||
CONTENT='iso'
|
||||
CONTENT_LABEL='ISO image'
|
||||
;;
|
||||
images)
|
||||
CONTENT='images'
|
||||
CONTENT_LABEL='VM Disk image'
|
||||
;;
|
||||
backup)
|
||||
CONTENT='backup'
|
||||
CONTENT_LABEL='Backup'
|
||||
;;
|
||||
snippets)
|
||||
CONTENT='snippets'
|
||||
CONTENT_LABEL='Snippets'
|
||||
;;
|
||||
*)
|
||||
msg_error "Invalid storage class '$CLASS'."
|
||||
exit 201
|
||||
;;
|
||||
esac
|
||||
# select_storage() {
|
||||
# local CLASS=$1 CONTENT CONTENT_LABEL
|
||||
# case $CLASS in
|
||||
# container)
|
||||
# CONTENT='rootdir'
|
||||
# CONTENT_LABEL='Container'
|
||||
# ;;
|
||||
# template)
|
||||
# CONTENT='vztmpl'
|
||||
# CONTENT_LABEL='Template'
|
||||
# ;;
|
||||
# iso)
|
||||
# CONTENT='iso'
|
||||
# CONTENT_LABEL='ISO image'
|
||||
# ;;
|
||||
# images)
|
||||
# CONTENT='images'
|
||||
# CONTENT_LABEL='VM Disk image'
|
||||
# ;;
|
||||
# backup)
|
||||
# CONTENT='backup'
|
||||
# CONTENT_LABEL='Backup'
|
||||
# ;;
|
||||
# snippets)
|
||||
# CONTENT='snippets'
|
||||
# CONTENT_LABEL='Snippets'
|
||||
# ;;
|
||||
# *)
|
||||
# msg_error "Invalid storage class '$CLASS'."
|
||||
# exit 201
|
||||
# ;;
|
||||
# esac
|
||||
|
||||
command -v whiptail >/dev/null || {
|
||||
msg_error "whiptail missing."
|
||||
exit 220
|
||||
}
|
||||
command -v numfmt >/dev/null || {
|
||||
msg_error "numfmt missing."
|
||||
exit 221
|
||||
}
|
||||
# command -v whiptail >/dev/null || {
|
||||
# msg_error "whiptail missing."
|
||||
# exit 220
|
||||
# }
|
||||
# command -v numfmt >/dev/null || {
|
||||
# msg_error "numfmt missing."
|
||||
# exit 221
|
||||
# }
|
||||
|
||||
local -a MENU
|
||||
while read -r line; do
|
||||
local TAG=$(echo "$line" | awk '{print $1}')
|
||||
local TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
|
||||
local FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf "%9sB", $6}')
|
||||
MENU+=("$TAG" "Type: $TYPE Free: $FREE" "OFF")
|
||||
done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
|
||||
# local -a MENU
|
||||
# while read -r line; do
|
||||
# local TAG=$(echo "$line" | awk '{print $1}')
|
||||
# local TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
|
||||
# local FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf "%9sB", $6}')
|
||||
# MENU+=("$TAG" "Type: $TYPE Free: $FREE" "OFF")
|
||||
# done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
|
||||
|
||||
if [ ${#MENU[@]} -eq 0 ]; then
|
||||
msg_error "No storage found for content type '$CONTENT'."
|
||||
exit 203
|
||||
fi
|
||||
# if [ ${#MENU[@]} -eq 0 ]; then
|
||||
# msg_error "No storage found for content type '$CONTENT'."
|
||||
# exit 203
|
||||
# fi
|
||||
|
||||
if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
|
||||
echo "${MENU[0]}"
|
||||
return
|
||||
fi
|
||||
# if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
|
||||
# echo "${MENU[0]}"
|
||||
# return
|
||||
# fi
|
||||
|
||||
local STORAGE
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
|
||||
16 70 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || {
|
||||
msg_error "Storage selection cancelled by user."
|
||||
exit 202
|
||||
}
|
||||
echo "$STORAGE"
|
||||
}
|
||||
# local STORAGE
|
||||
# STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
# "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
|
||||
# 16 70 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || {
|
||||
# msg_error "Storage selection cancelled by user."
|
||||
# exit 202
|
||||
# }
|
||||
# echo "$STORAGE"
|
||||
# }
|
||||
|
||||
manage_default_storage() {
|
||||
local file="/usr/local/community-scripts/default_storage"
|
||||
mkdir -p /usr/local/community-scripts
|
||||
# manage_default_storage() {
|
||||
# local file="/usr/local/community-scripts/default_storage"
|
||||
# mkdir -p /usr/local/community-scripts
|
||||
|
||||
local tmpl=$(select_storage template)
|
||||
local cont=$(select_storage container)
|
||||
# local tmpl=$(select_storage template)
|
||||
# local cont=$(select_storage container)
|
||||
|
||||
cat <<EOF >"$file"
|
||||
TEMPLATE_STORAGE=$tmpl
|
||||
CONTAINER_STORAGE=$cont
|
||||
EOF
|
||||
# cat <<EOF >"$file"
|
||||
# TEMPLATE_STORAGE=$tmpl
|
||||
# CONTAINER_STORAGE=$cont
|
||||
# EOF
|
||||
|
||||
msg_ok "Default Storage set: Template=${BL}$tmpl${CL} ${GN}|${CL} Container=${BL}$cont${CL}"
|
||||
whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
|
||||
--msgbox "Default Storage set:\n\nTemplate: $tmpl\nContainer: $cont" 10 58
|
||||
}
|
||||
# msg_ok "Default Storage set: Template=${BL}$tmpl${CL} ${GN}|${CL} Container=${BL}$cont${CL}"
|
||||
# whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
|
||||
# --msgbox "Default Storage set:\n\nTemplate: $tmpl\nContainer: $cont" 10 58
|
||||
# }
|
||||
|
||||
base_settings() {
|
||||
# Default Settings
|
||||
@@ -333,13 +333,13 @@ write_config() {
|
||||
|
||||
CT_TYPE="${CT_TYPE}"
|
||||
DISK_SIZE="${DISK_SIZE}"
|
||||
CORE_COUNT="${DISK_SIZE}"
|
||||
CORE_COUNT="${CORE_COUNT}"
|
||||
RAM_SIZE="${RAM_SIZE}"
|
||||
HN="${HN}"
|
||||
BRG="${BRG}"
|
||||
APT_CACHER_IP="${APT_CACHER_IP:-none}"
|
||||
DISABLEIP6="${DISABLEIP6}"
|
||||
PW="${PW:-none}"
|
||||
PW='${PW:-none}'
|
||||
SSH="${SSH}"
|
||||
SSH_AUTHORIZED_KEY="${SSH_AUTHORIZED_KEY}"
|
||||
VERBOSE="${VERBOSE}"
|
||||
@@ -351,6 +351,7 @@ SD="${SD:-none}"
|
||||
MAC="${MAC:-none}"
|
||||
NS="${NS:-none}"
|
||||
NET="${NET}"
|
||||
FUSE="${ENABLE_FUSE}"
|
||||
|
||||
EOF
|
||||
echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}"
|
||||
@@ -364,7 +365,7 @@ EOF
|
||||
|
||||
CT_TYPE="${CT_TYPE}"
|
||||
DISK_SIZE="${DISK_SIZE}"
|
||||
CORE_COUNT="${DISK_SIZE}"
|
||||
CORE_COUNT="${CORE_COUNT}"
|
||||
RAM_SIZE="${RAM_SIZE}"
|
||||
HN="${HN}"
|
||||
BRG="${BRG}"
|
||||
@@ -382,6 +383,7 @@ SD="${SD:-none}"
|
||||
MAC="${MAC:-none}"
|
||||
NS="${NS:-none}"
|
||||
NET="${NET}"
|
||||
FUSE="${ENABLE_FUSE}"
|
||||
|
||||
EOF
|
||||
echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}"
|
||||
@@ -402,9 +404,11 @@ echo_default() {
|
||||
|
||||
# Output the selected values with icons
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}"
|
||||
echo -e "${OS}${BOLD}${DGN}Operating System: $var_os | Version: $var_version${CL}"
|
||||
echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}"
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB | ${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT} | ${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
if [ "$VERB" == "yes" ]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}"
|
||||
fi
|
||||
@@ -861,12 +865,12 @@ advanced_settings() {
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
fi
|
||||
|
||||
# if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then
|
||||
# ENABLE_FUSE="yes"
|
||||
# else
|
||||
# ENABLE_FUSE="no"
|
||||
# fi
|
||||
# echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}"
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then
|
||||
ENABLE_FUSE="yes"
|
||||
else
|
||||
ENABLE_FUSE="no"
|
||||
fi
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}"
|
||||
|
||||
if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
|
||||
VERBOSE="yes"
|
||||
@@ -1195,12 +1199,10 @@ build_container() {
|
||||
-unprivileged $CT_TYPE
|
||||
$PW
|
||||
"
|
||||
# This executes create_lxc.sh and creates the container and .conf file
|
||||
CREATE_CMD="bash -c \"\$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)\""
|
||||
eval "$CREATE_CMD"
|
||||
bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)"
|
||||
RET=$?
|
||||
if [[ $RET -ne 0 ]]; then
|
||||
msg_error "in line $LINENO: exit code $RET: while executing command $CREATE_CMD"
|
||||
msg_error "rny: in line $LINENO: exit code $RET: while executing create_lxc.sh"
|
||||
exit $RET
|
||||
fi
|
||||
|
||||
@@ -1273,21 +1275,24 @@ EOF'
|
||||
pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null"
|
||||
else
|
||||
sleep 3
|
||||
# Set locale and timezone before update
|
||||
|
||||
pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen"
|
||||
pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \
|
||||
echo LANG=\$locale_line >/etc/default/locale && \
|
||||
locale-gen >/dev/null && \
|
||||
export LANG=\$locale_line"
|
||||
|
||||
pct exec "$CTID" -- bash -c "echo $tz >/etc/timezone && ln -sf /usr/share/zoneinfo/$tz /etc/localtime"
|
||||
if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then
|
||||
pct exec "$CTID" -- bash -c "echo $tz >/etc/timezone && ln -sf /usr/share/zoneinfo/$tz /etc/localtime"
|
||||
else
|
||||
msg_info "Skipping timezone setup – zone '$tz' not found in container"
|
||||
fi
|
||||
|
||||
# Install curl
|
||||
pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null"
|
||||
fi
|
||||
msg_ok "Customized LXC Container"
|
||||
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/$var_install.sh)" $?
|
||||
|
||||
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)" $?
|
||||
}
|
||||
|
||||
# This function sets the description of the container.
|
||||
|
||||
366
misc/core.func
366
misc/core.func
@@ -9,21 +9,21 @@
|
||||
# }
|
||||
# fi
|
||||
|
||||
trap 'on_error $? $LINENO' ERR
|
||||
trap 'on_exit' EXIT
|
||||
trap 'on_interrupt' INT
|
||||
trap 'on_terminate' TERM
|
||||
# trap 'on_error $? $LINENO' ERR
|
||||
# trap 'on_exit' EXIT
|
||||
# trap 'on_interrupt' INT
|
||||
# trap 'on_terminate' TERM
|
||||
|
||||
if ! declare -f wait_for >/dev/null; then
|
||||
wait_for() {
|
||||
true
|
||||
}
|
||||
fi
|
||||
# if ! declare -f wait_for >/dev/null; then
|
||||
# wait_for() {
|
||||
# true
|
||||
# }
|
||||
# fi
|
||||
|
||||
declare -A MSG_INFO_SHOWN=()
|
||||
SPINNER_PID=""
|
||||
SPINNER_ACTIVE=0
|
||||
SPINNER_MSG=""
|
||||
# declare -A MSG_INFO_SHOWN=()
|
||||
# SPINNER_PID=""
|
||||
# SPINNER_ACTIVE=0
|
||||
# SPINNER_MSG=""
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Loads core utility groups once (colors, formatting, icons, defaults).
|
||||
@@ -47,9 +47,9 @@ load_functions() {
|
||||
# Error & Signal Handling – robust, universal, subshell-safe
|
||||
# ============================================================================
|
||||
|
||||
_stop_spinner_on_error() {
|
||||
[[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null || true
|
||||
}
|
||||
# _stop_spinner_on_error() {
|
||||
# [[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null || true
|
||||
# }
|
||||
|
||||
_tool_error_hint() {
|
||||
local cmd="$1"
|
||||
@@ -89,44 +89,39 @@ _tool_error_hint() {
|
||||
esac
|
||||
}
|
||||
|
||||
on_error() {
|
||||
local code="$?"
|
||||
local line="${BASH_LINENO[0]:-unknown}"
|
||||
local cmd="${BASH_COMMAND:-unknown}"
|
||||
# on_error() {
|
||||
# local code="$?"
|
||||
# local line="${BASH_LINENO[0]:-unknown}"
|
||||
# local cmd="${BASH_COMMAND:-unknown}"
|
||||
|
||||
# Signalcode unterdrücken, falls INT/TERM kommt
|
||||
[[ "$code" == "130" || "$code" == "143" ]] && return
|
||||
# # Signalcode unterdrücken, falls INT/TERM kommt
|
||||
# [[ "$code" == "130" || "$code" == "143" ]] && return
|
||||
|
||||
_stop_spinner_on_error
|
||||
msg_error "Script failed at line $line with exit code $code: $cmd"
|
||||
exit "$code"
|
||||
}
|
||||
# _stop_spinner_on_error
|
||||
# msg_error "Script failed at line $line with exit code $code: $cmd"
|
||||
# exit "$code"
|
||||
# }
|
||||
|
||||
on_exit() {
|
||||
_stop_spinner_on_error
|
||||
[[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited cleanly"
|
||||
}
|
||||
# on_exit() {
|
||||
# _stop_spinner_on_error
|
||||
# [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited cleanly"
|
||||
# }
|
||||
|
||||
on_interrupt() {
|
||||
_stop_spinner_on_error
|
||||
msg_error "Interrupted by user (CTRL+C)"
|
||||
exit 130
|
||||
}
|
||||
# on_interrupt() {
|
||||
# _stop_spinner_on_error
|
||||
# msg_error "Interrupted by user (CTRL+C)"
|
||||
# exit 130
|
||||
# }
|
||||
|
||||
on_terminate() {
|
||||
_stop_spinner_on_error
|
||||
msg_error "Terminated by signal (SIGTERM)"
|
||||
exit 143
|
||||
}
|
||||
# on_terminate() {
|
||||
# _stop_spinner_on_error
|
||||
# msg_error "Terminated by signal (SIGTERM)"
|
||||
# exit 143
|
||||
# }
|
||||
|
||||
catch_errors() {
|
||||
trap 'on_error' ERR
|
||||
trap 'on_exit' EXIT
|
||||
trap 'on_interrupt' INT
|
||||
trap 'on_terminate' TERM
|
||||
|
||||
set -Eeuo pipefail
|
||||
shopt -s inherit_errexit
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -143,6 +138,13 @@ color() {
|
||||
CL=$(echo "\033[m")
|
||||
}
|
||||
|
||||
# Special for spinner and colorized output via printf
|
||||
color_spinner() {
|
||||
CS_YW=$'\033[33m'
|
||||
CS_YWB=$'\033[93m'
|
||||
CS_CL=$'\033[m'
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Defines formatting helpers like tab, bold, and line reset sequences.
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -363,149 +365,215 @@ fatal() {
|
||||
kill -INT $$
|
||||
}
|
||||
|
||||
# Ensure POSIX compatibility across Alpine and Debian/Ubuntu
|
||||
# === Spinner Start ===
|
||||
# Trap cleanup on various signals
|
||||
trap 'cleanup_spinner' EXIT INT TERM HUP
|
||||
# # Ensure POSIX compatibility across Alpine and Debian/Ubuntu
|
||||
# # === Spinner Start ===
|
||||
# # Trap cleanup on various signals
|
||||
# trap 'cleanup_spinner' EXIT INT TERM HUP
|
||||
|
||||
spinner_frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||
# spinner_frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||
|
||||
# === Spinner Start ===
|
||||
start_spinner() {
|
||||
local msg="$1"
|
||||
local spin_i=0
|
||||
local interval=0.1
|
||||
# # === Spinner Start ===
|
||||
# start_spinner() {
|
||||
# local msg="$1"
|
||||
# local spin_i=0
|
||||
# local interval=0.1
|
||||
|
||||
stop_spinner
|
||||
SPINNER_MSG="$msg"
|
||||
SPINNER_ACTIVE=1
|
||||
# stop_spinner
|
||||
# SPINNER_MSG="$msg"
|
||||
# SPINNER_ACTIVE=1
|
||||
|
||||
{
|
||||
while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do
|
||||
if [[ -t 2 ]]; then
|
||||
printf "\r\e[2K%s %b" "${TAB}${spinner_frames[spin_i]}${TAB}" "${YW}${SPINNER_MSG}${CL}" >&2
|
||||
else
|
||||
printf "%s...\n" "$SPINNER_MSG" >&2
|
||||
break
|
||||
fi
|
||||
spin_i=$(((spin_i + 1) % ${#spinner_frames[@]}))
|
||||
sleep "$interval"
|
||||
done
|
||||
} &
|
||||
# {
|
||||
# while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do
|
||||
# if [[ -t 2 ]]; then
|
||||
# printf "\r\e[2K%s %b" "${TAB}${spinner_frames[spin_i]}${TAB}" "${YW}${SPINNER_MSG}${CL}" >&2
|
||||
# else
|
||||
# printf "%s...\n" "$SPINNER_MSG" >&2
|
||||
# break
|
||||
# fi
|
||||
# spin_i=$(((spin_i + 1) % ${#spinner_frames[@]}))
|
||||
# sleep "$interval"
|
||||
# done
|
||||
# } &
|
||||
|
||||
local pid=$!
|
||||
if ps -p "$pid" >/dev/null 2>&1; then
|
||||
SPINNER_PID="$pid"
|
||||
else
|
||||
SPINNER_ACTIVE=0
|
||||
SPINNER_PID=""
|
||||
fi
|
||||
# local pid=$!
|
||||
# if ps -p "$pid" >/dev/null 2>&1; then
|
||||
# SPINNER_PID="$pid"
|
||||
# else
|
||||
# SPINNER_ACTIVE=0
|
||||
# SPINNER_PID=""
|
||||
# fi
|
||||
# }
|
||||
|
||||
# # === Spinner Stop ===
|
||||
# stop_spinner() {
|
||||
# if [[ "$SPINNER_ACTIVE" -eq 1 && -n "$SPINNER_PID" ]]; then
|
||||
# SPINNER_ACTIVE=0
|
||||
|
||||
# if kill -0 "$SPINNER_PID" 2>/dev/null; then
|
||||
# kill "$SPINNER_PID" 2>/dev/null || true
|
||||
# for _ in $(seq 1 10); do
|
||||
# sleep 0.05
|
||||
# kill -0 "$SPINNER_PID" 2>/dev/null || break
|
||||
# done
|
||||
# fi
|
||||
|
||||
# if [[ "$SPINNER_PID" =~ ^[0-9]+$ ]]; then
|
||||
# ps -p "$SPINNER_PID" -o pid= >/dev/null 2>&1 && wait "$SPINNER_PID" 2>/dev/null || true
|
||||
# fi
|
||||
|
||||
# printf "\r\e[2K" >&2
|
||||
# SPINNER_PID=""
|
||||
# fi
|
||||
# }
|
||||
|
||||
# cleanup_spinner() {
|
||||
# stop_spinner
|
||||
# }
|
||||
|
||||
# msg_info() {
|
||||
# local msg="$1"
|
||||
# [[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
|
||||
# MSG_INFO_SHOWN["$msg"]=1
|
||||
|
||||
# stop_spinner
|
||||
# echo -e "OS: ${var_os:-}"
|
||||
# if [[ "${VERBOSE:-no}" != "no" || "${var_os:-}" == "alpine" || ! -t 2 ]]; then
|
||||
# printf "\r\e[2K%s %b\n" "$HOURGLASS" "${YW}${msg}${CL}" >&2
|
||||
# else
|
||||
# start_spinner "$msg"
|
||||
# fi
|
||||
# }
|
||||
|
||||
spinner() {
|
||||
local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
|
||||
local i=0
|
||||
while true; do
|
||||
local index=$((i++ % ${#chars[@]}))
|
||||
printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${SPINNER_MSG:-}${CS_CL}"
|
||||
sleep 0.1
|
||||
done
|
||||
}
|
||||
|
||||
# === Spinner Stop ===
|
||||
stop_spinner() {
|
||||
if [[ "$SPINNER_ACTIVE" -eq 1 && -n "$SPINNER_PID" ]]; then
|
||||
SPINNER_ACTIVE=0
|
||||
local pid="${SPINNER_PID:-}"
|
||||
[[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(</tmp/.spinner.pid)
|
||||
|
||||
if kill -0 "$SPINNER_PID" 2>/dev/null; then
|
||||
kill "$SPINNER_PID" 2>/dev/null || true
|
||||
for _ in $(seq 1 10); do
|
||||
sleep 0.05
|
||||
kill -0 "$SPINNER_PID" 2>/dev/null || break
|
||||
done
|
||||
if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then
|
||||
if kill "$pid" 2>/dev/null; then
|
||||
sleep 0.05
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ "$SPINNER_PID" =~ ^[0-9]+$ ]]; then
|
||||
ps -p "$SPINNER_PID" -o pid= >/dev/null 2>&1 && wait "$SPINNER_PID" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
printf "\r\e[2K" >&2
|
||||
SPINNER_PID=""
|
||||
rm -f /tmp/.spinner.pid
|
||||
unset SPINNER_PID
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_spinner() {
|
||||
stop_spinner
|
||||
stty sane 2>/dev/null
|
||||
#printf "\r\033[K\e[?25h\n"
|
||||
}
|
||||
|
||||
msg_info() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
|
||||
MSG_INFO_SHOWN["$msg"]=1
|
||||
|
||||
stop_spinner
|
||||
|
||||
if [[ "${VERBOSE:-no}" != "no" || "${var_os:-}" == "alpine" || ! -t 2 ]]; then
|
||||
printf "\r\e[2K%s %b\n" "$HOURGLASS" "${YW}${msg}${CL}" >&2
|
||||
else
|
||||
start_spinner "$msg"
|
||||
fi
|
||||
SPINNER_MSG="$msg"
|
||||
color_spinner
|
||||
spinner &
|
||||
SPINNER_PID=$!
|
||||
echo "$SPINNER_PID" >/tmp/.spinner.pid
|
||||
disown "$SPINNER_PID" 2>/dev/null || true
|
||||
}
|
||||
|
||||
msg_ok() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner
|
||||
printf "\r\e[2K%s %b\n" "$CM" "${GN}${msg}${CL}" >&2
|
||||
if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then
|
||||
unset MSG_INFO_SHOWN["$msg"]
|
||||
fi
|
||||
local msg="$1"
|
||||
echo -e "${BFR:-} ${CM:-✔️} ${GN}${msg}${CL}"
|
||||
}
|
||||
|
||||
msg_error() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner
|
||||
printf "\r\e[2K%s %b\n" "$CROSS" "${RD}${msg}${CL}" >&2
|
||||
local msg="$1"
|
||||
echo -e "${BFR:-} ${CROSS:-✖️} ${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
msg_warn() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner
|
||||
printf "\r\e[2K%s %b\n" "$INFO" "${YWB}${msg}${CL}" >&2
|
||||
if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then
|
||||
unset MSG_INFO_SHOWN["$msg"]
|
||||
fi
|
||||
local msg="$1"
|
||||
echo -e "${BFR:-} ${INFO:-ℹ️} ${YWB}${msg}${CL}"
|
||||
}
|
||||
|
||||
msg_custom() {
|
||||
local symbol="${1:-"[*]"}"
|
||||
local color="${2:-"\e[36m"}" # Default: Cyan
|
||||
local color="${2:-"\e[36m"}"
|
||||
local msg="${3:-}"
|
||||
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner 2>/dev/null || true
|
||||
printf "\r\e[2K%s %b\n" "$symbol" "${color}${msg}${CL:-\e[0m}" >&2
|
||||
stop_spinner
|
||||
echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}"
|
||||
printf "\r\033[K\e[?25h\n"
|
||||
}
|
||||
|
||||
msg_progress() {
|
||||
local current="$1"
|
||||
local total="$2"
|
||||
local label="$3"
|
||||
local width=40
|
||||
local filled percent bar empty
|
||||
local fill_char="#"
|
||||
local empty_char="-"
|
||||
# msg_ok() {
|
||||
# local msg="$1"
|
||||
# [[ -z "$msg" ]] && return
|
||||
# stop_spinner
|
||||
# printf "\r\e[2K%s %b\n" "$CM" "${GN}${msg}${CL}" >&2
|
||||
# if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then
|
||||
# unset MSG_INFO_SHOWN["$msg"]
|
||||
# fi
|
||||
# }
|
||||
|
||||
if ! [[ "$current" =~ ^[0-9]+$ ]] || ! [[ "$total" =~ ^[0-9]+$ ]] || [[ "$total" -eq 0 ]]; then
|
||||
printf "\r\e[2K%s %b\n" "$CROSS" "${RD}Invalid progress input${CL}" >&2
|
||||
return
|
||||
fi
|
||||
# msg_error() {
|
||||
# local msg="$1"
|
||||
# [[ -z "$msg" ]] && return
|
||||
# stop_spinner
|
||||
# printf "\r\e[2K%s %b\n" "$CROSS" "${RD}${msg}${CL}" >&2
|
||||
# }
|
||||
|
||||
percent=$(((current * 100) / total))
|
||||
filled=$(((current * width) / total))
|
||||
empty=$((width - filled))
|
||||
# msg_warn() {
|
||||
# local msg="$1"
|
||||
# [[ -z "$msg" ]] && return
|
||||
# stop_spinner
|
||||
# printf "\r\e[2K%s %b\n" "$INFO" "${YWB}${msg}${CL}" >&2
|
||||
# if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then
|
||||
# unset MSG_INFO_SHOWN["$msg"]
|
||||
# fi
|
||||
# }
|
||||
|
||||
bar=$(printf "%${filled}s" | tr ' ' "$fill_char")
|
||||
bar+=$(printf "%${empty}s" | tr ' ' "$empty_char")
|
||||
# msg_custom() {
|
||||
# local symbol="${1:-"[*]"}"
|
||||
# local color="${2:-"\e[36m"}" # Default: Cyan
|
||||
# local msg="${3:-}"
|
||||
|
||||
printf "\r\e[2K%s [%s] %3d%% %s" "${TAB}" "$bar" "$percent" "$label" >&2
|
||||
# [[ -z "$msg" ]] && return
|
||||
# stop_spinner 2>/dev/null || true
|
||||
# printf "\r\e[2K%s %b\n" "$symbol" "${color}${msg}${CL:-\e[0m}" >&2
|
||||
# }
|
||||
|
||||
if [[ "$current" -eq "$total" ]]; then
|
||||
printf "\n" >&2
|
||||
fi
|
||||
}
|
||||
# msg_progress() {
|
||||
# local current="$1"
|
||||
# local total="$2"
|
||||
# local label="$3"
|
||||
# local width=40
|
||||
# local filled percent bar empty
|
||||
# local fill_char="#"
|
||||
# local empty_char="-"
|
||||
|
||||
# if ! [[ "$current" =~ ^[0-9]+$ ]] || ! [[ "$total" =~ ^[0-9]+$ ]] || [[ "$total" -eq 0 ]]; then
|
||||
# printf "\r\e[2K%s %b\n" "$CROSS" "${RD}Invalid progress input${CL}" >&2
|
||||
# return
|
||||
# fi
|
||||
|
||||
# percent=$(((current * 100) / total))
|
||||
# filled=$(((current * width) / total))
|
||||
# empty=$((width - filled))
|
||||
|
||||
# bar=$(printf "%${filled}s" | tr ' ' "$fill_char")
|
||||
# bar+=$(printf "%${empty}s" | tr ' ' "$empty_char")
|
||||
|
||||
# printf "\r\e[2K%s [%s] %3d%% %s" "${TAB}" "$bar" "$percent" "$label" >&2
|
||||
|
||||
# if [[ "$current" -eq "$total" ]]; then
|
||||
# printf "\n" >&2
|
||||
# fi
|
||||
# }
|
||||
|
||||
run_container_safe() {
|
||||
local ct="$1"
|
||||
@@ -555,3 +623,5 @@ check_or_create_swap() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
trap 'stop_spinner' EXIT INT TERM
|
||||
|
||||
@@ -27,7 +27,7 @@ trap on_terminate TERM
|
||||
|
||||
function on_exit() {
|
||||
local exit_code="$?"
|
||||
[[ -n "${lockfile:-}" ]]
|
||||
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
@@ -50,18 +50,30 @@ function on_terminate() {
|
||||
exit 143
|
||||
}
|
||||
|
||||
function check_storage_support() {
|
||||
local CONTENT="$1"
|
||||
local -a VALID_STORAGES=()
|
||||
|
||||
while IFS= read -r line; do
|
||||
local STORAGE=$(awk '{print $1}' <<<"$line")
|
||||
[[ "$STORAGE" == "storage" || -z "$STORAGE" ]] && continue
|
||||
VALID_STORAGES+=("$STORAGE")
|
||||
done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1')
|
||||
|
||||
[[ ${#VALID_STORAGES[@]} -gt 0 ]]
|
||||
}
|
||||
|
||||
# This checks for the presence of valid Container Storage and Template Storage locations
|
||||
msg_info "Validating Storage"
|
||||
VALIDCT=$(pvesm status -content rootdir | awk 'NR>1')
|
||||
if [ -z "$VALIDCT" ]; then
|
||||
msg_error "Unable to detect a valid Container Storage location."
|
||||
if ! check_storage_support "rootdir"; then
|
||||
msg_error "No valid storage found for 'rootdir' (Container)."
|
||||
exit 1
|
||||
fi
|
||||
VALIDTMP=$(pvesm status -content vztmpl | awk 'NR>1')
|
||||
if [ -z "$VALIDTMP" ]; then
|
||||
msg_error "Unable to detect a valid Template Storage location."
|
||||
if ! check_storage_support "vztmpl"; then
|
||||
msg_error "No valid storage found for 'vztmpl' (Template)."
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "Storage types rootdir and vztmpl are supported."
|
||||
|
||||
# This function is used to select the storage class and determine the corresponding storage content type and label.
|
||||
function select_storage() {
|
||||
@@ -107,12 +119,19 @@ function select_storage() {
|
||||
}
|
||||
|
||||
local -a MENU
|
||||
while read -r line; do
|
||||
local TAG=$(echo $line | awk '{print $1}')
|
||||
local TYPE=$(echo $line | awk '{printf "%-10s", $2}')
|
||||
local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
MENU+=("$TAG" "Type: $TYPE Free: $FREE " "OFF")
|
||||
done < <(pvesm status -content $CONTENT | awk 'NR>1')
|
||||
local -A STORAGE_MAP=()
|
||||
local COL_WIDTH=0
|
||||
|
||||
while read -r TAG TYPE _ TOTAL USED FREE _; do
|
||||
[[ -n "$TAG" && -n "$TYPE" ]] || continue
|
||||
local DISPLAY="${TAG} (${TYPE})"
|
||||
local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED")
|
||||
local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE")
|
||||
local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B"
|
||||
STORAGE_MAP["$DISPLAY"]="$TAG" # Map DISPLAY to actual TAG
|
||||
MENU+=("$DISPLAY" "$INFO" "OFF")
|
||||
((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY}
|
||||
done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
|
||||
|
||||
if [ ${#MENU[@]} -eq 0 ]; then
|
||||
msg_error "No storage found for content type '$CONTENT'."
|
||||
@@ -120,18 +139,32 @@ function select_storage() {
|
||||
fi
|
||||
|
||||
if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
|
||||
printf "%s" "${MENU[0]}"
|
||||
echo "${STORAGE_MAP[${MENU[0]}]}"
|
||||
return
|
||||
fi
|
||||
|
||||
local STORAGE
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
|
||||
16 70 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || {
|
||||
msg_error "Storage selection cancelled by user."
|
||||
exit 202
|
||||
}
|
||||
printf "%s" "$STORAGE"
|
||||
local WIDTH=$((COL_WIDTH + 42))
|
||||
local DISPLAY_SELECTED=""
|
||||
while true; do
|
||||
DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
|
||||
16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || {
|
||||
msg_error "Storage selection cancelled by user."
|
||||
exit 202
|
||||
}
|
||||
|
||||
# Validierung gegen STORAGE_MAP
|
||||
if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Invalid Selection" \
|
||||
--msgbox "No valid storage selected. Please choose a storage pool to continue." 9 60
|
||||
continue
|
||||
fi
|
||||
|
||||
break
|
||||
done
|
||||
|
||||
echo "${STORAGE_MAP["$DISPLAY_SELECTED"]}"
|
||||
|
||||
}
|
||||
|
||||
# Test if required variables are set
|
||||
@@ -172,10 +205,29 @@ if [[ -f "$DEFAULT_FILE" ]]; then
|
||||
msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
|
||||
fi
|
||||
else
|
||||
TEMPLATE_STORAGE=$(select_storage template)
|
||||
# TEMPLATE STORAGE SELECTION
|
||||
# Template Storage
|
||||
if ! TEMPLATE_STORAGE=$(select_storage template); then
|
||||
[[ $? -eq 202 ]] && {
|
||||
msg_error "Template Storage selection cancelled by user. Exiting."
|
||||
kill -INT $$
|
||||
}
|
||||
msg_error "Unexpected error during template storage selection."
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage."
|
||||
CONTAINER_STORAGE=$(select_storage container)
|
||||
|
||||
# Container Storage
|
||||
if ! CONTAINER_STORAGE=$(select_storage container); then
|
||||
[[ $? -eq 202 ]] && {
|
||||
msg_error "Container Storage selection cancelled by user. Exiting."
|
||||
kill -INT $$
|
||||
}
|
||||
msg_error "Unexpected error during container storage selection."
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
|
||||
|
||||
fi
|
||||
|
||||
# Check free space on selected container storage
|
||||
|
||||
@@ -31,7 +31,7 @@ catch_errors() {
|
||||
|
||||
# This function handles errors
|
||||
error_handler() {
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func)
|
||||
printf "\e[?25h"
|
||||
local exit_code="$?"
|
||||
local line_number="$1"
|
||||
@@ -65,7 +65,8 @@ setting_up_container() {
|
||||
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
|
||||
systemctl disable -q --now systemd-networkd-wait-online.service
|
||||
msg_ok "Set up Container OS"
|
||||
msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
|
||||
#msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
|
||||
msg_ok "Network Connected: ${BL}$(hostname -I)"
|
||||
}
|
||||
|
||||
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
|
||||
@@ -104,24 +105,24 @@ network_check() {
|
||||
fi
|
||||
|
||||
# DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
|
||||
GITHUB_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
|
||||
GITHUB_STATUS="GitHub DNS:"
|
||||
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
|
||||
GIT_STATUS="Git DNS:"
|
||||
DNS_FAILED=false
|
||||
|
||||
for HOST in "${GITHUB_HOSTS[@]}"; do
|
||||
for HOST in "${GIT_HOSTS[@]}"; do
|
||||
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
|
||||
if [[ -z "$RESOLVEDIP" ]]; then
|
||||
GITHUB_STATUS+="$HOST:($DNSFAIL)"
|
||||
GIT_STATUS+="$HOST:($DNSFAIL)"
|
||||
DNS_FAILED=true
|
||||
else
|
||||
GITHUB_STATUS+=" $HOST:($DNSOK)"
|
||||
GIT_STATUS+=" $HOST:($DNSOK)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$DNS_FAILED" == true ]]; then
|
||||
fatal "$GITHUB_STATUS"
|
||||
fatal "$GIT_STATUS"
|
||||
else
|
||||
msg_ok "$GITHUB_STATUS"
|
||||
msg_ok "$GIT_STATUS"
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
167
misc/tools.func
167
misc/tools.func
@@ -238,7 +238,6 @@ setup_mariadb() {
|
||||
msg_info "Setting up MariaDB $MARIADB_VERSION"
|
||||
# grab dynamic latest LTS version
|
||||
if [[ "$MARIADB_VERSION" == "latest" ]]; then
|
||||
$STD msg_info "Resolving latest GA MariaDB version"
|
||||
MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ |
|
||||
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' |
|
||||
grep -vE 'rc/|rolling/' |
|
||||
@@ -249,7 +248,6 @@ setup_mariadb() {
|
||||
msg_error "Could not determine latest GA MariaDB version"
|
||||
return 1
|
||||
fi
|
||||
$STD msg_ok "Latest GA MariaDB version is $MARIADB_VERSION"
|
||||
fi
|
||||
|
||||
local CURRENT_VERSION=""
|
||||
@@ -274,7 +272,6 @@ setup_mariadb() {
|
||||
$STD msg_info "Setup MariaDB $MARIADB_VERSION"
|
||||
fi
|
||||
|
||||
$STD msg_info "Setting up MariaDB Repository"
|
||||
curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" |
|
||||
gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg
|
||||
|
||||
@@ -824,19 +821,30 @@ function fetch_and_deploy_gh_release() {
|
||||
elif [[ "$mode" == "binary" ]]; then
|
||||
local arch
|
||||
arch=$(dpkg --print-architecture 2>/dev/null || uname -m)
|
||||
[[ "$arch" == "x86_64" ]] && arch="x86_64"
|
||||
[[ "$arch" == "x86_64" ]] && arch="amd64"
|
||||
[[ "$arch" == "aarch64" ]] && arch="arm64"
|
||||
|
||||
local assets url_match=""
|
||||
assets=$(echo "$json" | jq -r '.assets[].browser_download_url')
|
||||
|
||||
for u in $assets; do
|
||||
if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then
|
||||
url_match="$u"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# If explicit filename pattern is provided (param $6), match that first
|
||||
if [[ -n "$6" ]]; then
|
||||
for u in $assets; do
|
||||
[[ "$u" =~ $6 || "$u" == *"$6" ]] && url_match="$u" && break
|
||||
done
|
||||
fi
|
||||
|
||||
# If no match via explicit pattern, fall back to architecture heuristic
|
||||
if [[ -z "$url_match" ]]; then
|
||||
for u in $assets; do
|
||||
if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then
|
||||
url_match="$u"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Fallback: any .deb file
|
||||
if [[ -z "$url_match" ]]; then
|
||||
for u in $assets; do
|
||||
[[ "$u" =~ \.deb$ ]] && url_match="$u" && break
|
||||
@@ -1611,3 +1619,142 @@ function setup_imagemagick() {
|
||||
ensure_usr_local_bin_persist
|
||||
msg_ok "Setup ImageMagick $VERSION"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Installs FFmpeg from source or prebuilt binary (Debian/Ubuntu only).
|
||||
#
|
||||
# Description:
|
||||
# - Downloads and builds FFmpeg from GitHub (https://github.com/FFmpeg/FFmpeg)
|
||||
# - Supports specific version override via FFMPEG_VERSION (e.g. n7.1.1)
|
||||
# - Supports build profile via FFMPEG_TYPE:
|
||||
# - minimal : x264, vpx, mp3 only
|
||||
# - medium : adds subtitles, fonts, opus, vorbis
|
||||
# - full : adds dav1d, svt-av1, zlib, numa
|
||||
# - binary : downloads static build (johnvansickle.com)
|
||||
# - Defaults to latest stable version and full feature set
|
||||
#
|
||||
# Notes:
|
||||
# - Requires: curl, jq, build-essential, and matching codec libraries
|
||||
# - Result is installed to /usr/local/bin/ffmpeg
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function setup_ffmpeg() {
|
||||
local TMP_DIR
|
||||
TMP_DIR=$(mktemp -d)
|
||||
local GITHUB_REPO="FFmpeg/FFmpeg"
|
||||
local VERSION="${FFMPEG_VERSION:-}"
|
||||
local TYPE="${FFMPEG_TYPE:-full}"
|
||||
local BIN_PATH="/usr/local/bin/ffmpeg"
|
||||
|
||||
# Binary fallback mode
|
||||
if [[ "$TYPE" == "binary" ]]; then
|
||||
msg_info "Installing FFmpeg (static binary)"
|
||||
curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz"
|
||||
tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR"
|
||||
local EXTRACTED_DIR
|
||||
EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*")
|
||||
cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH"
|
||||
cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe
|
||||
chmod +x "$BIN_PATH" /usr/local/bin/ffprobe
|
||||
rm -rf "$TMP_DIR"
|
||||
msg_ok "Installed FFmpeg binary ($($BIN_PATH -version | head -n1))"
|
||||
return
|
||||
fi
|
||||
|
||||
# Auto-detect latest stable version if none specified
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
msg_info "Fetching latest stable FFmpeg tag"
|
||||
VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" |
|
||||
jq -r '.[].name' |
|
||||
grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | # stable only
|
||||
sort -V | tail -n1)
|
||||
fi
|
||||
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
msg_error "Could not determine FFmpeg version"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
msg_info "Installing FFmpeg ${VERSION} ($TYPE)"
|
||||
|
||||
# Dependency selection
|
||||
local DEPS=(build-essential yasm nasm pkg-config)
|
||||
case "$TYPE" in
|
||||
minimal)
|
||||
DEPS+=(libx264-dev libvpx-dev libmp3lame-dev)
|
||||
;;
|
||||
medium)
|
||||
DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev)
|
||||
;;
|
||||
full)
|
||||
DEPS+=(
|
||||
libx264-dev libx265-dev libvpx-dev libmp3lame-dev
|
||||
libfreetype6-dev libass-dev libopus-dev libvorbis-dev
|
||||
libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev
|
||||
)
|
||||
;;
|
||||
*)
|
||||
msg_error "Invalid FFMPEG_TYPE: $TYPE"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
$STD apt-get update
|
||||
$STD apt-get install -y "${DEPS[@]}"
|
||||
|
||||
curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz"
|
||||
tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR"
|
||||
cd "$TMP_DIR/FFmpeg-"* || {
|
||||
msg_error "Source extraction failed"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
}
|
||||
|
||||
local args=(
|
||||
--enable-gpl
|
||||
--enable-shared
|
||||
--enable-nonfree
|
||||
--disable-static
|
||||
--enable-libx264
|
||||
--enable-libvpx
|
||||
--enable-libmp3lame
|
||||
)
|
||||
|
||||
if [[ "$TYPE" != "minimal" ]]; then
|
||||
args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis)
|
||||
fi
|
||||
|
||||
if [[ "$TYPE" == "full" ]]; then
|
||||
args+=(--enable-libx265 --enable-libdav1d --enable-zlib)
|
||||
fi
|
||||
|
||||
if [[ ${#args[@]} -eq 0 ]]; then
|
||||
msg_error "FFmpeg configure args array is empty – aborting."
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
./configure "${args[@]}" >"$TMP_DIR/configure.log" 2>&1 || {
|
||||
msg_error "FFmpeg ./configure failed (see $TMP_DIR/configure.log)"
|
||||
cat "$TMP_DIR/configure.log" | tail -n 20
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
}
|
||||
|
||||
$STD make -j"$(nproc)"
|
||||
$STD make install
|
||||
|
||||
if ! command -v ffmpeg &>/dev/null; then
|
||||
msg_error "FFmpeg installation failed"
|
||||
rm -rf "$TMP_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local FINAL_VERSION
|
||||
FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}')
|
||||
rm -rf "$TMP_DIR"
|
||||
ensure_usr_local_bin_persist
|
||||
msg_ok "Setup FFmpeg $FINAL_VERSION"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user