mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-05-08 01:02:21 -04:00
Compare commits
17 Commits
fix/rustde
...
fix/termix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2c334f5c7 | ||
|
|
be2e3a4a3a | ||
|
|
3c02868add | ||
|
|
07cd4c0c3e | ||
|
|
f11edd21c9 | ||
|
|
9fc822d936 | ||
|
|
bf1c32ace8 | ||
|
|
611021ae8c | ||
|
|
12eb19ae4c | ||
|
|
c8dc0cffe0 | ||
|
|
95c7628362 | ||
|
|
50b3c3ae7f | ||
|
|
752fff3c8f | ||
|
|
129e221664 | ||
|
|
21c064464a | ||
|
|
6317e7a867 | ||
|
|
739e0aa41e |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -458,6 +458,47 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-05-07
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- vm: update disk image URL for Ubuntu 25.04 [@MickLesk](https://github.com/MickLesk) ([#14290](https://github.com/community-scripts/ProxmoxVE/pull/14290))
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- pangolin: bump version to 1.18.3 [@MickLesk](https://github.com/MickLesk) ([#14297](https://github.com/community-scripts/ProxmoxVE/pull/14297))
|
||||
|
||||
### 🗑️ Deleted Scripts
|
||||
|
||||
- Remove: LiteLLM [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#14294](https://github.com/community-scripts/ProxmoxVE/pull/14294))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- update-apps: some improvements [@MickLesk](https://github.com/MickLesk) ([#14275](https://github.com/community-scripts/ProxmoxVE/pull/14275))
|
||||
|
||||
## 2026-05-06
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Hoodik ([#14279](https://github.com/community-scripts/ProxmoxVE/pull/14279))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Pelican-Panel: create backup subdirectory before copying storage [@MickLesk](https://github.com/MickLesk) ([#14274](https://github.com/community-scripts/ProxmoxVE/pull/14274))
|
||||
- Rustdeskserver: remove redundant else with undefined RELEASE var [@MickLesk](https://github.com/MickLesk) ([#14272](https://github.com/community-scripts/ProxmoxVE/pull/14272))
|
||||
|
||||
### 🧰 Tools
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- AdguardHome-Sync replace ifconfig with hostname -I for IP detection [@MickLesk](https://github.com/MickLesk) ([#14273](https://github.com/community-scripts/ProxmoxVE/pull/14273))
|
||||
|
||||
## 2026-05-05
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
6
ct/headers/hoodik
Normal file
6
ct/headers/hoodik
Normal file
@@ -0,0 +1,6 @@
|
||||
__ __ ___ __
|
||||
/ / / /___ ____ ____/ (_) /__
|
||||
/ /_/ / __ \/ __ \/ __ / / //_/
|
||||
/ __ / /_/ / /_/ / /_/ / / ,<
|
||||
/_/ /_/\____/\____/\__,_/_/_/|_|
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
__ _ __ __ __ __ ___
|
||||
/ / (_) /____ / / / / / |/ /
|
||||
/ / / / __/ _ \/ / / / / /|_/ /
|
||||
/ /___/ / /_/ __/ /___/ /___/ / / /
|
||||
/_____/_/\__/\___/_____/_____/_/ /_/
|
||||
|
||||
64
ct/hoodik.sh
Normal file
64
ct/hoodik.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/hudikhq/hoodik
|
||||
|
||||
APP="Hoodik"
|
||||
var_tags="${var_tags:-cloud;storage}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-5}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -f /opt/hoodik/hoodik ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "hoodik" "hudikhq/hoodik"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop hoodik
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up Configuration"
|
||||
cp /opt/hoodik/.env /opt/hoodik.env.bak
|
||||
msg_ok "Backed up Configuration"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz"
|
||||
|
||||
msg_info "Restoring Configuration"
|
||||
cp /opt/hoodik.env.bak /opt/hoodik/.env
|
||||
rm -f /opt/hoodik.env.bak
|
||||
msg_ok "Restored Configuration"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start hoodik
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5443/auth/register${CL}"
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: stout01
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/BerriAI/litellm
|
||||
|
||||
APP="LiteLLM"
|
||||
var_tags="${var_tags:-ai;interface}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -f /etc/systemd/system/litellm.service ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop litellm
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
VENV_PATH="/opt/litellm/.venv"
|
||||
PYTHON_VERSION="3.13" USE_UVX="YES" setup_uv
|
||||
|
||||
msg_info "Updating LiteLLM"
|
||||
$STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma
|
||||
$STD "$VENV_PATH/bin/prisma" generate
|
||||
msg_ok "LiteLLM updated"
|
||||
|
||||
msg_info "Updating DB Schema"
|
||||
$STD /opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup
|
||||
msg_ok "DB Schema Updated"
|
||||
|
||||
msg_info "Updating Service"
|
||||
sed -i 's|ExecStart=uv --directory=/opt/litellm run litellm|ExecStart=/opt/litellm/.venv/bin/litellm|' /etc/systemd/system/litellm.service
|
||||
systemctl daemon-reload
|
||||
msg_ok "Updated Service"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start litellm
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}"
|
||||
@@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
# Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin
|
||||
|
||||
APP="Pangolin"
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.2}"
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.3}"
|
||||
var_tags="${var_tags:-proxy}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
|
||||
@@ -46,6 +46,7 @@ function update_script() {
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
cp -a /opt/pelican-panel/.env /opt/backup
|
||||
mkdir -p /opt/backup/storage/app/
|
||||
cp -a /opt/pelican-panel/storage/app/public /opt/backup/storage/app/
|
||||
|
||||
SQLITE_INSTALL=$(ls /opt/pelican-panel/database/*.sqlite 1>/dev/null 2>&1 && echo "true" || echo "false")
|
||||
|
||||
58
install/hoodik-install.sh
Normal file
58
install/hoodik-install.sh
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/hudikhq/hoodik
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
fetch_and_deploy_gh_release "hoodik" "hudikhq/hoodik" "prebuild" "latest" "/opt/hoodik" "*x86_64.tar.gz"
|
||||
|
||||
msg_info "Configuring Hoodik"
|
||||
mkdir -p /opt/hoodik_data
|
||||
JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)
|
||||
cat <<EOF >/opt/hoodik/.env
|
||||
DATA_DIR=/opt/hoodik_data
|
||||
HTTP_PORT=5443
|
||||
HTTP_ADDRESS=0.0.0.0
|
||||
JWT_SECRET=${JWT_SECRET}
|
||||
APP_URL=http://${LOCAL_IP}:5443
|
||||
SSL_DISABLED=true
|
||||
COOKIE_SECURE=false
|
||||
COOKIE_HTTP_ONLY=false
|
||||
MAILER_TYPE=none
|
||||
RUST_LOG=hoodik=info,error=info
|
||||
EOF
|
||||
msg_ok "Configured Hoodik"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/hoodik.service
|
||||
[Unit]
|
||||
Description=Hoodik - Encrypted File Storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/hoodik_data
|
||||
EnvironmentFile=/opt/hoodik/.env
|
||||
ExecStart=/opt/hoodik/hoodik
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now hoodik
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: stout01
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/BerriAI/litellm
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
build-essential \
|
||||
python3-dev
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PG_VERSION="17" setup_postgresql
|
||||
PG_DB_NAME="litellm_db" PG_DB_USER="litellm" setup_postgresql_db
|
||||
PYTHON_VERSION="3.13" USE_UVX="YES" setup_uv
|
||||
|
||||
msg_info "Setting up Virtual Environment"
|
||||
mkdir -p /opt/litellm
|
||||
cd /opt/litellm
|
||||
$STD uv venv --clear /opt/litellm/.venv
|
||||
$STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade
|
||||
$STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip
|
||||
$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma
|
||||
$STD /opt/litellm/.venv/bin/prisma generate
|
||||
msg_ok "Installed LiteLLM"
|
||||
|
||||
msg_info "Configuring LiteLLM"
|
||||
mkdir -p /opt
|
||||
cat <<EOF >/opt/litellm/litellm.yaml
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
database_url: postgresql://$PG_DB_USER:$PG_DB_PASS@127.0.0.1:5432/$PG_DB_NAME
|
||||
store_model_in_db: true
|
||||
EOF
|
||||
$STD /opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup
|
||||
msg_ok "Configured LiteLLM"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/litellm.service
|
||||
[Unit]
|
||||
Description=LiteLLM
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now litellm
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -22,7 +22,7 @@ $STD apt install -y \
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.2}"
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.18.3}"
|
||||
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
|
||||
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64"
|
||||
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_amd64.tar.gz"
|
||||
|
||||
@@ -100,6 +100,7 @@ sed -i 's|/app/html|/opt/termix/html|g' /etc/nginx/nginx.conf
|
||||
sed -i 's|/app/nginx|/opt/termix/nginx|g' /etc/nginx/nginx.conf
|
||||
sed -i 's|listen ${PORT};|listen 80;|g' /etc/nginx/nginx.conf
|
||||
|
||||
mkdir -p /tmp/nginx
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
@@ -3230,6 +3230,10 @@ check_container_resources() {
|
||||
if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then
|
||||
msg_warn "Under-provisioned: Required ${var_cpu} CPU/${var_ram}MB RAM, Current ${current_cpu} CPU/${current_ram}MB RAM"
|
||||
echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n"
|
||||
if is_unattended; then
|
||||
msg_error "Aborted: under-provisioned LXC in unattended mode (${current_cpu} CPU/${current_ram}MB RAM < ${var_cpu} CPU/${var_ram}MB RAM)"
|
||||
exit 113
|
||||
fi
|
||||
echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? <yes/No> "
|
||||
read -r prompt </dev/tty
|
||||
if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then
|
||||
@@ -3253,6 +3257,10 @@ check_container_storage() {
|
||||
usage=$((100 * used_size / total_size))
|
||||
if ((usage > 80)); then
|
||||
msg_warn "Storage is dangerously low (${usage}% used on /boot)"
|
||||
if is_unattended; then
|
||||
msg_error "Aborted: storage too low in unattended mode (${usage}% used on /boot)"
|
||||
exit 114
|
||||
fi
|
||||
echo -ne "Continue anyway? <y/N> "
|
||||
read -r prompt </dev/tty
|
||||
if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then
|
||||
|
||||
@@ -868,6 +868,12 @@ get_header() {
|
||||
# - Returns silently if header not available
|
||||
# ------------------------------------------------------------------------------
|
||||
header_info() {
|
||||
# Guard against printing the header twice in the same session (e.g. when
|
||||
# the ct script calls header_info at global scope AND again inside
|
||||
# update_script()).
|
||||
[[ "${_HEADER_SHOWN:-0}" == "1" ]] && return 0
|
||||
_HEADER_SHOWN=1
|
||||
|
||||
local app_name=$(echo "${APP,,}" | tr -d ' ')
|
||||
local header_content
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ EOF
|
||||
# HELPER FUNCTIONS
|
||||
# ==============================================================================
|
||||
get_ip() {
|
||||
ifconfig | grep -v '127.0.0.1' | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -m1 -Eo '([0-9]*\.){3}[0-9]*' || echo "127.0.0.1"
|
||||
hostname -I 2>/dev/null | awk '{print $1}' || ip -4 addr show scope global 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 || echo "127.0.0.1"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
@@ -42,6 +42,17 @@ var_skip_confirm="${var_skip_confirm:-no}"
|
||||
# Options: "yes" | "no" | "" (empty = interactive prompt)
|
||||
var_auto_reboot="${var_auto_reboot:-}"
|
||||
|
||||
# var_continue_on_error: Continue updating remaining containers if one update fails
|
||||
# Options: "yes" | "no" (default: no = stop on first error)
|
||||
# Note: containers with backups always attempt restore on failure regardless of this setting
|
||||
var_continue_on_error="${var_continue_on_error:-no}"
|
||||
|
||||
# var_dry_run: Check for available updates without applying them
|
||||
# Options: "yes" | "no" (default: no)
|
||||
# Output: lists each container with current vs. latest version
|
||||
# Note: requires the container to be running; does not modify any container
|
||||
var_dry_run="${var_dry_run:-no}"
|
||||
|
||||
# var_tags: Optionally override the tags used for auto-detection
|
||||
# Options: "community-script|proxmox-helper-scripts" (default)
|
||||
var_tags="${var_tags:-community-script|proxmox-helper-scripts}"
|
||||
@@ -59,6 +70,8 @@ function export_config_json() {
|
||||
"var_unattended": "${var_unattended}",
|
||||
"var_skip_confirm": "${var_skip_confirm}",
|
||||
"var_auto_reboot": "${var_auto_reboot}",
|
||||
"var_continue_on_error": "${var_continue_on_error}",
|
||||
"var_dry_run": "${var_dry_run}",
|
||||
"var_tags": "${var_tags}"
|
||||
}
|
||||
EOF
|
||||
@@ -78,10 +91,12 @@ Environment Variables:
|
||||
var_backup Enable backup before update (yes/no)
|
||||
var_backup_storage Storage location for backups
|
||||
var_container Container selection (all/all_running/all_stopped/101,102,...)
|
||||
var_unattended Run updates unattended (yes/no)
|
||||
var_skip_confirm Skip initial confirmation (yes/no)
|
||||
var_auto_reboot Auto-reboot containers if required (yes/no)
|
||||
var_tags Optionally override auto-detection tags ("prod|smb|community-script")
|
||||
var_unattended Run updates unattended (yes/no)
|
||||
var_skip_confirm Skip initial confirmation (yes/no)
|
||||
var_auto_reboot Auto-reboot containers if required (yes/no)
|
||||
var_continue_on_error Continue to next container on update failure (yes/no)
|
||||
var_dry_run Check for updates without applying them (yes/no)
|
||||
var_tags Optionally override auto-detection tags ("prod|smb|community-script")
|
||||
|
||||
Examples:
|
||||
# Run interactively
|
||||
@@ -93,6 +108,12 @@ Examples:
|
||||
# Update specific containers without backup
|
||||
var_backup=no var_container=101,102,105 var_unattended=yes var_skip_confirm=yes $(basename "$0")
|
||||
|
||||
# Unattended cron-style: skip confirm, continue on error, no backup
|
||||
var_backup=no var_container=all_running var_unattended=yes var_skip_confirm=yes var_continue_on_error=yes $(basename "$0")
|
||||
|
||||
# Dry-run: show available updates for all running containers without applying
|
||||
var_container=all_running var_skip_confirm=yes var_dry_run=yes $(basename "$0")
|
||||
|
||||
# Export current configuration
|
||||
$(basename "$0") --export-config
|
||||
EOF
|
||||
@@ -131,6 +152,62 @@ function detect_service() {
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
function dry_run_container() {
|
||||
local container="$1"
|
||||
local service="$2"
|
||||
|
||||
# Extract app name and source repo directly from check_for_gh_release call in the ct script
|
||||
# Pattern: check_for_gh_release "appname" "owner/repo"
|
||||
local check_line app_name app_lc source_repo
|
||||
check_line=$(echo "$script" | grep -m1 'check_for_gh_release')
|
||||
|
||||
if [[ -z "$check_line" ]]; then
|
||||
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): no check_for_gh_release found — skipping"
|
||||
DRY_RUN_RESULT="no check_for_gh_release found — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
app_name=$(echo "$check_line" | cut -d'"' -f2)
|
||||
source_repo=$(echo "$check_line" | cut -d'"' -f4)
|
||||
app_lc=$(echo "${app_name,,}" | tr -d ' ')
|
||||
|
||||
if [[ -z "$source_repo" || "$source_repo" != *"/"* ]]; then
|
||||
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot parse source repo — skipping"
|
||||
DRY_RUN_RESULT="cannot parse source repo — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
# Read installed version from container (stored by check_for_gh_release as ~/.<appname>)
|
||||
local current_version
|
||||
current_version=$(pct exec "$container" -- bash -c "cat \$HOME/.${app_lc} 2>/dev/null" 2>/dev/null || true)
|
||||
current_version="${current_version#v}"
|
||||
|
||||
# Query latest release from GitHub API
|
||||
local latest_version
|
||||
latest_version=$(curl -sSL --max-time 10 \
|
||||
-H 'Accept: application/vnd.github+json' \
|
||||
-H 'X-GitHub-Api-Version: 2022-11-28' \
|
||||
"https://api.github.com/repos/${source_repo}/releases/latest" 2>/dev/null |
|
||||
grep '"tag_name"' | head -1 | cut -d'"' -f4 | sed 's/^v//')
|
||||
|
||||
if [[ -z "$latest_version" ]]; then
|
||||
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): cannot fetch latest version from $source_repo"
|
||||
DRY_RUN_RESULT="cannot fetch latest version from $source_repo"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -z "$current_version" ]]; then
|
||||
echo -e "${BL}[DRY-RUN]${CL} Container $container ($service): installed version unknown, latest: ${latest_version} (${source_repo})"
|
||||
DRY_RUN_RESULT="version unknown — latest: ${latest_version}"
|
||||
elif [[ "$current_version" == "$latest_version" ]]; then
|
||||
echo -e "${GN}[DRY-RUN]${CL} Container $container ($service): up to date (${current_version})"
|
||||
DRY_RUN_RESULT="up to date (${current_version})"
|
||||
else
|
||||
echo -e "${YW}[DRY-RUN]${CL} Container $container ($service): update available ${current_version} → ${latest_version}"
|
||||
DRY_RUN_RESULT="update available ${current_version} → ${latest_version}"
|
||||
fi
|
||||
}
|
||||
|
||||
function backup_container() {
|
||||
msg_info "Creating backup for container $1"
|
||||
vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1
|
||||
@@ -169,8 +246,32 @@ END {
|
||||
' /etc/pve/storage.cfg)
|
||||
}
|
||||
|
||||
# Structured result tracking for the final summary report
|
||||
# Each entry: "CTID|service|STATUS|details"
|
||||
declare -a UPDATE_RESULTS=()
|
||||
function log_result() {
|
||||
# log_result <ctid> <service> <STATUS> <details>
|
||||
UPDATE_RESULTS+=("${1}|${2}|${3}|${4}")
|
||||
}
|
||||
|
||||
header_info
|
||||
|
||||
# =============================================================================
|
||||
# LOGGING SETUP
|
||||
# Key events are written directly to a timestamped log file under
|
||||
# /usr/local/community-scripts/update_apps/ — this avoids any stdout
|
||||
# redirection that would break interactive spinners or whiptail dialogs.
|
||||
# The full summary table is appended at the end of the run.
|
||||
# =============================================================================
|
||||
LOG_DIR="/usr/local/community-scripts/update_apps"
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="${LOG_DIR}/$(date '+%Y%m%d_%H%M%S').log"
|
||||
echo "Update started: $(date '+%Y-%m-%d %H:%M:%S')" >"$LOG_FILE"
|
||||
|
||||
function log_write() {
|
||||
echo "[$(date '+%H:%M:%S')] $*" >>"$LOG_FILE"
|
||||
}
|
||||
|
||||
# Skip confirmation if var_skip_confirm is set to yes
|
||||
if [[ "$var_skip_confirm" != "yes" ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit
|
||||
@@ -260,7 +361,10 @@ fi
|
||||
header_info
|
||||
|
||||
# Determine backup choice based on var_backup
|
||||
if [[ -n "$var_backup" ]]; then
|
||||
# Dry-run never needs a backup — skip the prompt entirely
|
||||
if [[ "$var_dry_run" == "yes" ]]; then
|
||||
BACKUP_CHOICE="no"
|
||||
elif [[ -n "$var_backup" ]]; then
|
||||
BACKUP_CHOICE="$var_backup"
|
||||
else
|
||||
BACKUP_CHOICE="no"
|
||||
@@ -270,7 +374,10 @@ else
|
||||
fi
|
||||
|
||||
# Determine unattended update based on var_unattended
|
||||
if [[ -n "$var_unattended" ]]; then
|
||||
# Dry-run never executes updates — skip the prompt entirely
|
||||
if [[ "$var_dry_run" == "yes" ]]; then
|
||||
UNATTENDED_UPDATE="no"
|
||||
elif [[ -n "$var_unattended" ]]; then
|
||||
UNATTENDED_UPDATE="$var_unattended"
|
||||
else
|
||||
UNATTENDED_UPDATE="no"
|
||||
@@ -321,6 +428,7 @@ fi
|
||||
containers_needing_reboot=()
|
||||
for container in $CHOICE; do
|
||||
echo -e "${BL}[INFO]${CL} Updating container $container"
|
||||
log_write "Container $container: starting"
|
||||
|
||||
if [ "$BACKUP_CHOICE" == "yes" ]; then
|
||||
backup_container $container
|
||||
@@ -342,9 +450,12 @@ for container in $CHOICE; do
|
||||
#1.1) If update script not detected, return
|
||||
if [ -z "${service}" ]; then
|
||||
echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container"
|
||||
log_result "$container" "(unknown)" "SKIPPED" "No update script found in container"
|
||||
log_write "Container $container: SKIPPED — no update script found"
|
||||
continue
|
||||
else
|
||||
echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}"
|
||||
log_write "Container $container: detected service '$service'"
|
||||
fi
|
||||
|
||||
#2) Extract service build/update resource requirements from config/installation file
|
||||
@@ -391,17 +502,29 @@ for container in $CHOICE; do
|
||||
fi
|
||||
|
||||
#3) if build resources are different than run resources, then:
|
||||
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then
|
||||
if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ] && [[ "$var_dry_run" != "yes" ]]; then
|
||||
pct set "$container" --cores "$build_cpu" --memory "$build_ram"
|
||||
fi
|
||||
|
||||
#3.5) Dry-run: report update availability without applying
|
||||
if [[ "$var_dry_run" == "yes" ]]; then
|
||||
DRY_RUN_RESULT=""
|
||||
dry_run_container "$container" "$service"
|
||||
log_result "$container" "$service" "DRY-RUN" "${DRY_RUN_RESULT:-version check only}"
|
||||
log_write "Container $container ($service): DRY-RUN — ${DRY_RUN_RESULT:-version check only}"
|
||||
continue
|
||||
fi
|
||||
|
||||
#4) Update service, using the update command
|
||||
# Prepend a no-op 'clear' wrapper to PATH so update scripts calling clear
|
||||
# don't fail without a TTY — works for all shells incl. ash (no export -f)
|
||||
SETUP_CMD="mkdir -p /tmp/.nc; printf '#!/bin/sh\n:\n' > /tmp/.nc/clear; chmod +x /tmp/.nc/clear; export PATH=/tmp/.nc:\$PATH; export TERM=dumb; "
|
||||
case "$os" in
|
||||
alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;;
|
||||
archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
|
||||
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
|
||||
ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
|
||||
opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;;
|
||||
alpine) pct exec "$container" -- ash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
|
||||
archlinux) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
|
||||
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
|
||||
ubuntu | debian | devuan) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
|
||||
opensuse) pct exec "$container" -- bash -c "${SETUP_CMD}${UPDATE_CMD}" ;;
|
||||
esac
|
||||
exit_code=$?
|
||||
|
||||
@@ -423,16 +546,31 @@ for container in $CHOICE; do
|
||||
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
msg_ok "Updated container $container"
|
||||
log_result "$container" "$service" "OK" "Updated successfully"
|
||||
log_write "Container $container ($service): OK"
|
||||
elif [ $exit_code -eq 75 ]; then
|
||||
echo -e "${YW}[WARN]${CL} Container $container skipped (requires interactive mode)"
|
||||
log_result "$container" "$service" "SKIPPED" "Requires interactive mode (exit 75)"
|
||||
log_write "Container $container ($service): SKIPPED — requires interactive mode"
|
||||
elif [ $exit_code -eq 113 ]; then
|
||||
echo -e "${YW}[WARN]${CL} Container $container skipped (under-provisioned: increase CPU/RAM to match template)"
|
||||
log_result "$container" "$service" "SKIPPED" "Under-provisioned — increase CPU/RAM to match template"
|
||||
log_write "Container $container ($service): SKIPPED — under-provisioned"
|
||||
elif [ $exit_code -eq 114 ]; then
|
||||
echo -e "${YW}[WARN]${CL} Container $container skipped (storage critically low on /boot)"
|
||||
log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)"
|
||||
log_write "Container $container ($service): SKIPPED — storage critically low on /boot"
|
||||
elif [ "$BACKUP_CHOICE" == "yes" ]; then
|
||||
msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore"
|
||||
log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore"
|
||||
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
|
||||
pct stop $container
|
||||
LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}')
|
||||
BACKUP_ENTRY=$(pvesm list "$STORAGE_CHOICE" 2>/dev/null | awk -v ctid="$container" '$1 ~ "vzdump-lxc-"ctid"-" || $1 ~ "/ct/"ctid"/" {print $1}' | sort -r | head -n1)
|
||||
if [ -z "$BACKUP_ENTRY" ]; then
|
||||
msg_error "No backup found in storage $STORAGE_CHOICE for container $container"
|
||||
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — no backup found for restore"
|
||||
log_write "Container $container ($service): FAILED — no backup found for restore"
|
||||
exit 235
|
||||
fi
|
||||
msg_info "Restoring from: $BACKUP_ENTRY"
|
||||
@@ -441,19 +579,76 @@ for container in $CHOICE; do
|
||||
if [ $restorestatus -eq 0 ]; then
|
||||
pct start $container
|
||||
msg_ok "Container $container successfully restored from backup"
|
||||
log_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup"
|
||||
log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY"
|
||||
else
|
||||
msg_error "Restore failed for container $container"
|
||||
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed"
|
||||
log_write "Container $container ($service): FAILED — restore also failed"
|
||||
exit 235
|
||||
fi
|
||||
else
|
||||
msg_error "Update failed for container $container. Exiting"
|
||||
exit "$exit_code"
|
||||
msg_error "Update failed for container $container (exit code: $exit_code)"
|
||||
log_result "$container" "$service" "FAILED" "Exit code $exit_code"
|
||||
log_write "Container $container ($service): FAILED (exit $exit_code)"
|
||||
if [[ "$var_continue_on_error" == "yes" ]]; then
|
||||
echo -e "${YW}[WARN]${CL} Continuing to next container (var_continue_on_error=yes)"
|
||||
continue
|
||||
else
|
||||
exit "$exit_code"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
wait
|
||||
header_info
|
||||
echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n"
|
||||
if [[ "$var_dry_run" == "yes" ]]; then
|
||||
echo -e "${GN}Dry-run complete. No containers were modified.${CL}\n"
|
||||
else
|
||||
echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# SUMMARY REPORT
|
||||
# =============================================================================
|
||||
if [ "${#UPDATE_RESULTS[@]}" -gt 0 ]; then
|
||||
SEPARATOR="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
HEADER=$(printf " %-8s %-22s %-10s %s" "CTID" "Service" "Status" "Details")
|
||||
|
||||
# terminal output (with colours)
|
||||
echo ""
|
||||
echo "$SEPARATOR"
|
||||
echo "$HEADER"
|
||||
echo "$SEPARATOR"
|
||||
for entry in "${UPDATE_RESULTS[@]}"; do
|
||||
IFS='|' read -r _ctid _svc _status _details <<<"$entry"
|
||||
case "$_status" in
|
||||
OK) _color="${GN}" ;;
|
||||
FAILED) _color="${RD}" ;;
|
||||
RESTORED) _color="${YW}" ;;
|
||||
*) _color="${YW}" ;;
|
||||
esac
|
||||
printf " %-8s %-22s ${_color}%-10s${CL} %s\n" "$_ctid" "$_svc" "$_status" "$_details"
|
||||
done
|
||||
echo "$SEPARATOR"
|
||||
echo ""
|
||||
echo "Full log: $LOG_FILE"
|
||||
echo ""
|
||||
|
||||
# append plain-text summary to log file
|
||||
{
|
||||
echo ""
|
||||
echo "Update finished: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo "$SEPARATOR"
|
||||
echo "$HEADER"
|
||||
echo "$SEPARATOR"
|
||||
for entry in "${UPDATE_RESULTS[@]}"; do
|
||||
IFS='|' read -r _ctid _svc _status _details <<<"$entry"
|
||||
printf " %-8s %-22s %-10s %s\n" "$_ctid" "$_svc" "$_status" "$_details"
|
||||
done
|
||||
echo "$SEPARATOR"
|
||||
} >>"$LOG_FILE"
|
||||
fi
|
||||
if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then
|
||||
echo -e "${RD}The following containers require a reboot:${CL}"
|
||||
for container_name in "${containers_needing_reboot[@]}"; do
|
||||
|
||||
@@ -494,7 +494,7 @@ fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
msg_info "Retrieving the URL for the Ubuntu 25.04 Disk Image"
|
||||
URL=https://cloud-images.ubuntu.com/plucky/current/plucky-server-cloudimg-amd64.img
|
||||
URL=https://cloud-images.ubuntu.com/releases/server/plucky/release/ubuntu-25.04-server-cloudimg-amd64.img
|
||||
sleep 2
|
||||
msg_ok "${CL}${BL}${URL}${CL}"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
|
||||
Reference in New Issue
Block a user