Compare commits

..

17 Commits

Author SHA1 Message Date
b483b31868 Update mafl.sh 2025-07-04 22:32:59 +02:00
2b848ff1d8 Update CHANGELOG.md (#5714)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 21:24:37 +01:00
3d69931675 Refactor: Mafl (#5702)
* Refactor: Mafl

* Update mafl.sh
2025-07-04 22:24:12 +02:00
3e504cf48f final fixes 2025-07-04 22:19:35 +02:00
d2cbfcd69a Update install.func 2025-07-04 22:12:37 +02:00
ebfb6a4e34 Update install.func 2025-07-04 22:07:23 +02:00
7403470bd7 Update install.func 2025-07-04 21:55:35 +02:00
af9475d280 Update CHANGELOG.md (#5712)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 20:54:51 +01:00
8882a17b6f Update CHANGELOG.md (#5711)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 20:54:41 +01:00
32d6194ade Update install.func 2025-07-04 21:54:15 +02:00
569089cb73 Update create_lxc.sh 2025-07-04 21:47:23 +02:00
f494e68016 Update create_lxc.sh 2025-07-04 21:43:17 +02:00
7561e26c0a Core layer refactor: centralized error traps and msg_* consistency (#5705) 2025-07-04 21:22:50 +02:00
a95be13c95 Update .app files (#5706)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2025-07-04 17:53:15 +02:00
9ab50d4248 Update trilium.sh 2025-07-04 17:29:35 +02:00
f5be1d270a Update CHANGELOG.md (#5699)
Some checks failed
Auto Update .app-files / update-app-files (push) Has been cancelled
Create Changelog Pull Request / update-changelog-pull-request (push) Has been cancelled
Close Discussion on PR Merge / close-discussion (push) Has been cancelled
Sync to Gitea / sync (push) Has been cancelled
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 13:08:22 +01:00
524a2a422d Update versions.json (#5698)
Co-authored-by: GitHub Actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 14:07:48 +02:00
14 changed files with 513 additions and 478 deletions

View File

@ -20,12 +20,20 @@ All LXC instances created using this repository come pre-installed with Midnight
- #### 🐞 Bug Fixes
- Refactor: Mafl [@tremor021](https://github.com/tremor021) ([#5702](https://github.com/community-scripts/ProxmoxVE/pull/5702))
- Outline: Fix sed command for v0.85.0 [@tremor021](https://github.com/tremor021) ([#5688](https://github.com/community-scripts/ProxmoxVE/pull/5688))
- Komodo: Update Script to use FerretDB / remove psql & sqlite options [@MickLesk](https://github.com/MickLesk) ([#5690](https://github.com/community-scripts/ProxmoxVE/pull/5690))
- ESPHome: Fix Linking issue to prevent version mismatch [@MickLesk](https://github.com/MickLesk) ([#5685](https://github.com/community-scripts/ProxmoxVE/pull/5685))
- Update Iptag [@DesertGamer](https://github.com/DesertGamer) ([#5677](https://github.com/community-scripts/ProxmoxVE/pull/5677))
- Cloudflare-DDNS: fix unvisible read command at install [@MickLesk](https://github.com/MickLesk) ([#5682](https://github.com/community-scripts/ProxmoxVE/pull/5682))
- #### ✨ New Features
- Core layer refactor: centralized error traps and msg_* consistency [@MickLesk](https://github.com/MickLesk) ([#5705](https://github.com/community-scripts/ProxmoxVE/pull/5705))
- #### 💥 Breaking Changes
- Update Iptag [@DesertGamer](https://github.com/DesertGamer) ([#5677](https://github.com/community-scripts/ProxmoxVE/pull/5677))
### 🌐 Website
- #### 📝 Script Information

View File

@ -1,6 +1,6 @@
______ _ ___ _ __ __
/_ __/____(_) (_)_ ______ ___ / | / /___ / /____ _____
/ / / ___/ / / / / / / __ `__ \ / |/ / __ \/ __/ _ \/ ___/
/ / / / / / / / /_/ / / / / / / / /| / /_/ / /_/ __(__ )
/_/ /_/ /_/_/_/\__,_/_/ /_/ /_/ /_/ |_/\____/\__/\___/____/
______ _ ___
/_ __/____(_) (_)_ ______ ___
/ / / ___/ / / / / / / __ `__ \
/ / / / / / / / /_/ / / / / / /
/_/ /_/ /_/_/_/\__,_/_/ /_/ /_/

View File

@ -9,7 +9,7 @@ APP="karakeep"
var_tags="${var_tags:-bookmark}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-14}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-12}"
var_unprivileged="${var_unprivileged:-1}"

View File

@ -27,18 +27,31 @@ function update_script() {
msg_error "No ${APP} Installation Found!"
exit
fi
RELEASE=$(curl -fsSL https://api.github.com/repos/hywax/mafl/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
msg_info "Updating Mafl to v${RELEASE} (Patience)"
systemctl stop mafl
curl -fsSL "https://github.com/hywax/mafl/archive/refs/tags/v${RELEASE}.tar.gz" -o $(basename "https://github.com/hywax/mafl/archive/refs/tags/v${RELEASE}.tar.gz")
tar -xzf v${RELEASE}.tar.gz
cp -r mafl-${RELEASE}/* /opt/mafl/
rm -rf mafl-${RELEASE}
cd /opt/mafl
yarn install
yarn build
systemctl start mafl
msg_ok "Updated Mafl to v${RELEASE}"
if [[ "${RELEASE}" != "$(cat ~/.mafl 2>/dev/null)" ]] || [[ ! -f ~/.mafl ]]; then
msg_info "Stopping Mafl service"
systemctl stop mafl
msg_ok "Service stopped"
msg_info "Performing backup"
mkdir -p /opt/mafl-backup/data
mv /opt/mafl/data /opt/mafl-backup/data
rm /opt/mafl
msg_ok "Backup complete"
fetch_and_deploy_gh_release "mafl" "hywax/mafl"
msg_info "Updating Mafl to v${RELEASE}"
cd /opt/mafl
yarn install
yarn build
mv /opt/mafl-backup/data /opt/mafl/data
systemctl start mafl
msg_ok "Updated Mafl to v${RELEASE}"
else
msg_ok "No update required. ${APP} is already at v${RELEASE}"
fi
exit
}

View File

@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/TriliumNext/Trilium
APP="Trilium Notes"
APP="Trilium"
var_tags="${var_tags:-notes}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"

View File

@ -21,7 +21,7 @@
"resources": {
"cpu": 2,
"ram": 4096,
"hdd": 14,
"hdd": 10,
"os": "debian",
"version": "12"
}

View File

@ -1,14 +1,69 @@
[
{
"name": "Graylog2/graylog2-server",
"version": "6.3.1",
"date": "2025-07-04T11:20:48Z"
},
{
"name": "mattermost/mattermost",
"version": "preview-v0.1",
"date": "2025-06-27T14:35:47Z"
},
{
"name": "theonedev/onedev",
"version": "v11.11.3",
"date": "2025-07-04T09:04:46Z"
},
{
"name": "nzbgetcom/nzbget",
"version": "v25.2",
"date": "2025-07-04T08:21:42Z"
},
{
"name": "bunkerity/bunkerweb",
"version": "v1.6.2",
"date": "2025-07-04T08:43:44Z"
},
{
"name": "Checkmk/checkmk",
"version": "v2.2.0p44",
"date": "2025-07-04T06:44:06Z"
},
{
"name": "redis/redis",
"version": "8.2-rc1-int",
"date": "2025-07-02T19:27:08Z"
},
{
"name": "Jackett/Jackett",
"version": "v0.22.2117",
"date": "2025-07-04T05:56:05Z"
},
{
"name": "hyperion-project/hyperion.ng",
"version": "2.1.1",
"date": "2025-06-14T17:45:06Z"
},
{
"name": "steveiliop56/tinyauth",
"version": "v3.4.1",
"date": "2025-06-11T07:53:44Z"
},
{
"name": "outline/outline",
"version": "v0.85.0",
"date": "2025-07-03T23:31:00Z"
"date": "2025-07-04T00:06:47Z"
},
{
"name": "home-assistant/operating-system",
"version": "15.2",
"date": "2025-04-14T15:37:12Z"
},
{
"name": "keycloak/keycloak",
"version": "26.3.0",
"date": "2025-07-02T12:26:44Z"
},
{
"name": "cloudflare/cloudflared",
"version": "2025.7.0",
@ -19,16 +74,6 @@
"version": "v4.1.2",
"date": "2025-07-03T16:59:29Z"
},
{
"name": "bunkerity/bunkerweb",
"version": "v1.6.1",
"date": "2025-03-15T17:29:17Z"
},
{
"name": "Checkmk/checkmk",
"version": "v2.4.0p6",
"date": "2025-07-03T16:40:42Z"
},
{
"name": "influxdata/influxdb",
"version": "v3.2.1",
@ -54,16 +99,6 @@
"version": "fumadocs-openapi@9.0.17",
"date": "2025-07-03T06:57:48Z"
},
{
"name": "mattermost/mattermost",
"version": "preview-v0.1",
"date": "2025-06-27T14:35:47Z"
},
{
"name": "Jackett/Jackett",
"version": "v0.22.2111",
"date": "2025-07-03T05:50:31Z"
},
{
"name": "esphome/esphome",
"version": "2025.6.3",
@ -104,16 +139,6 @@
"version": "2.5.1",
"date": "2025-07-02T19:38:06Z"
},
{
"name": "redis/redis",
"version": "8.2-rc1-int",
"date": "2025-07-02T19:27:08Z"
},
{
"name": "keycloak/keycloak",
"version": "26.3.0",
"date": "2025-07-02T12:26:44Z"
},
{
"name": "ollama/ollama",
"version": "v0.9.5",
@ -129,16 +154,6 @@
"version": "2025.7.0",
"date": "2025-07-02T16:23:42Z"
},
{
"name": "nzbgetcom/nzbget",
"version": "v25.1",
"date": "2025-06-27T09:14:14Z"
},
{
"name": "Graylog2/graylog2-server",
"version": "6.2.5",
"date": "2025-07-02T13:06:30Z"
},
{
"name": "wazuh/wazuh",
"version": "coverity-w27-4.13.0",
@ -169,11 +184,6 @@
"version": "v0.20.2",
"date": "2025-07-02T00:37:07Z"
},
{
"name": "hyperion-project/hyperion.ng",
"version": "2.1.1",
"date": "2025-06-14T17:45:06Z"
},
{
"name": "Threadfin/Threadfin",
"version": "1.2.35",
@ -279,11 +289,6 @@
"version": "0.50.5",
"date": "2025-06-29T08:54:47Z"
},
{
"name": "theonedev/onedev",
"version": "v11.11.2",
"date": "2025-06-29T01:40:39Z"
},
{
"name": "linkwarden/linkwarden",
"version": "v2.11.2",
@ -431,8 +436,8 @@
},
{
"name": "runtipi/runtipi",
"version": "v4.2.1",
"date": "2025-06-03T20:04:28Z"
"version": "nightly",
"date": "2025-06-23T19:10:33Z"
},
{
"name": "VictoriaMetrics/VictoriaMetrics",
@ -679,11 +684,6 @@
"version": "v1.63.1",
"date": "2025-06-11T11:05:42Z"
},
{
"name": "steveiliop56/tinyauth",
"version": "v3.4.1",
"date": "2025-06-11T07:53:44Z"
},
{
"name": "OctoPrint/OctoPrint",
"version": "1.11.2",

View File

@ -14,22 +14,17 @@ network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y make
$STD apt-get install -y g++
$STD apt-get install -y gcc
$STD apt-get install -y ca-certificates
$STD apt-get install -y \
ca-certificates \
build-essential
msg_ok "Installed Dependencies"
NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs
fetch_and_deploy_gh_release "mafl" "hywax/mafl"
RELEASE=$(curl -fsSL https://api.github.com/repos/hywax/mafl/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
msg_info "Installing Mafl v${RELEASE}"
curl -fsSL "https://github.com/hywax/mafl/archive/refs/tags/v${RELEASE}.tar.gz" -o "v${RELEASE}.tar.gz"
tar -xzf v${RELEASE}.tar.gz
mkdir -p /opt/mafl/data
curl -fsSL "https://raw.githubusercontent.com/hywax/mafl/main/.example/config.yml" -o "/opt/mafl/data/config.yml"
mv mafl-${RELEASE}/* /opt/mafl
rm -rf mafl-${RELEASE}
cd /opt/mafl
export NUXT_TELEMETRY_DISABLED=true
$STD yarn install

View File

@ -83,11 +83,6 @@ update_os() {
msg_info "Updating Container OS"
$STD apk -U upgrade
msg_ok "Updated Container OS"
msg_info "Installing core dependencies"
$STD apk update
$STD apk add newt curl openssh nano mc ncurses gpg
msg_ok "Core dependencies installed"
}
# This function modifies the message of the day (motd) and SSH settings

View File

@ -304,13 +304,12 @@ echo_default() {
fi
# Output the selected values with icons
echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}"
echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}"
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${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${CL}"
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}"
if [ "$VERB" == "yes" ]; then
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}"
fi
@ -1095,7 +1094,9 @@ build_container() {
# This executes create_lxc.sh and creates the container and .conf file
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/create_lxc.sh)" $?
LXC_CONFIG=/etc/pve/lxc/${CTID}.conf
LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
# USB passthrough for privileged LXC (CT_TYPE=0)
if [ "$CT_TYPE" == "0" ]; then
cat <<EOF >>"$LXC_CONFIG"
# USB passthrough
@ -1111,38 +1112,98 @@ lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=
EOF
fi
if [ "$CT_TYPE" == "0" ]; then
if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "immich" || "$APP" == "Tdarr" || "$APP" == "Open WebUI" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then
cat <<EOF >>"$LXC_CONFIG"
# VAAPI hardware transcoding
lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.cgroup2.devices.allow: c 29:0 rwm
lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
EOF
# VAAPI passthrough for privileged containers or known apps
VAAPI_APPS=(
"immich"
"Channels"
"Emby"
"ErsatzTV"
"Frigate"
"Jellyfin"
"Plex"
"Scrypted"
"Tdarr"
"Unmanic"
"Ollama"
"FileFlows"
"Open WebUI"
)
is_vaapi_app=false
for vaapi_app in "${VAAPI_APPS[@]}"; do
if [[ "$APP" == "$vaapi_app" ]]; then
is_vaapi_app=true
break
fi
else
if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "immich" || "$APP" == "Tdarr" || "$APP" == "Open WebUI" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then
if [[ -e "/dev/dri/renderD128" ]]; then
if [[ -e "/dev/dri/card0" ]]; then
cat <<EOF >>"$LXC_CONFIG"
# VAAPI hardware transcoding
dev0: /dev/dri/card0,gid=44
dev1: /dev/dri/renderD128,gid=104
EOF
else
cat <<EOF >>"$LXC_CONFIG"
# VAAPI hardware transcoding
dev0: /dev/dri/card1,gid=44
dev1: /dev/dri/renderD128,gid=104
EOF
done
if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) &&
([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then
echo ""
msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container"
if [ "$CT_TYPE" != "0" ]; then
msg_custom "⚠️ " "\e[33m" "Container is unprivileged VAAPI passthrough may not work without additional host configuration (e.g., idmap)."
fi
msg_custom " " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)."
echo ""
read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL
if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then
# Mount all devices automatically
if [[ -e /dev/dri/renderD128 ]]; then
echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
if [[ -e /dev/dri/card0 ]]; then
echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
if [[ -e /dev/fb0 ]]; then
echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
if [[ -d /dev/dri ]]; then
echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
fi
else
# Manual selection per device
if [[ -e /dev/dri/renderD128 ]]; then
read -rp "➤ Mount /dev/dri/renderD128 (GPU rendering)? [y/N]: " MOUNT_D128
if [[ "$MOUNT_D128" =~ ^[Yy]$ ]]; then
echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
fi
if [[ -e /dev/dri/card0 ]]; then
read -rp "➤ Mount /dev/dri/card0 (GPU hardware interface)? [y/N]: " MOUNT_CARD0
if [[ "$MOUNT_CARD0" =~ ^[Yy]$ ]]; then
echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
fi
if [[ -e /dev/fb0 ]]; then
read -rp "➤ Mount /dev/fb0 (Framebuffer, GUI)? [y/N]: " MOUNT_FB0
if [[ "$MOUNT_FB0" =~ ^[Yy]$ ]]; then
echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG"
echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG"
fi
fi
if [[ -d /dev/dri ]]; then
echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
fi
fi
fi
# TUN device passthrough
if [ "$ENABLE_TUN" == "yes" ]; then
cat <<EOF >>"$LXC_CONFIG"
lxc.cgroup2.devices.allow: c 10:200 rwm
@ -1172,10 +1233,13 @@ EOF'
locale-gen >/dev/null && \
export LANG=\$locale_line"
if [[ -z "${tz:-}" ]]; then
tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC")
fi
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"
pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime"
else
msg_info "Skipping timezone setup zone '$tz' not found in container"
msg_warn "Skipping timezone setup zone '$tz' not found in container"
fi
pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null"
@ -1255,7 +1319,9 @@ api_exit_script() {
fi
}
trap 'api_exit_script' EXIT
if command -v pveversion >/dev/null 2>&1; then
trap 'api_exit_script' EXIT
fi
trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM

View File

@ -1,30 +1,6 @@
# Copyright (c) 2021-2025 community-scripts ORG
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE
# if ! declare -f wait_for >/dev/null; then
# echo "[DEBUG] Undefined function 'wait_for' used from: ${BASH_SOURCE[*]}" >&2
# wait_for() {
# echo "[DEBUG] Fallback: wait_for called with: $*" >&2
# true
# }
# fi
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
declare -A MSG_INFO_SHOWN=()
SPINNER_PID=""
SPINNER_ACTIVE=0
SPINNER_MSG=""
# ------------------------------------------------------------------------------
# Loads core utility groups once (colors, formatting, icons, defaults).
# ------------------------------------------------------------------------------
@ -43,100 +19,51 @@ load_functions() {
# add more
}
on_error() {
local exit_code="$1"
local lineno="$2"
# ============================================================================
# Error & Signal Handling robust, universal, subshell-safe
# ============================================================================
stop_spinner
case "$exit_code" in
1) msg_error "Generic error occurred (line $lineno)" ;;
2) msg_error "Shell misuse (line $lineno)" ;;
126) msg_error "Command cannot execute (line $lineno)" ;;
127) msg_error "Command not found (line $lineno)" ;;
128) msg_error "Invalid exit argument (line $lineno)" ;;
130) msg_error "Script aborted by user (CTRL+C)" ;;
143) msg_error "Script terminated by SIGTERM" ;;
*) msg_error "Script failed at line $lineno with exit code $exit_code" ;;
esac
exit "$exit_code"
}
on_exit() {
cleanup_spinner || true
[[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited"
}
on_interrupt() {
msg_error "Interrupted by user (CTRL+C)"
exit 130
}
on_terminate() {
msg_error "Terminated by signal (TERM)"
exit 143
}
setup_trap_abort_handling() {
trap '__handle_signal_abort SIGINT' SIGINT
trap '__handle_signal_abort SIGTERM' SIGTERM
trap '__handle_unexpected_error $?' ERR
}
__handle_signal_abort() {
local signal="$1"
echo
[ -n "${SPINNER_PID:-}" ] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null
case "$signal" in
SIGINT)
msg_error "Script aborted by user (CTRL+C)"
exit 130
_tool_error_hint() {
local cmd="$1"
local code="$2"
case "$cmd" in
curl)
case "$code" in
6) echo "Curl: Could not resolve host (DNS problem)" ;;
7) echo "Curl: Failed to connect to host (connection refused)" ;;
22) echo "Curl: HTTP error (404/403 etc)" ;;
28) echo "Curl: Operation timeout" ;;
*) echo "Curl: Unknown error ($code)" ;;
esac
;;
SIGTERM)
msg_error "Script terminated (SIGTERM)"
exit 143
wget)
echo "Wget failed URL unreachable or permission denied"
;;
*)
msg_error "Script interrupted (unknown signal: $signal)"
exit 1
systemctl)
echo "Systemd unit failure check service name and permissions"
;;
jq)
echo "jq parse error malformed JSON or missing key"
;;
mariadb | mysql)
echo "MySQL/MariaDB command failed check credentials or DB"
;;
unzip)
echo "unzip failed corrupt file or missing permission"
;;
tar)
echo "tar failed invalid format or missing binary"
;;
node | npm | pnpm | yarn)
echo "Node tool failed check version compatibility or package.json"
;;
*) echo "" ;;
esac
}
__handle_unexpected_error() {
local exit_code="$1"
echo
[ -n "${SPINNER_PID:-}" ] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null
case "$exit_code" in
1)
msg_error "Generic error occurred (exit code 1)"
;;
2)
msg_error "Misuse of shell builtins (exit code 2)"
;;
126)
msg_error "Command invoked cannot execute (exit code 126)"
;;
127)
msg_error "Command not found (exit code 127)"
;;
128)
msg_error "Invalid exit argument (exit code 128)"
;;
130)
msg_error "Script aborted by user (CTRL+C)"
;;
143)
msg_error "Script terminated by SIGTERM"
;;
*)
msg_error "Unexpected error occurred (exit code $exit_code)"
;;
esac
exit "$exit_code"
catch_errors() {
set -Eeuo pipefail
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
}
# ------------------------------------------------------------------------------
@ -153,6 +80,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.
# ------------------------------------------------------------------------------
@ -196,6 +130,7 @@ icons() {
ADVANCED="${TAB}🧩${TAB}${CL}"
FUSE="${TAB}🗂️${TAB}${CL}"
HOURGLASS="${TAB}${TAB}"
}
# ------------------------------------------------------------------------------
@ -227,7 +162,7 @@ silent() {
# Function to download & save header files
get_header() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local app_type=${APP_TYPE:-ct} # Default 'ct'
local app_type=${APP_TYPE:-ct}
local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/${app_type}/headers/${app_name}"
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
@ -257,77 +192,39 @@ header_info() {
fi
}
# ------------------------------------------------------------------------------
# Performs a curl request with retry logic and inline feedback.
# ------------------------------------------------------------------------------
run_curl() {
if [ "$VERBOSE" = "no" ]; then
$STD curl "$@"
else
curl "$@"
ensure_tput() {
if ! command -v tput >/dev/null 2>&1; then
if grep -qi 'alpine' /etc/os-release; then
apk add --no-cache ncurses >/dev/null 2>&1
elif command -v apt-get >/dev/null 2>&1; then
apt-get update -qq >/dev/null
apt-get install -y -qq ncurses-bin >/dev/null 2>&1
fi
fi
}
curl_handler() {
set +e
trap 'set -e' RETURN
local args=()
local url=""
local max_retries=3
local delay=2
local attempt=1
local exit_code
local has_output_file=false
local result=""
is_alpine() {
local os_id="${var_os:-${PCT_OSTYPE:-}}"
# Parse arguments
for arg in "$@"; do
if [[ "$arg" != -* && -z "$url" ]]; then
url="$arg"
fi
[[ "$arg" == "-o" || "$arg" == --output ]] && has_output_file=true
args+=("$arg")
done
if [[ -z "$url" ]]; then
msg_error "No valid URL or option entered for curl_handler"
return 1
if [[ -z "$os_id" && -f /etc/os-release ]]; then
os_id="$(
. /etc/os-release 2>/dev/null
echo "${ID:-}"
)"
fi
$STD msg_info "Fetching: $url"
[[ "$os_id" == "alpine" ]]
}
while [[ $attempt -le $max_retries ]]; do
if $has_output_file; then
$STD run_curl "${args[@]}"
exit_code=$?
else
result=$(run_curl "${args[@]}")
exit_code=$?
fi
if [[ $exit_code -eq 0 ]]; then
$STD msg_ok "Fetched: $url"
$has_output_file || printf '%s' "$result"
return 0
fi
if ((attempt >= max_retries)); then
# Read error log if it exists
if [ -s /tmp/curl_error.log ]; then
local curl_stderr
curl_stderr=$(</tmp/curl_error.log)
rm -f /tmp/curl_error.log
fi
__curl_err_handler "$exit_code" "$url" "${curl_stderr:-}"
exit
fi
$STD printf "\r\033[K${INFO}${YW}Retry $attempt/$max_retries in ${delay}s...${CL}" >&2
sleep "$delay"
((attempt++))
done
set -e
is_verbose_mode() {
local verbose="${VERBOSE:-${var_verbose:-no}}"
local tty_status
if [[ -t 2 ]]; then
tty_status="interactive"
else
tty_status="not-a-tty"
fi
[[ "$verbose" != "no" || ! -t 2 ]]
}
# ------------------------------------------------------------------------------
@ -372,144 +269,93 @@ 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
spinner_frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
# === Spinner Start ===
start_spinner() {
local msg="$1"
local spin_i=0
local interval=0.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
} &
local pid=$!
if ps -p "$pid" >/dev/null 2>&1; then
SPINNER_PID="$pid"
else
SPINNER_ACTIVE=0
SPINNER_PID=""
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
}
clear_line() {
tput cr 2>/dev/null || echo -en "\r"
tput el 2>/dev/null || echo -en "\033[K"
}
# === 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
fi
}
cleanup_spinner() {
stop_spinner
unset SPINNER_PID SPINNER_MSG
stty sane 2>/dev/null || true
}
msg_info() {
local msg="$1"
[[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
[[ -z "$msg" ]] && return
if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then
declare -gA MSG_INFO_SHOWN=()
fi
[[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
MSG_INFO_SHOWN["$msg"]=1
stop_spinner
SPINNER_MSG="$msg"
if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then
start_spinner "$msg"
else
if is_verbose_mode || is_alpine; then
local HOURGLASS="${TAB}${TAB}"
printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2
return
fi
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
clear_line
printf "%s %b\n" "$CM" "${GN}${msg}${CL}" >&2
unset MSG_INFO_SHOWN["$msg"]
}
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
unset MSG_INFO_SHOWN["$msg"]
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
}
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
stop_spinner
echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}"
printf "\r\033[K\e[?25h\n"
}
run_container_safe() {
@ -560,3 +406,5 @@ check_or_create_swap() {
return 1
fi
}
trap 'stop_spinner' EXIT INT TERM

View File

@ -21,36 +21,67 @@ fi
# This sets error handling options and defines the error_handler function to handle errors
set -Eeuo pipefail
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap on_exit EXIT
trap on_interrupt INT
trap on_terminate TERM
function on_exit() {
local exit_code="$?"
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
exit "$exit_code"
}
# This function handles errors
function error_handler() {
printf "\e[?25h"
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
exit 200
printf "\e[?25h"
echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n"
exit "$exit_code"
}
function on_interrupt() {
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
exit 130
}
function on_terminate() {
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
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 "Validated Storage (rootdir / vztmpl)."
# This function is used to select the storage class and determine the corresponding storage content type and label.
function select_storage() {
local CLASS=$1
local CONTENT
local CONTENT_LABEL
local CLASS=$1 CONTENT CONTENT_LABEL
case $CLASS in
container)
CONTENT='rootdir'
@ -60,51 +91,72 @@ function select_storage() {
CONTENT='vztmpl'
CONTENT_LABEL='Container template'
;;
*) false || {
msg_error "Invalid storage class."
exit 201
} ;;
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'"
return 1
;;
esac
# Collect storage options
local -a MENU
local MSG_MAX_LENGTH=0
local -a MENU
local -A STORAGE_MAP
local COL_WIDTH=0
while read -r TAG TYPE _ _ _ FREE _; do
local TYPE_PADDED
local FREE_FMT
TYPE_PADDED=$(printf "%-10s" "$TYPE")
FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.2f <<<"$FREE")B
local ITEM="Type: $TYPE_PADDED Free: $FREE_FMT"
((${#ITEM} + 2 > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + 2))
MENU+=("$TAG" "$ITEM" "OFF")
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"
MENU+=("$DISPLAY" "$INFO" "OFF")
((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY}
done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
local OPTION_COUNT=$((${#MENU[@]} / 3))
if [ ${#MENU[@]} -eq 0 ]; then
msg_error "No storage found for content type '$CONTENT'."
return 2
fi
# Auto-select if only one option available
if [[ "$OPTION_COUNT" -eq 1 ]]; then
echo "${MENU[0]}"
if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}"
return 0
fi
# Display selection menu
local STORAGE
while [[ -z "${STORAGE:+x}" ]]; do
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
"Select the storage pool to use for the ${CONTENT_LABEL,,}.\nUse the spacebar to make a selection.\n" \
16 $((MSG_MAX_LENGTH + 23)) 6 \
"${MENU[@]}" 3>&1 1>&2 2>&3) || {
msg_error "Storage selection cancelled."
exit 202
}
done
local WIDTH=$((COL_WIDTH + 42))
while true; do
local 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)
echo "$STORAGE"
[[ $? -ne 0 ]] && return 3
if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then
whiptail --msgbox "No valid storage selected. Please try again." 8 58
continue
fi
STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}"
return 0
done
}
# Test if required variables are set
[[ "${CTID:-}" ]] || {
msg_error "You need to set 'CTID' variable."
@ -129,13 +181,55 @@ if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
exit 206
fi
# Get template storage
TEMPLATE_STORAGE=$(select_storage template)
msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage."
# DEFAULT_FILE="/usr/local/community-scripts/default_storage"
# if [[ -f "$DEFAULT_FILE" ]]; then
# source "$DEFAULT_FILE"
# if [[ -n "$TEMPLATE_STORAGE" && -n "$CONTAINER_STORAGE" ]]; then
# msg_info "Using default storage configuration from: $DEFAULT_FILE"
# msg_ok "Template Storage: ${BL}$TEMPLATE_STORAGE${CL} ${GN}|${CL} Container Storage: ${BL}$CONTAINER_STORAGE${CL}"
# else
# msg_warn "Default storage file exists but is incomplete falling back to manual selection"
# TEMPLATE_STORAGE=$(select_storage template)
# msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage."
# CONTAINER_STORAGE=$(select_storage container)
# msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
# fi
# else
# # TEMPLATE STORAGE SELECTION
# # Template Storage
# while true; do
# TEMPLATE_STORAGE=$(select_storage template)
# if [[ -n "$TEMPLATE_STORAGE" ]]; then
# msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage."
# break
# fi
# msg_warn "No valid template storage selected. Please try again."
# done
# Get container storage
CONTAINER_STORAGE=$(select_storage container)
msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
# while true; do
# CONTAINER_STORAGE=$(select_storage container)
# if [[ -n "$CONTAINER_STORAGE" ]]; then
# msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
# break
# fi
# msg_warn "No valid container storage selected. Please try again."
# done
# fi
while true; do
if select_storage template; then
TEMPLATE_STORAGE="$STORAGE_RESULT"
break
fi
done
while true; do
if select_storage container; then
CONTAINER_STORAGE="$STORAGE_RESULT"
break
fi
done
# Check free space on selected container storage
STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }')
@ -204,7 +298,7 @@ if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLAT
done
fi
msg_ok "LXC Template '$TEMPLATE' is ready to use."
msg_info "Creating LXC Container"
# Check and fix subuid/subgid
grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid
grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid
@ -215,12 +309,15 @@ PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}})
# Secure creation of the LXC container with lock and template check
lockfile="/tmp/template.${TEMPLATE}.lock"
exec 9>"$lockfile"
exec 9>"$lockfile" >/dev/null 2>&1 || {
msg_error "Failed to create lock file '$lockfile'."
exit 200
}
flock -w 60 9 || {
msg_error "Timeout while waiting for template lock"
exit 211
}
msg_info "Creating LXC Container"
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
msg_error "Container creation failed. Checking if template is corrupted or incomplete."
@ -252,16 +349,23 @@ if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[
sleep 1 # I/O-Sync-Delay
msg_ok "Re-downloaded LXC Template"
fi
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
msg_error "Container creation failed after re-downloading template."
exit 200
if ! pct list | awk '{print $1}' | grep -qx "$CTID"; then
msg_error "Container ID $CTID not listed in 'pct list' unexpected failure."
exit 215
fi
if ! grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf"; then
msg_error "RootFS entry missing in container config storage not correctly assigned."
exit 216
fi
if grep -q '^hostname:' "/etc/pve/lxc/$CTID.conf"; then
CT_HOSTNAME=$(grep '^hostname:' "/etc/pve/lxc/$CTID.conf" | awk '{print $2}')
if [[ ! "$CT_HOSTNAME" =~ ^[a-z0-9-]+$ ]]; then
msg_warn "Hostname '$CT_HOSTNAME' contains invalid characters may cause issues with networking or DNS."
fi
fi
if ! pct status "$CTID" &>/dev/null; then
msg_error "Container not found after pct create assuming failure."
exit 210
fi
msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created."

View File

@ -62,9 +62,11 @@ 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
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
network_check() {
set +e
@ -72,6 +74,7 @@ network_check() {
ipv4_connected=false
ipv6_connected=false
sleep 1
# Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers.
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
msg_ok "IPv4 Internet Connected"
@ -100,25 +103,26 @@ network_check() {
fi
# DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
GITHUB_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com")
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
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
}

View File

@ -239,10 +239,14 @@ setup_mariadb() {
DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)"
CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)"
if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then
msg_error "MariaDB mirror not reachable"
return 1
fi
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/' |
@ -253,7 +257,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=""
@ -278,7 +281,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