From a128f3c43d7facb65c9bac84bdd1e17722baa6a0 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:07:03 +0200 Subject: [PATCH 001/139] Update versions.json (#5150) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 86 +++++++++++++++--------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 32d7e8ce4..ee432bd8e 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,44 @@ [ + { + "name": "syncthing/syncthing", + "version": "2.0.0-rc.19", + "date": "2025-06-02T17:56:25Z" + }, + { + "name": "Prowlarr/Prowlarr", + "version": "v1.37.0.5076", + "date": "2025-06-04T11:04:53Z" + }, + { + "name": "Readarr/Readarr", + "version": "v2.0.0.4645", + "date": "2017-03-07T18:56:06Z" + }, + { + "name": "Lidarr/Lidarr", + "version": "v2.12.4.4658", + "date": "2025-06-09T17:27:45Z" + }, + { + "name": "Radarr/Radarr", + "version": "v5.26.2.10099", + "date": "2025-06-11T20:10:39Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2017", + "date": "2025-06-15T05:54:05Z" + }, + { + "name": "traccar/traccar", + "version": "v6.7.3", + "date": "2025-06-15T05:46:17Z" + }, + { + "name": "hyperion-project/hyperion.ng", + "version": "2.1.1", + "date": "2025-06-14T17:45:06Z" + }, { "name": "advplyr/audiobookshelf", "version": "v2.25.1", @@ -9,11 +49,6 @@ "version": "v25.0", "date": "2025-05-12T09:12:04Z" }, - { - "name": "hyperion-project/hyperion.ng", - "version": "2.1.1", - "date": "2025-06-14T17:45:06Z" - }, { "name": "bunkerity/bunkerweb", "version": "v1.6.2-rc5", @@ -40,9 +75,9 @@ "date": "2025-06-11T12:07:38Z" }, { - "name": "Jackett/Jackett", - "version": "v0.22.2016", - "date": "2025-06-14T05:59:28Z" + "name": "runtipi/runtipi", + "version": "v4.2.1", + "date": "2025-06-03T20:04:28Z" }, { "name": "FlareSolverr/FlareSolverr", @@ -129,11 +164,6 @@ "version": "v1.12.0-rc.4", "date": "2025-06-12T00:27:41Z" }, - { - "name": "Radarr/Radarr", - "version": "v5.26.2.10099", - "date": "2025-06-11T20:10:39Z" - }, { "name": "TandoorRecipes/recipes", "version": "2.0.0-alpha-4", @@ -274,11 +304,6 @@ "version": "v0.28.3", "date": "2025-06-09T18:11:46Z" }, - { - "name": "Lidarr/Lidarr", - "version": "v2.12.4.4658", - "date": "2025-06-09T17:27:45Z" - }, { "name": "Brandawg93/PeaNUT", "version": "v5.8.0", @@ -319,11 +344,6 @@ "version": "v2.0.1", "date": "2025-06-08T14:40:24Z" }, - { - "name": "Readarr/Readarr", - "version": "v2.0.0.4645", - "date": "2017-03-07T18:56:06Z" - }, { "name": "matze/wastebin", "version": "3.2.0", @@ -339,11 +359,6 @@ "version": "v1.5.8", "date": "2025-06-07T11:39:10Z" }, - { - "name": "syncthing/syncthing", - "version": "2.0.0-rc.19", - "date": "2025-06-02T17:56:25Z" - }, { "name": "evcc-io/evcc", "version": "0.204.2", @@ -439,11 +454,6 @@ "version": "340", "date": "2025-06-04T16:41:44Z" }, - { - "name": "Prowlarr/Prowlarr", - "version": "v1.37.0.5076", - "date": "2025-06-04T11:04:53Z" - }, { "name": "glpi-project/glpi", "version": "10.0.18", @@ -464,11 +474,6 @@ "version": "v4.100.3", "date": "2025-06-03T21:06:41Z" }, - { - "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" - }, { "name": "influxdata/influxdb", "version": "v1.12.1rc3", @@ -499,11 +504,6 @@ "version": "v0.24.4", "date": "2025-06-02T02:49:05Z" }, - { - "name": "traccar/traccar", - "version": "v6.7.2", - "date": "2025-06-01T20:40:00Z" - }, { "name": "gethomepage/homepage", "version": "v1.3.2", From 632e823749cbdd832f68e946541e7c87481b4c05 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sun, 15 Jun 2025 20:39:06 +0200 Subject: [PATCH 002/139] Create nic-offloading-fix.json --- frontend/public/json/nic-offloading-fix.json | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/nic-offloading-fix.json diff --git a/frontend/public/json/nic-offloading-fix.json b/frontend/public/json/nic-offloading-fix.json new file mode 100644 index 000000000..7d976e29e --- /dev/null +++ b/frontend/public/json/nic-offloading-fix.json @@ -0,0 +1,40 @@ +{ + "name": "NIC Offloading Fix", + "slug": "nic-offloading-fix", + "categories": [ + 1 + ], + "date_created": "2025-05-25", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "config_path": "", + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "description": "This script automates the process of disabling network interface card (NIC) offloading features specifically for Intel e1000e network interfaces on Linux systems.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/nic-offloading-fix.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" + } + ] +} From f5a5e4854c8fbfc878b7f832ab04693d39b78051 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:39:27 +0100 Subject: [PATCH 003/139] Update date in json (#5153) Co-authored-by: GitHub Actions --- frontend/public/json/nic-offloading-fix.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/nic-offloading-fix.json b/frontend/public/json/nic-offloading-fix.json index 7d976e29e..356a23d05 100644 --- a/frontend/public/json/nic-offloading-fix.json +++ b/frontend/public/json/nic-offloading-fix.json @@ -4,7 +4,7 @@ "categories": [ 1 ], - "date_created": "2025-05-25", + "date_created": "2025-06-15", "type": "pve", "updateable": false, "privileged": false, From d9474c3dd6b5febd6226c8ec5039ccfe6a1ad3a0 Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 20:55:01 +0200 Subject: [PATCH 004/139] LibreTranslate (#5154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/headers/libretranslate | 6 ++ ct/libretranslate.sh | 61 +++++++++++++++++++ frontend/public/json/libretranslate.json | 44 ++++++++++++++ install/libretranslate-install.sh | 76 ++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 ct/headers/libretranslate create mode 100644 ct/libretranslate.sh create mode 100644 frontend/public/json/libretranslate.json create mode 100644 install/libretranslate-install.sh diff --git a/ct/headers/libretranslate b/ct/headers/libretranslate new file mode 100644 index 000000000..bf4c68627 --- /dev/null +++ b/ct/headers/libretranslate @@ -0,0 +1,6 @@ + __ _ __ ______ __ __ + / / (_) /_ ________/_ __/________ _____ _____/ /___ _/ /____ + / / / / __ \/ ___/ _ \/ / / ___/ __ `/ __ \/ ___/ / __ `/ __/ _ \ + / /___/ / /_/ / / / __/ / / / / /_/ / / / (__ ) / /_/ / /_/ __/ +/_____/_/_.___/_/ \___/_/ /_/ \__,_/_/ /_/____/_/\__,_/\__/\___/ + diff --git a/ct/libretranslate.sh b/ct/libretranslate.sh new file mode 100644 index 000000000..12e529af0 --- /dev/null +++ b/ct/libretranslate.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/LibreTranslate/LibreTranslate + +APP="LibreTranslate" +var_tags="${var_tags:-Arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/libretranslate ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -s https://api.github.com/repos/LibreTranslate/LibreTranslate/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ "${RELEASE}" != "$(cat $HOME/.libretranslate)" ]] || [[ ! -f $HOME/.libretranslate ]]; then + msg_info "Stopping $APP" + systemctl stop libretranslate + msg_ok "Stopped $APP" + + msg_info "Updating $APP to ${RELEASE}" + cd /opt/libretranslate + source .venv/bin/activate + $STD pip install -U libretranslate + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start libretranslate + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + 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}:5000${CL}" diff --git a/frontend/public/json/libretranslate.json b/frontend/public/json/libretranslate.json new file mode 100644 index 000000000..5d90bf4bd --- /dev/null +++ b/frontend/public/json/libretranslate.json @@ -0,0 +1,44 @@ +{ + "name": "LibreTranslate", + "slug": "libretranslate", + "categories": [ + 0 + ], + "date_created": "2025-06-13", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5000, + "documentation": "https://github.com/LibreTranslate/LibreTranslate?tab=readme-ov-file#settings--flags", + "website": "https://libretranslate.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/libretranslate.webp", + "config_path": "", + "description": "Free and Open Source Machine Translation API, entirely self-hosted. Unlike other APIs, it doesn't rely on proprietary providers such as Google or Azure to perform translations. Instead, its translation engine is powered by the open source Argos Translate library.", + "install_methods": [ + { + "type": "default", + "script": "ct/libretranslate.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 20, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "During the installation, application will download language models used for translation. Depending on how fast your internet/host is, this can take 5-10 minutes.", + "type": "info" + }, + { + "text": "At every boot of LXC, application will look for updates for language models installed. This can prolong the startup of the LXC.", + "type": "info" + } + ] +} diff --git a/install/libretranslate-install.sh b/install/libretranslate-install.sh new file mode 100644 index 000000000..6779f0397 --- /dev/null +++ b/install/libretranslate-install.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/LibreTranslate/LibreTranslate + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y --no-install-recommends \ + pkg-config \ + gcc \ + g++ \ + libicu-dev +msg_ok "Installed dependencies" + +msg_info "Setup Python3" +$STD apt-get install -y \ + python3-pip \ + python3-dev \ + python3-icu +msg_ok "Setup Python3" + +setup_uv +fetch_and_deploy_gh_release "LibreTranslate/LibreTranslate" + +msg_info "Setup LibreTranslate (Patience)" +cd /opt/libretranslate +$STD uv venv .venv +$STD source .venv/bin/activate +$STD uv pip install --upgrade pip setuptools +$STD uv pip install Babel==2.12.1 +$STD .venv/bin/python scripts/compile_locales.py +$STD uv pip install torch==2.2.0 --extra-index-url https://download.pytorch.org/whl/cpu +$STD uv pip install "numpy<2" +$STD uv pip install . +$STD uv pip install libretranslate +$STD .venv/bin/python scripts/install_models.py +msg_ok "Installed LibreTranslate" + +msg_info "Creating Service" +cat </etc/systemd/system/libretranslate.service +[Unit] +Description=LibreTranslate +After=network.target + +[Service] +User=root +Type=idle +Restart=always +Environment="PATH=/usr/local/lib/python3.11/dist-packages/libretranslate" +ExecStart=/opt/libretranslate/.venv/bin/python3 /opt/libretranslate/.venv/bin/libretranslate --host * --update-models +ExecReload=/bin/kill -s HUP +KillMode=mixed +TimeoutStopSec=1 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now libretranslate +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 89399ef421f6f3dd5f1fbbedd218417942745cc3 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:55:36 +0100 Subject: [PATCH 005/139] Update date in json (#5156) Co-authored-by: GitHub Actions --- frontend/public/json/libretranslate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/libretranslate.json b/frontend/public/json/libretranslate.json index 5d90bf4bd..85eebd688 100644 --- a/frontend/public/json/libretranslate.json +++ b/frontend/public/json/libretranslate.json @@ -4,7 +4,7 @@ "categories": [ 0 ], - "date_created": "2025-06-13", + "date_created": "2025-06-15", "type": "ct", "updateable": true, "privileged": false, From 63dfc2bd7b56b013666d203aca38b0333671692e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:55:39 +0100 Subject: [PATCH 006/139] Update CHANGELOG.md (#5157) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 777f6f0b5..4ed6ae9ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-15 +### 🆕 New Scripts + + - LibreTranslate ([#5154](https://github.com/community-scripts/ProxmoxVE/pull/5154)) + ## 2025-06-14 ### 🚀 Updated Scripts From dabfb5717369da0f8ed1070228c9de6478536f03 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:56:47 +0100 Subject: [PATCH 007/139] Update CHANGELOG.md (#5158) Co-authored-by: github-actions[bot] From 65e4c027b80f0d87c85b6c4ccd071ea750b791ed Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 02:15:31 +0200 Subject: [PATCH 008/139] Update versions.json (#5164) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index ee432bd8e..91345afe5 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,29 @@ [ + { + "name": "jellyfin/jellyfin", + "version": "v10.10.7", + "date": "2025-04-05T19:14:59Z" + }, + { + "name": "crafty-controller/crafty-4", + "version": "v4.4.11", + "date": "2025-06-15T21:41:02Z" + }, + { + "name": "TriliumNext/Notes", + "version": "v0.95.0", + "date": "2025-06-15T21:12:04Z" + }, + { + "name": "karakeep-app/karakeep", + "version": "cli/v0.25.0", + "date": "2025-06-15T17:48:29Z" + }, + { + "name": "tobychui/zoraxy", + "version": "v3.1.9", + "date": "2025-03-01T02:24:33Z" + }, { "name": "syncthing/syncthing", "version": "2.0.0-rc.19", @@ -334,11 +359,6 @@ "version": "5.26.8", "date": "2025-06-08T22:50:58Z" }, - { - "name": "karakeep-app/karakeep", - "version": "ios/v1.7.0-1", - "date": "2025-06-08T22:02:33Z" - }, { "name": "Forceu/Gokapi", "version": "v2.0.1", @@ -349,11 +369,6 @@ "version": "3.2.0", "date": "2025-06-07T21:33:22Z" }, - { - "name": "jellyfin/jellyfin", - "version": "v10.10.7", - "date": "2025-04-05T19:14:59Z" - }, { "name": "jordan-dalby/ByteStash", "version": "v1.5.8", @@ -364,11 +379,6 @@ "version": "0.204.2", "date": "2025-06-07T11:38:28Z" }, - { - "name": "TriliumNext/Notes", - "version": "v0.94.1", - "date": "2025-06-07T10:32:38Z" - }, { "name": "homebridge/homebridge", "version": "v1.10.0", @@ -644,11 +654,6 @@ "version": "v0.26.2", "date": "2025-05-22T05:24:42Z" }, - { - "name": "tobychui/zoraxy", - "version": "v3.1.9", - "date": "2025-03-01T02:24:33Z" - }, { "name": "apache/tika", "version": "3.2.0-rc2", @@ -669,11 +674,6 @@ "version": "v0.46.2", "date": "2025-05-20T11:21:04Z" }, - { - "name": "crafty-controller/crafty-4", - "version": "v4.4.9", - "date": "2025-05-20T00:08:29Z" - }, { "name": "Part-DB/Part-DB-server", "version": "v1.17.1", From 3044a774c2e5f1552ce5a48988e57996389daf2e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:16:08 +0100 Subject: [PATCH 009/139] Update CHANGELOG.md (#5165) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ed6ae9ac..f077a51bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-16 + ## 2025-06-15 ### 🆕 New Scripts From df43101b4e86a8de19c0327aae3b94197ba48a2b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 16 Jun 2025 06:59:18 +0200 Subject: [PATCH 010/139] Intel NIC offload Fix by @rcastley (#5155) * Create nic-offloading-fix.sh * Update nic-offloading-fix.json --- frontend/public/json/nic-offloading-fix.json | 2 +- tools/pve/nic-offloading-fix.sh | 227 +++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 tools/pve/nic-offloading-fix.sh diff --git a/frontend/public/json/nic-offloading-fix.json b/frontend/public/json/nic-offloading-fix.json index 356a23d05..7d976e29e 100644 --- a/frontend/public/json/nic-offloading-fix.json +++ b/frontend/public/json/nic-offloading-fix.json @@ -4,7 +4,7 @@ "categories": [ 1 ], - "date_created": "2025-06-15", + "date_created": "2025-05-25", "type": "pve", "updateable": false, "privileged": false, diff --git a/tools/pve/nic-offloading-fix.sh b/tools/pve/nic-offloading-fix.sh new file mode 100644 index 000000000..91a97fbbb --- /dev/null +++ b/tools/pve/nic-offloading-fix.sh @@ -0,0 +1,227 @@ +#!/usr/bin/env bash + +# Creates a systemd service to disable NIC offloading features for Intel e1000e interfaces +# Author: rcastley +# License: MIT + +YW=$(echo "\033[33m") +YWB=$'\e[93m' +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") +TAB=" " +CM="${TAB}✔️${TAB}" +CROSS="${TAB}✖️${TAB}" +INFO="${TAB}ℹ️${TAB}${CL}" +WARN="${TAB}⚠️${TAB}${CL}" + +function header_info { + clear + cat <<"EOF" + + _ ____________ ____ __________ ___ ____ _ __ __ + / | / / _/ ____/ / __ \/ __/ __/ /___ ____ _____/ (_)___ ____ _ / __ \(_)________ _/ /_ / /__ _____ + / |/ // // / / / / / /_/ /_/ / __ \/ __ `/ __ / / __ \/ __ `/ / / / / / ___/ __ `/ __ \/ / _ \/ ___/ + / /| // // /___ / /_/ / __/ __/ / /_/ / /_/ / /_/ / / / / / /_/ / / /_/ / (__ ) /_/ / /_/ / / __/ / +/_/ |_/___/\____/ \____/_/ /_/ /_/\____/\__,_/\__,_/_/_/ /_/\__, / /_____/_/____/\__,_/_.___/_/\___/_/ + /____/ + +EOF +} + +header_info + +function msg_info() { echo -e "${INFO} ${YW}${1}...${CL}"; } +function msg_ok() { echo -e "${CM} ${GN}${1}${CL}"; } +function msg_error() { echo -e "${CROSS} ${RD}${1}${CL}"; } +function msg_warn() { echo -e "${WARN} ${YWB}${1}"; } + +# Check for root privileges +if [ "$(id -u)" -ne 0 ]; then + msg_error "Error: This script must be run as root." + exit 1 +fi + +if ! command -v ethtool >/dev/null 2>&1; then + msg_info "Installing ethtool" + apt-get update &>/dev/null + apt-get install -y ethtool &>/dev/null || { msg_error "Failed to install ethtool. Exiting."; exit 1; } + msg_ok "ethtool installed successfully" +fi + +# Get list of network interfaces using Intel e1000e driver +INTERFACES=() +COUNT=0 + +msg_info "Searching for Intel e1000e interfaces" + +for device in /sys/class/net/*; do + interface="$(basename "$device")" # or adjust the rest of the usages below, as mostly you'll use the path anyway + # Skip loopback interface and virtual interfaces + if [[ "$interface" != "lo" ]] && [[ ! "$interface" =~ ^(tap|fwbr|veth|vmbr|bonding_masters) ]]; then + # Check if the interface uses the e1000e driver + driver=$(basename $(readlink -f /sys/class/net/$interface/device/driver 2>/dev/null) 2>/dev/null) + + if [[ "$driver" == "e1000e" ]]; then + # Get MAC address for additional identification + mac=$(cat /sys/class/net/$interface/address 2>/dev/null) + INTERFACES+=("$interface" "Intel e1000e NIC ($mac)") + ((COUNT++)) + fi + fi +done + +# Check if any Intel e1000e interfaces were found +if [ ${#INTERFACES[@]} -eq 0 ]; then + whiptail --title "Error" --msgbox "No Intel e1000e network interfaces found!" 10 60 + msg_error "No Intel e1000e network interfaces found! Exiting." + exit 1 +fi + +msg_ok "Found ${BL}$COUNT${GN} Intel e1000e interfaces" + +# Create a checklist for interface selection with all interfaces initially checked +INTERFACES_CHECKLIST=() +for ((i=0; i<${#INTERFACES[@]}; i+=2)); do + INTERFACES_CHECKLIST+=("${INTERFACES[i]}" "${INTERFACES[i+1]}" "ON") +done + +# Show interface selection checklist +SELECTED_INTERFACES=$(whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Network Interfaces" \ + --separate-output --checklist "Select Intel e1000e network interfaces\n(Space to toggle, Enter to confirm):" 15 80 6 \ + "${INTERFACES_CHECKLIST[@]}" 3>&1 1>&2 2>&3) + +exitstatus=$? +if [ $exitstatus != 0 ]; then + msg_info "User canceled. Exiting." + exit 0 +fi + +# Check if any interfaces were selected +if [ -z "$SELECTED_INTERFACES" ]; then + msg_error "No interfaces selected. Exiting." + exit 0 +fi + +# Convert the selected interfaces into an array +readarray -t INTERFACE_ARRAY <<< "$SELECTED_INTERFACES" + +# Show the number of selected interfaces +INTERFACE_COUNT=${#INTERFACE_ARRAY[@]} + +# Print selected interfaces +for iface in "${INTERFACE_ARRAY[@]}"; do + msg_ok "Selected interface: ${BL}$iface${CL}" +done + +# Ask for confirmation with the list of selected interfaces +CONFIRMATION_MSG="You have selected the following interface(s):\n\n" +for iface in "${INTERFACE_ARRAY[@]}"; do + SPEED=$(cat /sys/class/net/$iface/speed 2>/dev/null) + MAC=$(cat /sys/class/net/$iface/address 2>/dev/null) + CONFIRMATION_MSG+="- $iface (MAC: $MAC, Speed: ${SPEED}Mbps)\n" +done +CONFIRMATION_MSG+="\nThis will create systemd service(s) to disable offloading features.\n\nProceed?" + +if ! whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Confirmation" \ + --yesno "$CONFIRMATION_MSG" 20 80; then + msg_info "User canceled. Exiting." + exit 0 +fi + +# Loop through all selected interfaces and create services for each +for SELECTED_INTERFACE in "${INTERFACE_ARRAY[@]}"; do + # Create service name for this interface + SERVICE_NAME="disable-nic-offload-$SELECTED_INTERFACE.service" + SERVICE_PATH="/etc/systemd/system/$SERVICE_NAME" + + # Create the service file with e1000e specific optimizations + msg_info "Creating systemd service for interface: ${BL}$SELECTED_INTERFACE${YW}" + + # Start with the common part of the service file + cat > "$SERVICE_PATH" << EOF +[Unit] +Description=Disable NIC offloading for Intel e1000e interface $SELECTED_INTERFACE +After=network.target + +[Service] +Type=oneshot +# Disable all offloading features for Intel e1000e +ExecStart=/sbin/ethtool -K $SELECTED_INTERFACE gso off gro off tso off tx off rx off rxvlan off txvlan off sg off +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target +EOF + + # Check if service file was created successfully + if [ ! -f "$SERVICE_PATH" ]; then + whiptail --title "Error" --msgbox "Failed to create service file for $SELECTED_INTERFACE!" 10 50 + msg_error "Failed to create service file for $SELECTED_INTERFACE! Skipping to next interface." + continue + fi + + # Configure this service + { + echo "25"; sleep 0.2 + # Reload systemd to recognize the new service + systemctl daemon-reload + echo "50"; sleep 0.2 + # Start the service + systemctl start "$SERVICE_NAME" + echo "75"; sleep 0.2 + # Enable the service to start on boot + systemctl enable "$SERVICE_NAME" + echo "100"; sleep 0.2 + } | whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --gauge "Configuring service for $SELECTED_INTERFACE..." 10 80 0 + + # Individual service status + if systemctl is-active --quiet "$SERVICE_NAME"; then + SERVICE_STATUS="Active" + else + SERVICE_STATUS="Inactive" + fi + + if systemctl is-enabled --quiet "$SERVICE_NAME"; then + BOOT_STATUS="Enabled" + else + BOOT_STATUS="Disabled" + fi + + # Show individual service results + msg_ok "Service for ${BL}$SELECTED_INTERFACE${GN} created and enabled!" + msg_info "${TAB}Service: ${BL}$SERVICE_NAME${YW}" + msg_info "${TAB}Status: ${BL}$SERVICE_STATUS${YW}" + msg_info "${TAB}Start on boot: ${BL}$BOOT_STATUS${YW}" +done + +# Prepare summary of all interfaces +SUMMARY_MSG="Services created successfully!\n\n" +SUMMARY_MSG+="Configured Interfaces:\n" + +for iface in "${INTERFACE_ARRAY[@]}"; do + SERVICE_NAME="disable-nic-offload-$iface.service" + if systemctl is-active --quiet "$SERVICE_NAME"; then + SVC_STATUS="Active" + else + SVC_STATUS="Inactive" + fi + + if systemctl is-enabled --quiet "$SERVICE_NAME"; then + BOOT_SVC_STATUS="Enabled" + else + BOOT_SVC_STATUS="Disabled" + fi + + SUMMARY_MSG+="- $iface: $SVC_STATUS, Boot: $BOOT_SVC_STATUS\n" +done + +# Show summary results +whiptail --backtitle "Intel e1000e NIC Offloading Disabler" --title "Success" --msgbox "$SUMMARY_MSG" 20 80 + +msg_ok "Intel e1000e optimization complete for ${#INTERFACE_ARRAY[@]} interface(s)!" + +exit 0 From 142fea21bd936afe41b03bad5e37b09ddc6f8cb5 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 05:59:43 +0100 Subject: [PATCH 011/139] Update CHANGELOG.md (#5168) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f077a51bf..410485416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-16 +### 🌐 Website + + - #### 📝 Script Information + + - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) + ## 2025-06-15 ### 🆕 New Scripts From e6c21811bb2f27c3e976ace33e57b8822fbf691b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Mon, 16 Jun 2025 07:31:22 +0200 Subject: [PATCH 012/139] Firefly: Add Data Importer to LXC (#5159) * Add Data Importer to Firefly LXC * Remove useless var --- ct/firefly.sh | 2 +- frontend/public/json/firefly.json | 4 ++++ install/firefly-install.sh | 28 ++++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ct/firefly.sh b/ct/firefly.sh index 1471f3fb2..1c646e3aa 100644 --- a/ct/firefly.sh +++ b/ct/firefly.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: quantumryuu +# Author: quantumryuu | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://firefly-iii.org/ diff --git a/frontend/public/json/firefly.json b/frontend/public/json/firefly.json index 4c8d232f8..cff7e2b9f 100644 --- a/frontend/public/json/firefly.json +++ b/frontend/public/json/firefly.json @@ -32,6 +32,10 @@ "password": null }, "notes": [ + { + "text": "Data Importer is at `http:///dataimporter/`", + "type": "info" + }, { "text": "Database credentials: `cat ~/firefly.creds`", "type": "info" diff --git a/install/firefly-install.sh b/install/firefly-install.sh index c10e9ca8e..82082d76b 100644 --- a/install/firefly-install.sh +++ b/install/firefly-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: quantumryuu +# Author: quantumryuu | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://firefly-iii.org/ @@ -30,7 +30,6 @@ msg_info "Setting up database" DB_NAME=firefly DB_USER=firefly DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -MYSQL_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1) $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" @@ -43,6 +42,7 @@ mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRI msg_ok "Set up database" msg_info "Installing Firefly III (Patience)" +LOCAL_IP=$(hostname -I | awk '{print $1}') RELEASE=$(curl -fsSL https://api.github.com/repos/firefly-iii/firefly-iii/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') cd /opt curl -fsSL "https://github.com/firefly-iii/firefly-iii/releases/download/v${RELEASE}/FireflyIII-v${RELEASE}.tar.gz" -o "FireflyIII-v${RELEASE}.tar.gz" @@ -61,6 +61,14 @@ $STD php artisan firefly:upgrade-database $STD php artisan firefly:correct-database $STD php artisan firefly:report-integrity $STD php artisan firefly:laravel-passport-keys +IMPORTER_RELEASE=$(curl -fsSL https://api.github.com/repos/firefly-iii/data-importer/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') +mkdir -p /opt/firefly/dataimporter +cd /opt +curl -fsSL "https://github.com/firefly-iii/data-importer/releases/download/v${IMPORTER_RELEASE}/DataImporter-v${IMPORTER_RELEASE}.tar.gz" -o "DataImporter-v${IMPORTER_RELEASE}.tar.gz" +tar -xzf "DataImporter-v${IMPORTER_RELEASE}.tar.gz" -C /opt/firefly/dataimporter +cp /opt/firefly/dataimporter/.env.example /opt/firefly/dataimporter/.env +sed -i "s#FIREFLY_III_URL=#FIREFLY_III_URL=http://${LOCAL_IP}#g" /opt/firefly/dataimporter/.env +chown -R www-data:www-data /opt/firefly echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Installed Firefly III" @@ -75,7 +83,18 @@ cat </etc/apache2/sites-available/firefly.conf AllowOverride All Require all granted - + + Alias /dataimporter/ /opt/firefly/dataimporter/public/ + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + SetHandler application/x-httpd-php + + ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined @@ -93,7 +112,8 @@ motd_ssh customize msg_info "Cleaning up" -rm -rf /opt/FireflyIII-v${RELEASE}.tar.gz +rm -rf "/opt/FireflyIII-v${RELEASE}.tar.gz" +rm -rf "/opt/DataImporter-v${IMPORTER_RELEASE}.tar.gz" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 5ee48aba1e4f5d7bcde98d223e767eebb1ae3ef4 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 06:32:05 +0100 Subject: [PATCH 013/139] Update CHANGELOG.md (#5170) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 410485416..3728c684d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-16 +### 🚀 Updated Scripts + + - Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) + ### 🌐 Website - #### 📝 Script Information From 6e1ee7d16e590605a1562164024872f4e3a869d6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 16 Jun 2025 07:40:21 +0200 Subject: [PATCH 014/139] Refactor: IP-Tag (#5152) --- frontend/public/json/add-iptag.json | 49 ++++ frontend/public/json/add-lxc-iptag.json | 44 --- {misc => tools/pve}/add-iptag.sh | 0 tools/pve/add-lxc-iptag.sh | 357 ------------------------ 4 files changed, 49 insertions(+), 401 deletions(-) create mode 100644 frontend/public/json/add-iptag.json delete mode 100644 frontend/public/json/add-lxc-iptag.json rename {misc => tools/pve}/add-iptag.sh (100%) delete mode 100644 tools/pve/add-lxc-iptag.sh diff --git a/frontend/public/json/add-iptag.json b/frontend/public/json/add-iptag.json new file mode 100644 index 000000000..3b23d543c --- /dev/null +++ b/frontend/public/json/add-iptag.json @@ -0,0 +1,49 @@ + +{ + "name": "Proxmox VE LXC IP-Tag", + "slug": "add-iptag", + "categories": [ + 1 + ], + "date_created": "2025-06-15", + "type": "addon", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/proxmox.svg", + "config_path": "", + "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/add-iptag.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" + }, + { + "text": "Configuration: `nano /opt/iptag/iptag.conf`. iptag.service must be restarted after change.", + "type": "info" + }, + { + "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", + "type": "warning" + } + ] +} diff --git a/frontend/public/json/add-lxc-iptag.json b/frontend/public/json/add-lxc-iptag.json deleted file mode 100644 index 4f3b148b7..000000000 --- a/frontend/public/json/add-lxc-iptag.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "Proxmox VE LXC IP-Tag", - "slug": "add-lxc-iptag", - "categories": [ - 1 - ], - "date_created": "2024-12-16", - "type": "pve", - "updateable": false, - "privileged": false, - "interface_port": null, - "documentation": null, - "website": null, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": "/opt/lxc-iptag/iptag.conf", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", - "install_methods": [ - { - "type": "default", - "script": "tools/pve/add-lxc-iptag.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Execute within the Proxmox shell", - "type": "info" - }, - { - "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", - "type": "warning" - } - ] -} diff --git a/misc/add-iptag.sh b/tools/pve/add-iptag.sh similarity index 100% rename from misc/add-iptag.sh rename to tools/pve/add-iptag.sh diff --git a/tools/pve/add-lxc-iptag.sh b/tools/pve/add-lxc-iptag.sh deleted file mode 100644 index 88732be66..000000000 --- a/tools/pve/add-lxc-iptag.sh +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/gitsang/lxc-iptag - -function header_info { - clear - cat <<"EOF" - __ _ ________ ________ ______ - / / | |/ / ____/ / _/ __ \ /_ __/___ _____ _ - / / | / / / // /_/ /_____/ / / __ `/ __ `/ - / /___/ / /___ _/ // ____/_____/ / / /_/ / /_/ / -/_____/_/|_\____/ /___/_/ /_/ \__,_/\__, / - /____/ -EOF -} - -clear -header_info -APP="LXC IP-Tag" -hostname=$(hostname) - -# Farbvariablen -YW=$(echo "\033[33m") -GN=$(echo "\033[1;92m") -RD=$(echo "\033[01;31m") -CL=$(echo "\033[m") -BFR="\\r\\033[K" -HOLD=" " -CM=" ✔️ ${CL}" -CROSS=" ✖️ ${CL}" - -# This function enables error handling in the script by setting options and defining a trap for the ERR signal. -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. -error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - 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" -} - -# This function displays a spinner. -spinner() { - local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -while true; do - read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn - case $yn in - [Yy]*) break ;; - [Nn]*) - msg_error "Installation cancelled." - exit - ;; - *) msg_error "Please answer yes or no." ;; - esac -done - -if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later." - msg_error "Exiting..." - sleep 2 - exit -fi - -FILE_PATH="/usr/local/bin/iptag" -if [[ -f "$FILE_PATH" ]]; then - msg_info "The file already exists: '$FILE_PATH'. Skipping installation." - exit 0 -fi - -msg_info "Installing Dependencies" -apt-get update &>/dev/null -apt-get install -y ipcalc net-tools &>/dev/null -msg_ok "Installed Dependencies" - -msg_info "Setting up IP-Tag Scripts" -mkdir -p /opt/lxc-iptag -msg_ok "Setup IP-Tag Scripts" - -msg_info "Setup Default Config" -if [[ ! -f /opt/lxc-iptag/iptag.conf ]]; then - cat </opt/lxc-iptag/iptag.conf -# Configuration file for LXC IP tagging - -# List of allowed CIDRs -CIDR_LIST=( - 192.168.0.0/16 - 172.16.0.0/12 - 10.0.0.0/8 - 100.64.0.0/10 -) - -# Interval settings (in seconds) -LOOP_INTERVAL=60 -FW_NET_INTERFACE_CHECK_INTERVAL=60 -LXC_STATUS_CHECK_INTERVAL=-1 -FORCE_UPDATE_INTERVAL=1800 -EOF - msg_ok "Setup default config" -else - msg_ok "Default config already exists" -fi - -msg_info "Setup Main Function" -if [[ ! -f /opt/lxc-iptag/iptag ]]; then - cat <<'EOF' >/opt/lxc-iptag/iptag -#!/bin/bash - -# =============== CONFIGURATION =============== # - -CONFIG_FILE="/opt/lxc-iptag/iptag.conf" - -# Load the configuration file if it exists -if [ -f "$CONFIG_FILE" ]; then - # shellcheck source=./lxc-iptag.conf - source "$CONFIG_FILE" -fi - -# Convert IP to integer for comparison -ip_to_int() { - local ip="${1}" - local a b c d - - IFS=. read -r a b c d <<< "${ip}" - echo "$((a << 24 | b << 16 | c << 8 | d))" -} - -# Check if IP is in CIDR -ip_in_cidr() { - local ip="${1}" - local cidr="${2}" - - ip_int=$(ip_to_int "${ip}") - netmask_int=$(ip_to_int "$(ipcalc -b "${cidr}" | grep Broadcast | awk '{print $2}')") - masked_ip_int=$(( "${ip_int}" & "${netmask_int}" )) - [[ ${ip_int} -eq ${masked_ip_int} ]] && return 0 || return 1 -} - -# Check if IP is in any CIDRs -ip_in_cidrs() { - local ip="${1}" - local cidrs=() - - mapfile -t cidrs < <(echo "${2}" | tr ' ' '\n') - for cidr in "${cidrs[@]}"; do - ip_in_cidr "${ip}" "${cidr}" && return 0 - done - - return 1 -} - -# Check if IP is valid -is_valid_ipv4() { - local ip=$1 - local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" - - if [[ $ip =~ $regex ]]; then - IFS='.' read -r -a parts <<< "$ip" - for part in "${parts[@]}"; do - if ! [[ $part =~ ^[0-9]+$ ]] || ((part < 0 || part > 255)); then - return 1 - fi - done - return 0 - else - return 1 - fi -} - -lxc_status_changed() { - current_lxc_status=$(pct list 2>/dev/null) - if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then - return 1 - else - last_lxc_status="${current_lxc_status}" - return 0 - fi -} - -fw_net_interface_changed() { - current_net_interface=$(ifconfig | grep "^fw") - if [ "${last_net_interface}" == "${current_net_interface}" ]; then - return 1 - else - last_net_interface="${current_net_interface}" - return 0 - fi -} - -# =============== MAIN =============== # - -update_lxc_iptags() { - vmid_list=$(pct list 2>/dev/null | grep -v VMID | awk '{print $1}') - for vmid in ${vmid_list}; do - last_tagged_ips=() - current_valid_ips=() - next_tags=() - - # Parse current tags - mapfile -t current_tags < <(pct config "${vmid}" | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for current_tag in "${current_tags[@]}"; do - if is_valid_ipv4 "${current_tag}"; then - last_tagged_ips+=("${current_tag}") - continue - fi - next_tags+=("${current_tag}") - done - - # Get current IPs - current_ips_full=$(lxc-info -n "${vmid}" -i | awk '{print $2}') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}" && ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - current_valid_ips+=("${ip}") - next_tags+=("${ip}") - fi - done - - # Skip if no ip change - if [[ "$(echo "${last_tagged_ips[@]}" | tr ' ' '\n' | sort -u)" == "$(echo "${current_valid_ips[@]}" | tr ' ' '\n' | sort -u)" ]]; then - echo "Skipping ${vmid} cause ip no changes" - continue - fi - - # Set tags - echo "Setting ${vmid} tags from ${current_tags[*]} to ${next_tags[*]}" - pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" - done -} - -check() { - current_time=$(date +%s) - - time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_lxc_status_check}" -ge "${STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking lxc status..." - last_lxc_status_check_time=${current_time} - if lxc_status_changed; then - update_lxc_iptags - last_update_time=${current_time} - return - fi - fi - - time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking fw net interface..." - last_fw_net_interface_check_time=${current_time} - if fw_net_interface_changed; then - update_lxc_iptags - last_update_time=${current_time} - return - fi - fi - - time_since_last_update=$((current_time - last_update_time)) - if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating lxc iptags..." - update_lxc_iptags - last_update_time=${current_time} - return - fi -} - -# main: Set the IP tags for all LXC containers -main() { - while true; do - check - sleep "${LOOP_INTERVAL}" - done -} - -main -EOF - msg_ok "Setup Main Function" -else - msg_ok "Main Function already exists" -fi -chmod +x /opt/lxc-iptag/iptag - -msg_info "Creating Service" -if [[ ! -f /lib/systemd/system/iptag.service ]]; then - cat </lib/systemd/system/iptag.service -[Unit] -Description=LXC IP-Tag service -After=network.target - -[Service] -Type=simple -ExecStart=/opt/lxc-iptag/iptag -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - msg_ok "Created Service" -else - msg_ok "Service already exists." -fi - -msg_ok "Setup IP-Tag Scripts" - -msg_info "Starting Service" -systemctl daemon-reload &>/dev/null -systemctl enable -q --now iptag.service &>/dev/null -msg_ok "Started Service" -SPINNER_PID="" -echo -e "\n${APP} installation completed successfully! ${CL}\n" From 7fc39a442b8c3c24238554e891d99181bf8dad80 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 06:40:57 +0100 Subject: [PATCH 015/139] Update date in json (#5171) Co-authored-by: GitHub Actions --- frontend/public/json/add-iptag.json | 91 ++++++++++++++--------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/frontend/public/json/add-iptag.json b/frontend/public/json/add-iptag.json index 3b23d543c..d96fee0fa 100644 --- a/frontend/public/json/add-iptag.json +++ b/frontend/public/json/add-iptag.json @@ -1,49 +1,48 @@ - { - "name": "Proxmox VE LXC IP-Tag", - "slug": "add-iptag", - "categories": [ - 1 - ], - "date_created": "2025-06-15", - "type": "addon", - "updateable": false, - "privileged": false, - "interface_port": null, - "documentation": null, - "website": null, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/proxmox.svg", - "config_path": "", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", - "install_methods": [ - { - "type": "default", - "script": "tools/pve/add-iptag.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null + "name": "Proxmox VE LXC IP-Tag", + "slug": "add-iptag", + "categories": [ + 1 + ], + "date_created": "2025-06-16", + "type": "addon", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/proxmox.svg", + "config_path": "", + "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/add-iptag.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" }, - "notes": [ - { - "text": "Execute within the Proxmox shell", - "type": "info" - }, - { - "text": "Configuration: `nano /opt/iptag/iptag.conf`. iptag.service must be restarted after change.", - "type": "info" - }, - { - "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", - "type": "warning" - } - ] + { + "text": "Configuration: `nano /opt/iptag/iptag.conf`. iptag.service must be restarted after change.", + "type": "info" + }, + { + "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", + "type": "warning" + } + ] } From 0bf641f02ce454feddd12fe532c20c289a55fdff Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 06:41:00 +0100 Subject: [PATCH 016/139] Update CHANGELOG.md (#5172) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3728c684d..baf764bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🌐 Website + - Refactor: IP-Tag [@MickLesk](https://github.com/MickLesk) ([#5152](https://github.com/community-scripts/ProxmoxVE/pull/5152)) + - #### 📝 Script Information - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) From 5671d8e957747e0816f66b7a590b580c84ad82be Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 06:42:19 +0100 Subject: [PATCH 017/139] Update CHANGELOG.md (#5173) Co-authored-by: github-actions[bot] From 9f26e25126160df5e0b056c3938eafa958143a33 Mon Sep 17 00:00:00 2001 From: Omar Minaya Date: Mon, 16 Jun 2025 04:16:51 -0400 Subject: [PATCH 018/139] Kasm: Storing Creds Fix (#5162) * this should do it. * Update kasm-install.sh --------- Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- install/kasm-install.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/install/kasm-install.sh b/install/kasm-install.sh index b1d7913aa..9dda36c72 100644 --- a/install/kasm-install.sh +++ b/install/kasm-install.sh @@ -16,17 +16,25 @@ update_os msg_info "Installing Kasm Workspaces" KASM_VERSION=$(curl -fsSL 'https://www.kasmweb.com/downloads' | grep -o 'https://kasm-static-content.s3.amazonaws.com/kasm_release_[^"]*\.tar\.gz' | head -n 1 | sed -E 's/.*release_(.*)\.tar\.gz/\1/') curl -fsSL -o "/opt/kasm_release_${KASM_VERSION}.tar.gz" "https://kasm-static-content.s3.amazonaws.com/kasm_release_${KASM_VERSION}.tar.gz" -tar -xf "/opt/kasm_release_${KASM_VERSION}.tar.gz" -C /opt -printf 'y\ny\ny\n4\n' | bash /opt/kasm_release/install.sh -touch ~/kasm-install.output -sed -n '/Kasm UI Login Credentials/,$p' ~/kasm-install.output >~/kasm.creds + +cd /opt +tar -xf "kasm_release_${KASM_VERSION}.tar.gz" +chmod +x /opt/kasm_release/install.sh +printf 'y\ny\ny\n4\n' | bash /opt/kasm_release/install.sh > ~/kasm-install.output 2>&1 +cat ~/kasm-install.output | grep -A 20 -i "credentials\|login\|password\|admin" | sed '1i Kasm-Workspaces-Credentials' >~/kasm.creds + msg_ok "Installed Kasm Workspaces" motd_ssh customize +msg_info "Displaying Kasm Credentials" +cat ~/kasm.creds +msg_ok "Kasm Credentials displayed" + msg_info "Cleaning up" -$STD rm -f "/opt/kasm_release_${KASM_VERSION}.tar.gz" +rm -f /opt/kasm_release_${KASM_VERSION}.tar.gz +rm -f ~/kasm-install.output $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From b001687f5ce73c3aafe6844fec92e3d38e43d9fd Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 09:17:21 +0100 Subject: [PATCH 019/139] Update CHANGELOG.md (#5174) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baf764bb4..ecb2db18c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,17 +16,17 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-16 +### 🆕 New Scripts + + - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) + ### 🚀 Updated Scripts - Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) -### 🌐 Website + - #### 🐞 Bug Fixes - - Refactor: IP-Tag [@MickLesk](https://github.com/MickLesk) ([#5152](https://github.com/community-scripts/ProxmoxVE/pull/5152)) - - - #### 📝 Script Information - - - Intel NIC offload Fix by @rcastley [@MickLesk](https://github.com/MickLesk) ([#5155](https://github.com/community-scripts/ProxmoxVE/pull/5155)) + - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) ## 2025-06-15 From 04dab68e17bd9dc59f216936b1fdf74a8726022b Mon Sep 17 00:00:00 2001 From: Bram Suurd <78373894+BramSuurdje@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:46:05 +0200 Subject: [PATCH 020/139] Update default image asset in the public directory and update api route to only search for files that end with .json (#5179) * Update default image asset in the public directory * Enhance file filtering in getScripts function to include only JSON files --- frontend/public/defaultimg.png | Bin 77312 -> 112228 bytes frontend/src/app/api/categories/route.ts | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/public/defaultimg.png b/frontend/public/defaultimg.png index 4aac77d5c835d4f5b0242329b9f8bb0e142faf07..33d118a27d3e2041eb26b5c6a36bce44753929c4 100644 GIT binary patch literal 112228 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%0D>ugj49)stb(zdo> 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 Vq>?np9G~B76(u#rlBaJ!{14`HX&wLo literal 77312 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�%8kbxKQLgho=Qq=*ZMXfrpL+N6 z{_~zc=UnET^L)?qJm)#T=XsoqC)vhqGX=M_kC-D9{IdHMC&ku#GWfmjM7H`cpTZuO z!PC-?O+WW+q0tqSG}Zerck+rMiUvnK^j4_fNPwd*@73y5-n~(gVSTGODEG zym8rWgIY!lo2_~a}crTNAbg)Qj%>! zdYHRnxj_z(BShLu65q;ex&W7_Gh33n#MFuzE%$oKbCw}o zUoNAnc~W|GA6=&-q{>QqV4ValRQ)5drVjp@5oP)$y|nh|nxi*rREX1(-;w=(a$?oe z`Td?__LWz-kAEUUH0aY~PqJpK7hZ->|L%45aGXipc&|2>Q~BD5Xu5>gZf5!}K2ur8 z3SH#a+)>X7+Abg$KN{5G)4zpsG%Fln`yTmzwq20mJ&(M3APPMC*TZnJlS zh!>I(W{uN~PtveMggh-|>#~kWifyh}jHypM*#rD&|>M+tA%pV})Px zf}k1*bFlT+Ub=0mvCbN!hSr^Ieg}y1CuNzWGjDy^lPeMx= z03|THBJs}*kxFSeF;x&YLz^5%S>8r((;rW4y*>PE-N-|OKxG|wRS*m z;&b6vL2|FlzDE8(a$YgzVOv{@{PHZAHo;Bz$g=vdrK!u`D>}+;$xI9N=dWjWRn~^g zb6|ZWH@;Af`Adxp?Szmk3so#J(Al*ApjQR>oUBSvHw+`FOA@pf9TgcxX6xUpcI5-0 zUzYF#@&&3DQrsbdhhreupk!HyEmA#AsdKX2AuCEN z&_+sJ&}Lj-trjU$QnFW6RJv8i`?VgoKJeH5y~4RLpdB>Z_}(Zqle5@_@efCqu4nfo zOlkyfB*w2>kVac9ua1J`TS~#$mrxLKT%t&gGN1pp&n;GWZp{B8o*fg@noKiM+&S3h zwjplGL|_N&h71c^uv<8TTk+FU zJJdT-1nov*phXcS7f*bVX=RLd4Q$$r&?50DeF$sh)l+u77MNI31r9HQYOwNO_^4w2 z|Ni@q+BXOQC^&gkR8)x)`WM1l479rw^Yfcs+VVM(QBh6%$Ke`MqJ>pU3#zXd7Pbzw zv0bY_ekR0Je^d<|N zeb+Sgs}6kVj9dA#_NH-evF3-Y65;**PO6TzYg&}1(TW%!P=V188}!Sm6qZHpP0GE`oq|oJ?uR?zA`MPl3gMxAj1wK9C>FLSS#eTp^&HEgD^xJ}mt{(`Pr6rJ0 z2`gg3TgFvXof|f(+LbTKv3vHfPB;=+3D%35iiy>9zm6xuSr=kkFb8>6X=&+{j11P1 zB8oajKj?u?S;~-IkF^Yl}x-nxV9A1B#?>cJijaPR*2#XaIz1m0hO6Hj{fUy|o7nuvPguml1 zi0&6a?sLo}4hJeKE7@Fj92*ifupuMidGk>8K@L{*Ze+tY*A-L!u%l}GbTKkybjH8L z%nPVVbt)0dl&5hV; zLT_du*u=1xMwmOWw{x@kh`q(`pymT>S)!n|f9L-ItHM40JVrD%f4gUY4^nd^Y9&gb+=3`EG&er^!iXEIHb1AHXX7qt%saJ3!zm@ zTYa;8zD*@$gIM| z8*anTXa(?A71u-eZ { const getScripts = async () => { const filePaths = (await fs.readdir(jsonDir)) - .filter((fileName) => fileName !== metadataFileName && fileName !== versionFileName) + .filter((fileName) => + fileName.endsWith(".json") && + fileName !== metadataFileName && + fileName !== versionFileName + ) .map((fileName) => path.resolve(jsonDir, fileName)); const scripts = await Promise.all( From e97c96d0bbed01c1b14228f724babe3ba4008a0b Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:46:32 +0100 Subject: [PATCH 021/139] Update CHANGELOG.md (#5180) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb2db18c..52c7a3034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,12 @@ All LXC instances created using this repository come pre-installed with Midnight - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) +### 🌐 Website + + - #### 🐞 Bug Fixes + + - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) + ## 2025-06-15 ### 🆕 New Scripts From d21cbf1d4087365b572bba685e7015803439f85e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:07:47 +0200 Subject: [PATCH 022/139] Update versions.json (#5181) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 70 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 91345afe5..8070d51e1 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,39 @@ [ + { + "name": "Graylog2/graylog2-server", + "version": "6.3.0-rc.1", + "date": "2025-06-16T11:28:08Z" + }, + { + "name": "bunkerity/bunkerweb", + "version": "v1.6.2-rc5", + "date": "2025-06-16T10:26:18Z" + }, + { + "name": "home-assistant/operating-system", + "version": "15.2", + "date": "2025-04-14T15:37:12Z" + }, + { + "name": "moghtech/komodo", + "version": "v1.18.3", + "date": "2025-06-16T07:03:46Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2020", + "date": "2025-06-16T05:56:11Z" + }, + { + "name": "esphome/esphome", + "version": "2025.5.2", + "date": "2025-06-03T08:45:14Z" + }, + { + "name": "firefly-iii/firefly-iii", + "version": "v6.2.17", + "date": "2025-06-11T12:07:38Z" + }, { "name": "jellyfin/jellyfin", "version": "v10.10.7", @@ -49,11 +84,6 @@ "version": "v5.26.2.10099", "date": "2025-06-11T20:10:39Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2017", - "date": "2025-06-15T05:54:05Z" - }, { "name": "traccar/traccar", "version": "v6.7.3", @@ -74,11 +104,6 @@ "version": "v25.0", "date": "2025-05-12T09:12:04Z" }, - { - "name": "bunkerity/bunkerweb", - "version": "v1.6.2-rc5", - "date": "2025-06-14T16:44:14Z" - }, { "name": "ollama/ollama", "version": "v0.9.1-rc1", @@ -94,11 +119,6 @@ "version": "v2.15.0", "date": "2025-06-14T10:48:57Z" }, - { - "name": "firefly-iii/firefly-iii", - "version": "v6.2.17", - "date": "2025-06-11T12:07:38Z" - }, { "name": "runtipi/runtipi", "version": "v4.2.1", @@ -194,11 +214,6 @@ "version": "2.0.0-alpha-4", "date": "2025-05-14T05:01:45Z" }, - { - "name": "esphome/esphome", - "version": "2025.5.2", - "date": "2025-06-03T08:45:14Z" - }, { "name": "MediaBrowser/Emby.Releases", "version": "4.8.11.0", @@ -279,11 +294,6 @@ "version": "v1.131.0", "date": "2025-06-03T14:13:00Z" }, - { - "name": "Graylog2/graylog2-server", - "version": "6.3.0-beta.5", - "date": "2025-06-10T11:19:42Z" - }, { "name": "OctoPrint/OctoPrint", "version": "1.11.2", @@ -384,11 +394,6 @@ "version": "v1.10.0", "date": "2025-06-07T08:31:48Z" }, - { - "name": "moghtech/komodo", - "version": "v1.18.1", - "date": "2025-06-07T06:25:20Z" - }, { "name": "OliveTin/OliveTin", "version": "2025.6.6", @@ -904,11 +909,6 @@ "version": "0.22.5", "date": "2025-04-15T02:52:26Z" }, - { - "name": "home-assistant/operating-system", - "version": "15.2", - "date": "2025-04-14T15:37:12Z" - }, { "name": "Tautulli/Tautulli", "version": "v2.15.2", From 2a4353fa13327db18d25ef5b4fcd90106e9f0124 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:20:46 +0200 Subject: [PATCH 023/139] [core] Move install_php() from VED to main (#5182) --- misc/tools.func | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index f636db10d..4f64e9e14 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -380,45 +380,44 @@ install_php() { # Deduplicate modules COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - local CURRENT_PHP + local CURRENT_PHP="" if command -v php >/dev/null 2>&1; then CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - else - CURRENT_PHP="" fi if [[ -z "$CURRENT_PHP" ]]; then - msg_info "Setup PHP $PHP_VERSION" + msg_info "No PHP found, setting up PHP $PHP_VERSION" elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then msg_info "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" - if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/php.list - $STD apt-get update - fi - $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true fi + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/php.list + $STD apt-get update + fi + local MODULE_LIST="php${PHP_VERSION}" IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do MODULE_LIST+=" php${PHP_VERSION}-${mod}" done + if [[ "$PHP_FPM" == "YES" ]]; then MODULE_LIST+=" php${PHP_VERSION}-fpm" fi - if [[ "$PHP_APACHE" == "YES" ]]; then - # Optionally disable old Apache PHP module + if [[ "$PHP_APACHE" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then $STD a2dismod php${CURRENT_PHP} || true fi fi - if [[ "$PHP_FPM" == "YES" ]]; then + if [[ "$PHP_FPM" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then $STD systemctl stop php${CURRENT_PHP}-fpm || true $STD systemctl disable php${CURRENT_PHP}-fpm || true fi @@ -436,8 +435,7 @@ install_php() { fi # Patch all relevant php.ini files - local PHP_INI_PATHS=() - PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/cli/php.ini") + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") @@ -452,7 +450,6 @@ install_php() { fi done } - # ------------------------------------------------------------------------------ # Installs or updates Composer globally. # From 8576463522538a1009c8d29714ced2a67181e0f6 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:21:14 +0100 Subject: [PATCH 024/139] Update CHANGELOG.md (#5183) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c7a3034..bd27cc216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts - - Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) + - [core] Move install_php() from VED to main [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#5182](https://github.com/community-scripts/ProxmoxVE/pull/5182)) +- Firefly: Add Data Importer to LXC [@tremor021](https://github.com/tremor021) ([#5159](https://github.com/community-scripts/ProxmoxVE/pull/5159)) - #### 🐞 Bug Fixes From fb4075ac123aca8b35878f4ee68440da096650de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:40:12 +0200 Subject: [PATCH 025/139] Fix missing dependencies (#5185) --- install/wastebin-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/wastebin-install.sh b/install/wastebin-install.sh index 98bf7b2fc..d6d6fb7c4 100644 --- a/install/wastebin-install.sh +++ b/install/wastebin-install.sh @@ -13,6 +13,10 @@ setting_up_container network_check update_os +msg_info "Installing dependencies" +$STD apt-get install -y zstd +msg_ok "Installed dependencies" + msg_info "Installing Wastebin" temp_file=$(mktemp) RELEASE=$(curl -fsSL https://api.github.com/repos/matze/wastebin/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') From d850dde4ef3f05ed5ca36036da9a89a4cbd98b64 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:40:49 +0100 Subject: [PATCH 026/139] Update CHANGELOG.md (#5186) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd27cc216..3e0cc393a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Wastebin: Fix missing dependencies [@tremor021](https://github.com/tremor021) ([#5185](https://github.com/community-scripts/ProxmoxVE/pull/5185)) - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) ### 🌐 Website From ec8a8b19089500015527d96f2f0ebfc5c8f6611f Mon Sep 17 00:00:00 2001 From: Koen Date: Mon, 16 Jun 2025 16:01:27 +0200 Subject: [PATCH 027/139] add optional Cloud-init support to Debian VM script (#5137) * Edit Debian VM script to use Cloud-init * Edit Debian VM page to point to Cloud-init guide * Revert "Edit Debian VM page to point to Cloud-init guide" This reverts commit 3f99b2def15690e3a88fd5fa10b8ceefb8867bab. * Edit Debian VM script to ask for cloud-init --- vm/debian-vm.sh | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/vm/debian-vm.sh b/vm/debian-vm.sh index d99fe39e4..746a534e8 100644 --- a/vm/debian-vm.sh +++ b/vm/debian-vm.sh @@ -57,6 +57,7 @@ MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e @@ -191,6 +192,7 @@ function default_settings() { VLAN="" MTU="" START_VM="yes" + CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" @@ -204,6 +206,7 @@ function default_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" } @@ -373,6 +376,14 @@ function advanced_settings() { exit-script fi + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" + CLOUD_INIT="yes" + else + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" + CLOUD_INIT="no" + fi + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" @@ -439,7 +450,11 @@ 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 Debian 12 Qcow2 Disk Image" -URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 +if [ "$CLOUD_INIT" == "yes" ]; then + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 +else + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 +fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -474,11 +489,20 @@ qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null +if [ "$CLOUD_INIT" == "yes" ]; then + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -scsi1 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +else + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +fi DESCRIPTION=$( cat < From 5e649d7cb12e33181c2235a935e6d408d23cfa56 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:02:08 +0100 Subject: [PATCH 028/139] Update CHANGELOG.md (#5188) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e0cc393a..608be70b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ All LXC instances created using this repository come pre-installed with Midnight - Wastebin: Fix missing dependencies [@tremor021](https://github.com/tremor021) ([#5185](https://github.com/community-scripts/ProxmoxVE/pull/5185)) - Kasm: Storing Creds Fix [@omiinaya](https://github.com/omiinaya) ([#5162](https://github.com/community-scripts/ProxmoxVE/pull/5162)) + - #### ✨ New Features + + - add optional Cloud-init support to Debian VM script [@koendiender](https://github.com/koendiender) ([#5137](https://github.com/community-scripts/ProxmoxVE/pull/5137)) + ### 🌐 Website - #### 🐞 Bug Fixes From 13b1c684eea3e2ce46d50a5eafc5e6f28ae75dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:00:14 +0200 Subject: [PATCH 029/139] Refactor: 2FAuth (#5184) * Refactor: 2fauth * Update --- ct/2fauth.sh | 25 +++---------------------- install/2fauth-install.sh | 31 +++++++------------------------ 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/ct/2fauth.sh b/ct/2fauth.sh index 9dc9fc541..f16c854fb 100644 --- a/ct/2fauth.sh +++ b/ct/2fauth.sh @@ -24,21 +24,16 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder if [[ ! -d "/opt/2fauth" ]]; then msg_error "No ${APP} Installation Found!" exit fi - - # Crawling the new version and checking whether an update is required RELEASE=$(curl -fsSL https://api.github.com/repos/Bubka/2FAuth/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/2fauth_version.txt)" ]] || [[ ! -f /opt/2fauth_version.txt ]]; then msg_info "Updating $APP to ${RELEASE}" - $STD apt-get update $STD apt-get -y upgrade - # Creating Backup msg_info "Creating Backup" mv "/opt/2fauth" "/opt/2fauth-backup" if ! dpkg -l | grep -q 'php8.3'; then @@ -46,37 +41,24 @@ function update_script() { fi msg_ok "Backup Created" - # Upgrade PHP if ! dpkg -l | grep -q 'php8.3'; then $STD apt-get install -y \ lsb-release \ - gpg - curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/deb.sury.org-php.gpg - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" >/etc/apt/sources.list.d/php.list - $STD apt-get update - $STD apt-get install -y php8.3-{bcmath,common,ctype,curl,fileinfo,fpm,gd,mbstring,mysql,xml,cli,intl} + gnupg2 + PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" install_php sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf fi - - # Execute Update - curl -fsSL -o "${RELEASE}.zip" "https://github.com/Bubka/2FAuth/archive/refs/tags/${RELEASE}.zip" - $STD unzip "${RELEASE}.zip" - mv "2FAuth-${RELEASE//v/}/" "/opt/2fauth" + fetch_and_deploy_gh_release "Bubka/2FAuth" mv "/opt/2fauth-backup/.env" "/opt/2fauth/.env" mv "/opt/2fauth-backup/storage" "/opt/2fauth/storage" cd "/opt/2fauth" || return - chown -R www-data: "/opt/2fauth" chmod -R 755 "/opt/2fauth" - export COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev --prefer-source - php artisan 2fauth:install - $STD systemctl restart nginx - # Cleaning up msg_info "Cleaning Up" rm -rf "v${RELEASE}.zip" if dpkg -l | grep -q 'php8.2'; then @@ -86,7 +68,6 @@ function update_script() { $STD apt-get -y autoclean msg_ok "Cleanup Completed" - # Last Action echo "${RELEASE}" >/opt/2fauth_version.txt msg_ok "Updated $APP to ${RELEASE}" else diff --git a/install/2fauth-install.sh b/install/2fauth-install.sh index f32f55457..291d52ca1 100644 --- a/install/2fauth-install.sh +++ b/install/2fauth-install.sh @@ -15,17 +15,12 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - lsb-release -curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/deb.sury.org-php.gpg -echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" >/etc/apt/sources.list.d/php.list -$STD apt-get update - -$STD apt-get install -y \ - nginx \ - composer \ - php8.3-{bcmath,common,ctype,curl,fileinfo,fpm,gd,intl,mbstring,mysql,xml,cli} + lsb-release \ + nginx msg_ok "Installed Dependencies" +PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" install_php +install_composer install_mariadb msg_info "Setting up Database" @@ -43,16 +38,12 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/2FAuth.creds msg_ok "Set up Database" -msg_info "Setup 2FAuth" -RELEASE=$(curl -fsSL https://api.github.com/repos/Bubka/2FAuth/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL "https://github.com/Bubka/2FAuth/archive/refs/tags/${RELEASE}.zip" -o "${RELEASE}.zip" -$STD unzip "${RELEASE}.zip" -mv "2FAuth-${RELEASE//v/}/" /opt/2fauth +fetch_and_deploy_gh_release "Bubka/2FAuth" -cd "/opt/2fauth" || return +msg_info "Setup 2FAuth" +cd /opt/2fauth cp .env.example .env IPADDRESS=$(hostname -I | awk '{print $1}') - sed -i -e "s|^APP_URL=.*|APP_URL=http://$IPADDRESS|" \ -e "s|^DB_CONNECTION=$|DB_CONNECTION=mysql|" \ -e "s|^DB_DATABASE=$|DB_DATABASE=$DB_NAME|" \ @@ -60,22 +51,16 @@ sed -i -e "s|^APP_URL=.*|APP_URL=http://$IPADDRESS|" \ -e "s|^DB_PORT=$|DB_PORT=3306|" \ -e "s|^DB_USERNAME=$|DB_USERNAME=$DB_USER|" \ -e "s|^DB_PASSWORD=$|DB_PASSWORD=$DB_PASS|" .env - export COMPOSER_ALLOW_SUPERUSER=1 $STD composer update --no-plugins --no-scripts $STD composer install --no-dev --prefer-source --no-plugins --no-scripts - $STD php artisan key:generate --force - $STD php artisan migrate:refresh $STD php artisan passport:install -q -n $STD php artisan storage:link $STD php artisan config:cache - chown -R www-data: /opt/2fauth chmod -R 755 /opt/2fauth - -echo "${RELEASE}" >"/opt/2fauth_version.txt" msg_ok "Setup 2fauth" msg_info "Configure Service" @@ -107,7 +92,6 @@ server { } } EOF - systemctl reload nginx msg_ok "Configured Service" @@ -115,7 +99,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -f "/opt/v${RELEASE}.zip" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From c7e0faa33bc6aa01afe8d410f80b5e463220786a Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:00:57 +0100 Subject: [PATCH 030/139] Update CHANGELOG.md (#5192) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 608be70b8..bdba3e69b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ All LXC instances created using this repository come pre-installed with Midnight - add optional Cloud-init support to Debian VM script [@koendiender](https://github.com/koendiender) ([#5137](https://github.com/community-scripts/ProxmoxVE/pull/5137)) + - #### 🔧 Refactor + + - Refactor: 2FAuth [@tremor021](https://github.com/tremor021) ([#5184](https://github.com/community-scripts/ProxmoxVE/pull/5184)) + ### 🌐 Website - #### 🐞 Bug Fixes From 15772d880261c5ea7e4ec03a7b2471e93937b57d Mon Sep 17 00:00:00 2001 From: Bram Suurd <78373894+BramSuurdje@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:01:18 +0200 Subject: [PATCH 031/139] Update default image asset in the public directory (#5189) * Update default image asset in the public directory * update background to be blue instead of black --- frontend/public/defaultimg.png | Bin 112228 -> 122822 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/public/defaultimg.png b/frontend/public/defaultimg.png index 33d118a27d3e2041eb26b5c6a36bce44753929c4..14d13fdba2a282a4714e460925be1245bb4068b2 100644 GIT binary patch literal 122822 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 literal 112228 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%0D>ugj49)stb(zdo> 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 Vq>?np9G~B76(u#rlBaJ!{14`HX&wLo From ccad7a2cd84c3db9544ab26462cfe41505f7066c Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:01:33 +0100 Subject: [PATCH 032/139] Update CHANGELOG.md (#5193) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdba3e69b..1d17c25b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,10 @@ All LXC instances created using this repository come pre-installed with Midnight - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) + - #### ✨ New Features + + - Update default image asset in the public directory [@BramSuurdje](https://github.com/BramSuurdje) ([#5189](https://github.com/community-scripts/ProxmoxVE/pull/5189)) + ## 2025-06-15 ### 🆕 New Scripts From 0c17402efc6ad58a0d5a216b42fba959ea86ed58 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:02:00 +0100 Subject: [PATCH 033/139] Update CHANGELOG.md (#5194) Co-authored-by: github-actions[bot] From 0b3c645ffb5e31a9b6e6c01f4f8284cb21b2bf7e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:03:00 +0200 Subject: [PATCH 034/139] Update .app files (#5191) Co-authored-by: GitHub Actions --- tools/headers/add-iptag | 6 ++++++ tools/headers/add-lxc-iptag | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 tools/headers/add-iptag delete mode 100644 tools/headers/add-lxc-iptag diff --git a/tools/headers/add-iptag b/tools/headers/add-iptag new file mode 100644 index 000000000..153ccd369 --- /dev/null +++ b/tools/headers/add-iptag @@ -0,0 +1,6 @@ + ________ ______ + / _/ __ \ /_ __/___ _____ _ + / // /_/ /_____/ / / __ `/ __ `/ + _/ // ____/_____/ / / /_/ / /_/ / +/___/_/ /_/ \__,_/\__, / + /____/ diff --git a/tools/headers/add-lxc-iptag b/tools/headers/add-lxc-iptag deleted file mode 100644 index 766517bf2..000000000 --- a/tools/headers/add-lxc-iptag +++ /dev/null @@ -1,6 +0,0 @@ - __ _ ________ ________ ______ - / / | |/ / ____/ / _/ __ \ /_ __/___ _____ _ - / / | / / / // /_/ /_____/ / / __ `/ __ `/ - / /___/ / /___ _/ // ____/_____/ / / /_/ / /_/ / -/_____/_/|_\____/ /___/_/ /_/ \__,_/\__, / - /____/ From 8241ed932d14a68d048b889bcc03ccaeb54d950a Mon Sep 17 00:00:00 2001 From: Bram Suurd <78373894+BramSuurdje@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:13:33 +0200 Subject: [PATCH 035/139] Refactor layout and component styles for improved responsiveness (#5195) --- frontend/src/app/layout.tsx | 2 +- frontend/src/app/scripts/_components/ScriptAccordion.tsx | 2 +- frontend/src/app/scripts/_components/ScriptItem.tsx | 4 ++-- frontend/src/app/scripts/_components/Sidebar.tsx | 4 ++-- frontend/src/app/scripts/page.tsx | 2 +- frontend/src/components/Navbar.tsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 9a341ccfb..07cb6e2a1 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -104,7 +104,7 @@ export default function RootLayout({
-
+
{children} diff --git a/frontend/src/app/scripts/_components/ScriptAccordion.tsx b/frontend/src/app/scripts/_components/ScriptAccordion.tsx index 23616877b..e4190e552 100644 --- a/frontend/src/app/scripts/_components/ScriptAccordion.tsx +++ b/frontend/src/app/scripts/_components/ScriptAccordion.tsx @@ -56,7 +56,7 @@ export default function ScriptAccordion({ value={expandedItem} onValueChange={handleAccordionChange} collapsible - className="overflow-y-scroll max-h-[calc(100vh-225px)] overflow-x-hidden mt-3 p-2" + className="overflow-y-scroll max-h-[calc(100vh-225px)] overflow-x-hidden p-2" > {items.map((category) => ( +

Selected Script

@@ -132,7 +132,7 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
-
+
}> diff --git a/frontend/src/app/scripts/_components/Sidebar.tsx b/frontend/src/app/scripts/_components/Sidebar.tsx index 3680210fa..38b7a3547 100644 --- a/frontend/src/app/scripts/_components/Sidebar.tsx +++ b/frontend/src/app/scripts/_components/Sidebar.tsx @@ -22,7 +22,7 @@ const Sidebar = ({ }, [] as Script[]); return ( -
+

Categories

@@ -40,4 +40,4 @@ const Sidebar = ({ ); }; -export default Sidebar; \ No newline at end of file +export default Sidebar; diff --git a/frontend/src/app/scripts/page.tsx b/frontend/src/app/scripts/page.tsx index 590b67358..32d207bbc 100644 --- a/frontend/src/app/scripts/page.tsx +++ b/frontend/src/app/scripts/page.tsx @@ -47,7 +47,7 @@ function ScriptContent() { setSelectedScript={setSelectedScript} />

-
+
{selectedScript && item ? ( ) : ( diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index ac0d8ba02..8ede326c0 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -34,7 +34,7 @@ function Navbar() { isScrolled ? "glass border-b bg-background/50" : "" }`} > -
+
Date: Mon, 16 Jun 2025 16:14:01 +0100 Subject: [PATCH 036/139] Update CHANGELOG.md (#5198) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d17c25b5..789ff45c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🌐 Website + - Refactor layout and component styles for improved responsiveness [@BramSuurdje](https://github.com/BramSuurdje) ([#5195](https://github.com/community-scripts/ProxmoxVE/pull/5195)) + - #### 🐞 Bug Fixes - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) From 906a0a0fbc8172297dfee42aecd5a9ebe2d61ec4 Mon Sep 17 00:00:00 2001 From: Bram Suurd <78373894+BramSuurdje@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:14:17 +0200 Subject: [PATCH 037/139] Refactor ScriptItem and ConfigFile components to conditionally render config file location. Update ConfigFile to accept configPath prop instead of item. (#5197) --- .../app/scripts/_components/ScriptItem.tsx | 24 ++++++++++--------- .../_components/ScriptItems/ConfigFile.tsx | 5 ++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/scripts/_components/ScriptItem.tsx b/frontend/src/app/scripts/_components/ScriptItem.tsx index 5471c04f6..7b46205fd 100644 --- a/frontend/src/app/scripts/_components/ScriptItem.tsx +++ b/frontend/src/app/scripts/_components/ScriptItem.tsx @@ -15,12 +15,12 @@ import { ResourceDisplay } from "./ResourceDisplay"; import { getDisplayValueFromType } from "./ScriptInfoBlocks"; import Alerts from "./ScriptItems/Alerts"; import Buttons from "./ScriptItems/Buttons"; +import ConfigFile from "./ScriptItems/ConfigFile"; import DefaultPassword from "./ScriptItems/DefaultPassword"; import Description from "./ScriptItems/Description"; import InstallCommand from "./ScriptItems/InstallCommand"; import InterFaces from "./ScriptItems/InterFaces"; import Tooltips from "./ScriptItems/Tooltips"; -import ConfigFile from "./ScriptItems/ConfigFile"; interface ScriptItemProps { item: Script; @@ -152,16 +152,18 @@ export function ScriptItem({ item, setSelectedScript }: ScriptItemProps) {
- -
-

- Location of config file -

-
- -
- -
+ {item.config_path && ( + <> + +
+

Location of config file

+
+ +
+ +
+ + )}
diff --git a/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx b/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx index 0f4f43fb0..d11ed24d2 100644 --- a/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx +++ b/frontend/src/app/scripts/_components/ScriptItems/ConfigFile.tsx @@ -1,10 +1,9 @@ import ConfigCopyButton from "@/components/ui/config-copy-button"; -import { Script } from "@/lib/types"; -export default function ConfigFile({ item }: { item: Script }) { +export default function ConfigFile({ configPath }: { configPath: string }) { return (
- {item.config_path ? item.config_path : "No config path set"} + {configPath}
); } From 9b700195e47de42ca0c898b8cacfc0ac8a21adb9 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:14:35 +0100 Subject: [PATCH 038/139] Update CHANGELOG.md (#5199) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 789ff45c4..027472449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Refactor ScriptItem and ConfigFile components to conditionally render config file location. Update ConfigFile to accept configPath prop instead of item. [@BramSuurdje](https://github.com/BramSuurdje) ([#5197](https://github.com/community-scripts/ProxmoxVE/pull/5197)) - Update default image asset in the public directory and update api route to only search for files that end with .json [@BramSuurdje](https://github.com/BramSuurdje) ([#5179](https://github.com/community-scripts/ProxmoxVE/pull/5179)) - #### ✨ New Features From 4057fadfe00eff7278b42a3d9a2c32d387e713f6 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:15:16 +0100 Subject: [PATCH 039/139] Update CHANGELOG.md (#5200) Co-authored-by: github-actions[bot] From a648b9abc41d235fd50b58697e70f985de2c00ef Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:16:24 +0200 Subject: [PATCH 040/139] Change wrong Type of iptag --- frontend/public/json/add-iptag.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/add-iptag.json b/frontend/public/json/add-iptag.json index d96fee0fa..bc042fb55 100644 --- a/frontend/public/json/add-iptag.json +++ b/frontend/public/json/add-iptag.json @@ -5,7 +5,7 @@ 1 ], "date_created": "2025-06-16", - "type": "addon", + "type": "pve", "updateable": false, "privileged": false, "interface_port": null, From 6d7d15ce46c76a976eb04ddabf8538495a5ca4a5 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 02:14:58 +0200 Subject: [PATCH 041/139] Update versions.json (#5208) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 104 ++++++++++++++--------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 8070d51e1..c433481df 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,14 +1,59 @@ [ + { + "name": "kimai/kimai", + "version": "2.36.1", + "date": "2025-06-16T19:20:54Z" + }, + { + "name": "bunkerity/bunkerweb", + "version": "testing", + "date": "2025-06-16T18:10:42Z" + }, + { + "name": "msgbyte/tianji", + "version": "v1.21.14", + "date": "2025-06-16T17:54:48Z" + }, + { + "name": "goauthentik/authentik", + "version": "version/2025.6.2", + "date": "2025-06-16T17:54:39Z" + }, + { + "name": "NodeBB/NodeBB", + "version": "v2.8.20", + "date": "2025-06-16T17:03:52Z" + }, + { + "name": "emqx/emqx", + "version": "e5.9.1-alpha.1", + "date": "2025-06-16T15:34:01Z" + }, + { + "name": "open-webui/open-webui", + "version": "v0.6.15", + "date": "2025-06-16T14:34:42Z" + }, + { + "name": "grokability/snipe-it", + "version": "v8.1.16", + "date": "2025-06-16T13:49:37Z" + }, + { + "name": "clusterzx/paperless-ai", + "version": "v3.0.5", + "date": "2025-06-16T13:25:55Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.95.3", + "date": "2025-06-03T11:09:42Z" + }, { "name": "Graylog2/graylog2-server", "version": "6.3.0-rc.1", "date": "2025-06-16T11:28:08Z" }, - { - "name": "bunkerity/bunkerweb", - "version": "v1.6.2-rc5", - "date": "2025-06-16T10:26:18Z" - }, { "name": "home-assistant/operating-system", "version": "15.2", @@ -121,8 +166,8 @@ }, { "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" + "version": "nightly", + "date": "2025-06-14T07:49:50Z" }, { "name": "FlareSolverr/FlareSolverr", @@ -159,11 +204,6 @@ "version": "v12.0.1+security-01", "date": "2025-06-13T04:15:18Z" }, - { - "name": "msgbyte/tianji", - "version": "v1.21.13", - "date": "2025-06-13T04:11:30Z" - }, { "name": "FlowiseAI/Flowise", "version": "flowise@3.0.2", @@ -179,11 +219,6 @@ "version": "v2.3.0p34", "date": "2025-06-12T12:15:44Z" }, - { - "name": "n8n-io/n8n", - "version": "n8n@1.95.3", - "date": "2025-06-03T11:09:42Z" - }, { "name": "docker/compose", "version": "v2.37.1", @@ -229,11 +264,6 @@ "version": "0.50.3", "date": "2025-06-11T15:19:52Z" }, - { - "name": "kimai/kimai", - "version": "2.36.0", - "date": "2025-06-11T12:31:07Z" - }, { "name": "cloudflare/cloudflared", "version": "2025.6.0", @@ -269,11 +299,6 @@ "version": "server/public/v0.1.15", "date": "2025-06-11T03:56:25Z" }, - { - "name": "emqx/emqx", - "version": "e5.10.0", - "date": "2025-06-10T16:03:18Z" - }, { "name": "node-red/node-red", "version": "4.1.0-beta.1", @@ -284,11 +309,6 @@ "version": "v0.107.62", "date": "2025-05-27T12:10:19Z" }, - { - "name": "open-webui/open-webui", - "version": "v0.6.14", - "date": "2025-06-10T14:18:04Z" - }, { "name": "element-hq/synapse", "version": "v1.131.0", @@ -349,11 +369,6 @@ "version": "v3.3.0", "date": "2025-06-09T15:58:04Z" }, - { - "name": "NodeBB/NodeBB", - "version": "v2.8.19", - "date": "2025-06-09T15:32:25Z" - }, { "name": "seanmorley15/AdventureLog", "version": "v0.10.0", @@ -404,11 +419,6 @@ "version": "v4.1.2", "date": "2025-06-06T17:44:58Z" }, - { - "name": "goauthentik/authentik", - "version": "version/2025.6.1", - "date": "2025-06-06T15:28:21Z" - }, { "name": "ioBroker/ioBroker", "version": "2025-05-31", @@ -504,11 +514,6 @@ "version": "1.26.3", "date": "2025-06-02T22:00:14Z" }, - { - "name": "grokability/snipe-it", - "version": "v8.1.15", - "date": "2025-06-02T17:38:24Z" - }, { "name": "inventree/InvenTree", "version": "0.17.13", @@ -664,11 +669,6 @@ "version": "3.2.0-rc2", "date": "2025-05-21T20:09:07Z" }, - { - "name": "clusterzx/paperless-ai", - "version": "v3.0.4", - "date": "2025-05-21T19:03:53Z" - }, { "name": "ipfs/kubo", "version": "v0.35.0", From 795fe72768ff8c83518438a4982f29b9d29c5b01 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 01:15:30 +0100 Subject: [PATCH 042/139] Update CHANGELOG.md (#5209) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 027472449..b937350c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-17 + ## 2025-06-16 ### 🆕 New Scripts From 4400cd2b97498dd2ad67ebdcc48979f0f8381a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Tue, 17 Jun 2025 13:45:20 +0200 Subject: [PATCH 043/139] Add .env (#5216) --- install/libretranslate-install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/install/libretranslate-install.sh b/install/libretranslate-install.sh index 6779f0397..2b83b1bdf 100644 --- a/install/libretranslate-install.sh +++ b/install/libretranslate-install.sh @@ -43,6 +43,10 @@ $STD uv pip install "numpy<2" $STD uv pip install . $STD uv pip install libretranslate $STD .venv/bin/python scripts/install_models.py + +cat </opt/libretranslate/.env +LT_PORT=5000 +EOF msg_ok "Installed LibreTranslate" msg_info "Creating Service" @@ -56,6 +60,7 @@ User=root Type=idle Restart=always Environment="PATH=/usr/local/lib/python3.11/dist-packages/libretranslate" +EnvironmentFile=/opt/libretranslate/.env ExecStart=/opt/libretranslate/.venv/bin/python3 /opt/libretranslate/.venv/bin/libretranslate --host * --update-models ExecReload=/bin/kill -s HUP KillMode=mixed From 1691fafcc1f3e72d83afe4334d975697bef2054a Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 12:45:47 +0100 Subject: [PATCH 044/139] Update CHANGELOG.md (#5218) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b937350c7..061aed7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-17 +### 🚀 Updated Scripts + + - #### ✨ New Features + + - LibreTranslate: Add .env for easier configuration [@tremor021](https://github.com/tremor021) ([#5216](https://github.com/community-scripts/ProxmoxVE/pull/5216)) + ## 2025-06-16 ### 🆕 New Scripts From 7e046d830dba3ec0b1ef8d890078d81473aa80bd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:01:46 +0200 Subject: [PATCH 045/139] IPTag: Better explanation (#5213) --- frontend/public/json/add-iptag.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/json/add-iptag.json b/frontend/public/json/add-iptag.json index bc042fb55..7fd9fd35a 100644 --- a/frontend/public/json/add-iptag.json +++ b/frontend/public/json/add-iptag.json @@ -1,5 +1,5 @@ { - "name": "Proxmox VE LXC IP-Tag", + "name": "Proxmox VE LXC Tag", "slug": "add-iptag", "categories": [ 1 @@ -12,8 +12,8 @@ "documentation": null, "website": null, "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/proxmox.svg", - "config_path": "", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "config_path": "/opt/iptag/iptag.conf", + "description": "This script automatically adds IP address as tags to LXC containers or VM's using a systemd service. The service also updates the tags if a LXC/VM IP address is changed.", "install_methods": [ { "type": "default", From 8ac24981a6d216b2c2380534805d8711f6ce5dcd Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 13:02:31 +0100 Subject: [PATCH 046/139] Update CHANGELOG.md (#5219) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 061aed7df..2e6aba3c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ All LXC instances created using this repository come pre-installed with Midnight - LibreTranslate: Add .env for easier configuration [@tremor021](https://github.com/tremor021) ([#5216](https://github.com/community-scripts/ProxmoxVE/pull/5216)) +### 🌐 Website + + - #### 📝 Script Information + + - IPTag: Better explanation [@MickLesk](https://github.com/MickLesk) ([#5213](https://github.com/community-scripts/ProxmoxVE/pull/5213)) + ## 2025-06-16 ### 🆕 New Scripts From ef9ca4847719581c33fce107c1d3cac0e3212cbc Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:07:50 +0200 Subject: [PATCH 047/139] Update versions.json (#5220) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 90 +++++++++++++++--------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index c433481df..592bae9de 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,39 @@ [ + { + "name": "glpi-project/glpi", + "version": "10.0.18", + "date": "2025-02-12T11:07:02Z" + }, + { + "name": "mattermost/mattermost", + "version": "v10.9.1", + "date": "2025-06-17T07:06:51Z" + }, + { + "name": "morpheus65535/bazarr", + "version": "v1.5.2", + "date": "2025-05-11T16:40:55Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2024", + "date": "2025-06-17T05:51:55Z" + }, + { + "name": "donaldzou/WGDashboard", + "version": "v4.2.4", + "date": "2025-06-17T05:37:06Z" + }, + { + "name": "webmin/webmin", + "version": "2.402", + "date": "2025-06-17T05:20:42Z" + }, + { + "name": "keycloak/keycloak", + "version": "26.2.5", + "date": "2025-05-28T06:49:43Z" + }, { "name": "kimai/kimai", "version": "2.36.1", @@ -19,6 +54,11 @@ "version": "version/2025.6.2", "date": "2025-06-16T17:54:39Z" }, + { + "name": "runtipi/runtipi", + "version": "v4.2.1", + "date": "2025-06-03T20:04:28Z" + }, { "name": "NodeBB/NodeBB", "version": "v2.8.20", @@ -64,11 +104,6 @@ "version": "v1.18.3", "date": "2025-06-16T07:03:46Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2020", - "date": "2025-06-16T05:56:11Z" - }, { "name": "esphome/esphome", "version": "2025.5.2", @@ -164,21 +199,11 @@ "version": "v2.15.0", "date": "2025-06-14T10:48:57Z" }, - { - "name": "runtipi/runtipi", - "version": "nightly", - "date": "2025-06-14T07:49:50Z" - }, { "name": "FlareSolverr/FlareSolverr", "version": "v3.3.25", "date": "2025-06-14T02:52:44Z" }, - { - "name": "keycloak/keycloak", - "version": "26.2.5", - "date": "2025-05-28T06:49:43Z" - }, { "name": "home-assistant/core", "version": "2025.6.1", @@ -194,16 +219,16 @@ "version": "3.0.6", "date": "2025-06-13T15:02:37Z" }, + { + "name": "grafana/grafana", + "version": "v11.4.6", + "date": "2025-06-13T14:11:41Z" + }, { "name": "jenkinsci/jenkins", "version": "jenkins-2.514", "date": "2025-06-10T14:27:57Z" }, - { - "name": "grafana/grafana", - "version": "v12.0.1+security-01", - "date": "2025-06-13T04:15:18Z" - }, { "name": "FlowiseAI/Flowise", "version": "flowise@3.0.2", @@ -234,11 +259,6 @@ "version": "v3.3.0", "date": "2025-06-12T06:54:48Z" }, - { - "name": "morpheus65535/bazarr", - "version": "v1.5.2", - "date": "2025-05-11T16:40:55Z" - }, { "name": "documenso/documenso", "version": "v1.12.0-rc.4", @@ -294,11 +314,6 @@ "version": "v0.15.0-rc2", "date": "2025-06-11T04:29:22Z" }, - { - "name": "mattermost/mattermost", - "version": "server/public/v0.1.15", - "date": "2025-06-11T03:56:25Z" - }, { "name": "node-red/node-red", "version": "4.1.0-beta.1", @@ -459,11 +474,6 @@ "version": "mariadb-11.8.2", "date": "2025-06-04T13:35:16Z" }, - { - "name": "donaldzou/WGDashboard", - "version": "v4.2.3", - "date": "2025-05-07T15:35:04Z" - }, { "name": "actualbudget/actual", "version": "v25.6.1", @@ -479,21 +489,11 @@ "version": "340", "date": "2025-06-04T16:41:44Z" }, - { - "name": "glpi-project/glpi", - "version": "10.0.18", - "date": "2025-02-12T11:07:02Z" - }, { "name": "louislam/uptime-kuma", "version": "2.0.0-beta.2-temp", "date": "2025-03-28T08:45:58Z" }, - { - "name": "webmin/webmin", - "version": "2.401", - "date": "2025-06-04T02:53:03Z" - }, { "name": "coder/code-server", "version": "v4.100.3", From 6d1d903345a0dae284de247c56d7eac91c928af8 Mon Sep 17 00:00:00 2001 From: TJ Date: Tue, 17 Jun 2025 13:35:17 +0100 Subject: [PATCH 048/139] trilium: fix update function after db changes folder (#5207) * Update trilium.sh Fixed issue with db location * Update ct/trilium.sh Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> * Update trilium.sh --------- Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- ct/trilium.sh | 77 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/ct/trilium.sh b/ct/trilium.sh index 4a54c9f69..dbb37cf84 100644 --- a/ct/trilium.sh +++ b/ct/trilium.sh @@ -29,38 +29,55 @@ function update_script() { fi if [[ ! -f /opt/${APP}_version.txt ]]; then touch /opt/${APP}_version.txt; fi RELEASE=$(curl -fsSL https://api.github.com/repos/TriliumNext/Notes/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "v${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "Stopping ${APP}" - systemctl stop trilium - sleep 1 - msg_ok "Stopped ${APP}" - - msg_info "Updating to ${RELEASE}" - mkdir -p /opt/trilium_backup - mv /opt/trilium/db /opt/trilium_backup/ - rm -rf /opt/trilium - cd /tmp - curl -fsSL "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" -o $(basename "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz") - tar -xf TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz - mv TriliumNextNotes-Server-$RELEASE-linux-x64 /opt/trilium - cp -r /opt/trilium_backup/db /opt/trilium/ - echo "v${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated to ${RELEASE}" - - msg_info "Cleaning up" - rm -rf /tmp/TriliumNextNotes-Server-${RELEASE}-linux-x64.tar.xz - rm -rf /opt/trilium_backup - msg_ok "Cleaned" - - msg_info "Starting ${APP}" - systemctl start trilium - sleep 1 - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" + if [[ "v${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + + if [[ -d /opt/trilium/db ]]; then + DB_PATH="/opt/trilium/db" + DB_RESTORE_PATH="/opt/trilium/db" + elif [[ -d /opt/trilium/assets/db ]]; then + DB_PATH="/opt/trilium/assets/db" + DB_RESTORE_PATH="/opt/trilium/assets/db" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_error "Database not found in either /opt/trilium/db or /opt/trilium/assets/db" + exit 1 fi - exit + + msg_info "Stopping ${APP}" + systemctl stop trilium + sleep 1 + msg_ok "Stopped ${APP}" + + msg_info "Updating to ${RELEASE}" + mkdir -p /opt/trilium_backup + cp -r "${DB_PATH}" /opt/trilium_backup/ + rm -rf /opt/trilium + cd /tmp + curl -fsSL "https://github.com/TriliumNext/Notes/releases/download/v${RELEASE}/TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" -o "TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" + tar -xf "TriliumNextNotes-Server-v${RELEASE}-linux-x64.tar.xz" + mv "TriliumNextNotes-Server-${RELEASE}-linux-x64" /opt/trilium + + # Restore database + mkdir -p "$(dirname "${DB_RESTORE_PATH}")" + cp -r /opt/trilium_backup/$(basename "${DB_PATH}") "${DB_RESTORE_PATH}" + + echo "v${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated to ${RELEASE}" + + msg_info "Cleaning up" + rm -rf "/tmp/TriliumNextNotes-Server-${RELEASE}-linux-x64.tar.xz" + rm -rf /opt/trilium_backup + msg_ok "Cleaned" + + msg_info "Starting ${APP}" + systemctl start trilium + sleep 1 + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" +else + msg_ok "No update required. ${APP} is already at ${RELEASE}" +fi + +exit } start From 219a7853e15ce49a3aecbf51d287686831a4bce0 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 13:36:02 +0100 Subject: [PATCH 049/139] Update CHANGELOG.md (#5221) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e6aba3c2..2e1114ee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts + - #### 🐞 Bug Fixes + + - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) + - #### ✨ New Features - LibreTranslate: Add .env for easier configuration [@tremor021](https://github.com/tremor021) ([#5216](https://github.com/community-scripts/ProxmoxVE/pull/5216)) From 40f083ea40e1cbdbcb8a6f185c5bd0ce660bd003 Mon Sep 17 00:00:00 2001 From: Desert Gamer <44316028+DesertGamer@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:56:27 +0300 Subject: [PATCH 050/139] Resolve issue #5212 (#5226) --- tools/pve/add-iptag.sh | 414 ++++++++++++++++++----------------------- 1 file changed, 186 insertions(+), 228 deletions(-) diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index c3bf88cf9..ba81b838e 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -531,302 +531,260 @@ if [[ ! -f /opt/iptag/iptag ]]; then cat <<'EOF' >/opt/iptag/iptag #!/bin/bash # =============== CONFIGURATION =============== # -CONFIG_FILE="/opt/iptag/iptag.conf" +readonly CONFIG_FILE="/opt/iptag/iptag.conf" +readonly DEFAULT_TAG_FORMAT="full" +readonly DEFAULT_CHECK_INTERVAL=60 # Load the configuration file if it exists if [ -f "$CONFIG_FILE" ]; then - # shellcheck source=./iptag.conf - source "$CONFIG_FILE" + # shellcheck source=./iptag.conf + source "$CONFIG_FILE" fi # Convert IP to integer for comparison ip_to_int() { - local ip="$1" - local a b c d - IFS=. read -r a b c d <<< "${ip}" - echo "$((a << 24 | b << 16 | c << 8 | d))" + local ip="$1" + local a b c d + IFS=. read -r a b c d <<< "${ip}" + echo "$((a << 24 | b << 16 | c << 8 | d))" } # Check if IP is in CIDR ip_in_cidr() { - local ip="$1" - local cidr="$2" + local ip="$1" cidr="$2" + ipcalc -c "$ip" "$cidr" >/dev/null 2>&1 || return 1 - # Use ipcalc with the -c option (check), which returns 0 if the IP is in the network - if ipcalc -c "$ip" "$cidr" >/dev/null 2>&1; then - # Get network address and mask from CIDR - local network prefix + local network prefix ip_parts net_parts network=$(echo "$cidr" | cut -d/ -f1) prefix=$(echo "$cidr" | cut -d/ -f2) + IFS=. read -r -a ip_parts <<< "$ip" + IFS=. read -r -a net_parts <<< "$network" - # Check if IP is in the network - local ip_a ip_b ip_c ip_d net_a net_b net_c net_d - IFS=. read -r ip_a ip_b ip_c ip_d <<< "$ip" - IFS=. read -r net_a net_b net_c net_d <<< "$network" - - # Check octets match based on prefix length - local result=0 - if (( prefix >= 8 )); then - [[ "$ip_a" != "$net_a" ]] && result=1 - fi - if (( prefix >= 16 )); then - [[ "$ip_b" != "$net_b" ]] && result=1 - fi - if (( prefix >= 24 )); then - [[ "$ip_c" != "$net_c" ]] && result=1 - fi - - return $result - fi - - return 1 + case $prefix in + 8) [[ "${ip_parts[0]}" == "${net_parts[0]}" ]] ;; + 16) [[ "${ip_parts[0]}.${ip_parts[1]}" == "${net_parts[0]}.${net_parts[1]}" ]] ;; + 24) [[ "${ip_parts[0]}.${ip_parts[1]}.${ip_parts[2]}" == "${net_parts[0]}.${net_parts[1]}.${net_parts[2]}" ]] ;; + 32) [[ "$ip" == "$network" ]] ;; + *) return 1 ;; + esac } # Format IP address according to the configuration format_ip_tag() { - local ip="$1" - local format="${TAG_FORMAT:-full}" + local ip="$1" + local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}" - case "$format" in - "last_octet") - echo "${ip##*.}" - ;; - "last_two_octets") - echo "${ip#*.*.}" - ;; - *) - echo "$ip" - ;; - esac + case "$format" in + "last_octet") echo "${ip##*.}" ;; + "last_two_octets") echo "${ip#*.*.}" ;; + *) echo "$ip" ;; + esac } # Check if IP is in any CIDRs ip_in_cidrs() { - local ip="$1" - local cidrs="$2" - - # Check that cidrs is not empty - [[ -z "$cidrs" ]] && return 1 - - local IFS=' ' - for cidr in $cidrs; do - ip_in_cidr "$ip" "$cidr" && return 0 - done - return 1 + local ip="$1" cidrs="$2" + [[ -z "$cidrs" ]] && return 1 + local IFS=' ' + for cidr in $cidrs; do + ip_in_cidr "$ip" "$cidr" && return 0 + done + return 1 } # Check if IP is valid is_valid_ipv4() { - local ip="$1" - [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 - local IFS='.' - read -ra parts <<< "$ip" - for part in "${parts[@]}"; do - [[ "$part" =~ ^[0-9]+$ ]] && ((part >= 0 && part <= 255)) || return 1 - done - return 0 -} - -lxc_status_changed() { - current_lxc_status=$(pct list 2>/dev/null) - if [ "${last_lxc_status}" == "${current_lxc_status}" ]; then - return 1 - else - last_lxc_status="${current_lxc_status}" + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + + local IFS='.' parts + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + (( part >= 0 && part <= 255 )) || return 1 + done return 0 - fi -} - -vm_status_changed() { - current_vm_status=$(qm list 2>/dev/null) - if [ "${last_vm_status}" == "${current_vm_status}" ]; then - return 1 - else - last_vm_status="${current_vm_status}" - return 0 - fi -} - -fw_net_interface_changed() { - current_net_interface=$(ifconfig | grep "^fw") - if [ "${last_net_interface}" == "${current_net_interface}" ]; then - return 1 - else - last_net_interface="${current_net_interface}" - return 0 - fi } # Get VM IPs using MAC addresses and ARP table get_vm_ips() { - local vmid=$1 - local ips="" + local vmid=$1 ips="" macs found_ip=false + qm status "$vmid" 2>/dev/null | grep -q "status: running" || return - # Check if VM is running - qm status "$vmid" 2>/dev/null | grep -q "status: running" || return + macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -oE '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + [[ -z "$macs" ]] && return - # Get MAC addresses from VM configuration - local macs - macs=$(qm config "$vmid" 2>/dev/null | grep -E 'net[0-9]+' | grep -o -E '[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}') + for mac in $macs; do + local ip + ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}') + [[ -n "$ip" ]] && { ips+="$ip "; found_ip=true; } + done - # Look up IPs from ARP table using MAC addresses - for mac in $macs; do - local ip - ip=$(arp -an 2>/dev/null | grep -i "$mac" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}') - if [ -n "$ip" ]; then - ips+="$ip " + if ! $found_ip; then + local agent_ip + agent_ip=$(qm agent "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' || true) + [[ -n "$agent_ip" ]] && ips+="$agent_ip " fi - done - echo "$ips" + echo "${ips% }" } # Update tags for container or VM update_tags() { - local type="$1" - local vmid="$2" - local config_cmd="pct" - [[ "$type" == "vm" ]] && config_cmd="qm" + local type="$1" vmid="$2" config_cmd="pct" + [[ "$type" == "vm" ]] && config_cmd="qm" - # Get current IPs - local current_ips_full - if [[ "$type" == "lxc" ]]; then - # Redirect error output to suppress AppArmor warnings - current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') - else - current_ips_full=$(get_vm_ips "${vmid}") - fi - - # Parse current tags and get valid IPs - local current_tags=() - local next_tags=() - mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') - - for tag in "${current_tags[@]}"; do - # Skip tag if it looks like an IP (full or partial) - if ! is_valid_ipv4 "${tag}" && ! [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then - next_tags+=("${tag}") + local current_ips_full + if [[ "$type" == "lxc" ]]; then + current_ips_full=$(lxc-info -n "${vmid}" -i 2>/dev/null | grep -E "^IP:" | awk '{print $2}') + else + current_ips_full=$(get_vm_ips "${vmid}") fi - done + [[ -z "$current_ips_full" ]] && return - # Add valid IPs to tags - local added_ips=() - local skipped_ips=() + local current_tags=() next_tags=() current_ip_tags=() + mapfile -t current_tags < <($config_cmd config "${vmid}" 2>/dev/null | grep tags | awk '{print $2}' | sed 's/;/\n/g') - for ip in ${current_ips_full}; do - if is_valid_ipv4 "${ip}"; then - if ip_in_cidrs "${ip}" "${CIDR_LIST[*]}"; then - local formatted_ip=$(format_ip_tag "$ip") - next_tags+=("${formatted_ip}") - added_ips+=("${formatted_ip}") - else - skipped_ips+=("${ip}") - fi + # Separate IP and non-IP tags + for tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + current_ip_tags+=("${tag}") + else + next_tags+=("${tag}") + fi + done + + local formatted_ips=() needs_update=false added_ips=() + for ip in ${current_ips_full}; do + if is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then + local formatted_ip=$(format_ip_tag "$ip") + formatted_ips+=("$formatted_ip") + if [[ ! " ${current_ip_tags[*]} " =~ " ${formatted_ip} " ]]; then + needs_update=true + added_ips+=("$formatted_ip") + next_tags+=("$formatted_ip") + fi + fi + done + + [[ ${#formatted_ips[@]} -eq 0 ]] && return + + # Add existing IP tags that are still valid + for tag in "${current_ip_tags[@]}"; do + if [[ " ${formatted_ips[*]} " =~ " ${tag} " ]]; then + if [[ ! " ${next_tags[*]} " =~ " ${tag} " ]]; then + next_tags+=("$tag") + fi + fi + done + + if [[ "$needs_update" == true ]]; then + echo "${type^} ${vmid}: adding IP tags: ${added_ips[*]}" + $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null fi - done +} - # Log only if there are changes - if [ ${#added_ips[@]} -gt 0 ]; then - echo "${type^} ${vmid}: added IP tags: ${added_ips[*]}" - fi +# Update all instances of specified type +update_all_tags() { + local type="$1" list_cmd="pct" vmids count=0 + [[ "$type" == "vm" ]] && list_cmd="qm" + + vmids=$($list_cmd list 2>/dev/null | grep -v VMID | awk '{print $1}') + for vmid in $vmids; do ((count++)); done + + echo "Found ${count} running ${type}s" + [[ $count -eq 0 ]] && return - # Update if changed - if [[ "$(IFS=';'; echo "${current_tags[*]}")" != "$(IFS=';'; echo "${next_tags[*]}")" ]]; then - $config_cmd set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null - fi + for vmid in $vmids; do + update_tags "$type" "$vmid" + done } # Check if status changed check_status_changed() { - local type="$1" - local current_status - - case "$type" in - "lxc") - current_status=$(pct list 2>/dev/null | grep -v VMID) - [[ "${last_lxc_status}" == "${current_status}" ]] && return 1 - last_lxc_status="${current_status}" - ;; - "vm") - current_status=$(qm list 2>/dev/null | grep -v VMID) - [[ "${last_vm_status}" == "${current_status}" ]] && return 1 - last_vm_status="${current_status}" - ;; - "fw") - current_status=$(ifconfig 2>/dev/null | grep "^fw") - [[ "${last_net_interface}" == "${current_status}" ]] && return 1 - last_net_interface="${current_status}" - ;; - esac - return 0 + local type="$1" current + case "$type" in + "lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;; + "vm") current=$(qm list 2>/dev/null | grep -v VMID) ;; + "fw") current=$(ifconfig 2>/dev/null | grep "^fw") ;; + esac + local last_var="last_${type}_status" + [[ "${!last_var}" == "$current" ]] && return 1 + eval "$last_var='$current'" + return 0 } +# Main check function check() { - current_time=$(date +%s) + local current_time changes_detected=false + current_time=$(date +%s) - # Check LXC status - time_since_last_lxc_status_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_lxc_status_check}" -ge "${LXC_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking LXC status..." - last_lxc_status_check_time=${current_time} - if check_status_changed "lxc"; then - update_all_tags "lxc" - last_update_lxc_time=${current_time} + # Check LXC status + local time_since_last_lxc_check=$((current_time - last_lxc_status_check_time)) + if [[ "${LXC_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_lxc_check}" -ge "${LXC_STATUS_CHECK_INTERVAL:-60}" ]]; then + echo "Checking LXC status..." + last_lxc_status_check_time=${current_time} + if check_status_changed "lxc"; then + changes_detected=true + update_all_tags "lxc" + last_update_lxc_time=${current_time} + fi fi - fi - # Check VM status - time_since_last_vm_status_check=$((current_time - last_vm_status_check_time)) - if [[ "${VM_STATUS_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_vm_status_check}" -ge "${VM_STATUS_CHECK_INTERVAL}" ]]; then - echo "Checking VM status..." - last_vm_status_check_time=${current_time} - if check_status_changed "vm"; then - update_all_tags "vm" - last_update_vm_time=${current_time} + # Check VM status + local time_since_last_vm_check=$((current_time - last_vm_status_check_time)) + if [[ "${VM_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_vm_check}" -ge "${VM_STATUS_CHECK_INTERVAL:-60}" ]]; then + echo "Checking VM status..." + last_vm_status_check_time=${current_time} + if check_status_changed "vm"; then + changes_detected=true + update_all_tags "vm" + last_update_vm_time=${current_time} + fi fi - fi - # Check network interface changes - time_since_last_fw_net_interface_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL}" -gt 0 ]] \ - && [[ "${time_since_last_fw_net_interface_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL}" ]]; then - echo "Checking network interfaces..." - last_fw_net_interface_check_time=${current_time} - if check_status_changed "fw"; then - update_all_tags "lxc" - update_all_tags "vm" - last_update_lxc_time=${current_time} - last_update_vm_time=${current_time} + # Check network interface changes + local time_since_last_fw_check=$((current_time - last_fw_net_interface_check_time)) + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "${time_since_last_fw_check}" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" ]]; then + echo "Checking network interfaces..." + last_fw_net_interface_check_time=${current_time} + if check_status_changed "fw"; then + changes_detected=true + update_all_tags "lxc" + update_all_tags "vm" + last_update_lxc_time=${current_time} + last_update_vm_time=${current_time} + fi fi - fi - # Force update if needed - for type in "lxc" "vm"; do - local last_update_var="last_update_${type}_time" - local time_since_last_update=$((current_time - ${!last_update_var})) - if [ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL} ]; then - echo "Force updating ${type} tags..." - update_all_tags "$type" - eval "${last_update_var}=${current_time}" - fi - done + # Force update if needed + for type in "lxc" "vm"; do + local last_update_var="last_update_${type}_time" + local time_since_last_update=$((current_time - ${!last_update_var})) + if [[ ${time_since_last_update} -ge ${FORCE_UPDATE_INTERVAL:-1800} ]]; then + echo "Force updating ${type} tags..." + changes_detected=true + update_all_tags "$type" + eval "${last_update_var}=${current_time}" + fi + done + + $changes_detected || echo "No changes detected in system status" } # Initialize time variables -last_lxc_status_check_time=0 -last_vm_status_check_time=0 -last_fw_net_interface_check_time=0 -last_update_lxc_time=0 -last_update_vm_time=0 +declare -g last_lxc_status="" last_vm_status="" last_fw_status="" +declare -g last_lxc_status_check_time=0 last_vm_status_check_time=0 last_fw_net_interface_check_time=0 +declare -g last_update_lxc_time=0 last_update_vm_time=0 -# main: Set the IP tags for all LXC containers and VMs +# Main loop main() { - while true; do - check - sleep "${LOOP_INTERVAL}" - done + while true; do + check + sleep "${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}" + done } main From 5773459a39488e7af3933a3deab042b1cabbbeca Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:57:05 +0100 Subject: [PATCH 051/139] Update CHANGELOG.md (#5228) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e1114ee4..3e60e22ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Update IP-Tag [@DesertGamer](https://github.com/DesertGamer) ([#5226](https://github.com/community-scripts/ProxmoxVE/pull/5226)) - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) - #### ✨ New Features From 2ea372f0345905e07cc590c66a1e42739e7bf7ca Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 17 Jun 2025 10:57:08 -0400 Subject: [PATCH 052/139] Immich: ensure in proper working dir when updating (#5227) --- ct/immich.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/immich.sh b/ct/immich.sh index c925c52b0..eddf23ae8 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -30,6 +30,7 @@ function update_script() { STAGING_DIR=/opt/staging BASE_DIR=${STAGING_DIR}/base-images SOURCE_DIR=${STAGING_DIR}/image-source + cd /root if [[ -f ~/.intel_version ]]; then curl -fsSLO https://raw.githubusercontent.com/immich-app/immich/refs/heads/main/machine-learning/Dockerfile readarray -t INTEL_URLS < <(sed -n "/intel/p" ./Dockerfile | awk '{print $3}') From 48da94f77aee0d3ecba2321345f78ab2b003a5df Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:57:35 +0100 Subject: [PATCH 053/139] Update CHANGELOG.md (#5229) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e60e22ba..a96e2894b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Immich: ensure in proper working dir when updating [@vhsdream](https://github.com/vhsdream) ([#5227](https://github.com/community-scripts/ProxmoxVE/pull/5227)) - Update IP-Tag [@DesertGamer](https://github.com/DesertGamer) ([#5226](https://github.com/community-scripts/ProxmoxVE/pull/5226)) - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) From 6520b7f4d4770e562a6fd7d2862fc556f0999526 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:57:51 +0100 Subject: [PATCH 054/139] Update CHANGELOG.md (#5230) Co-authored-by: github-actions[bot] From 4a1ae5144629f2d5b1f925661e42bd76dc0d7508 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 17 Jun 2025 23:06:06 +0200 Subject: [PATCH 055/139] gitea-mirror: increase build ressources (#5235) --- ct/gitea-mirror.sh | 6 +++--- frontend/public/json/gitea-mirror.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh index 9122e5f80..6ea7bd885 100644 --- a/ct/gitea-mirror.sh +++ b/ct/gitea-mirror.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="gitea-mirror" var_tags="${var_tags:-mirror;gitea}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-5}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/gitea-mirror.json b/frontend/public/json/gitea-mirror.json index 387c611b9..5be5cc905 100644 --- a/frontend/public/json/gitea-mirror.json +++ b/frontend/public/json/gitea-mirror.json @@ -19,9 +19,9 @@ "type": "default", "script": "ct/gitea-mirror.sh", "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 5, + "cpu": 2, + "ram": 2048, + "hdd": 6, "os": "Debian", "version": "12" } From 0d56db2d3d1464102eb75e1c61f87d84154042ef Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 22:06:45 +0100 Subject: [PATCH 056/139] Update CHANGELOG.md (#5237) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a96e2894b..a217d5847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - gitea-mirror: increase build ressources [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5235](https://github.com/community-scripts/ProxmoxVE/pull/5235)) - Immich: ensure in proper working dir when updating [@vhsdream](https://github.com/vhsdream) ([#5227](https://github.com/community-scripts/ProxmoxVE/pull/5227)) - Update IP-Tag [@DesertGamer](https://github.com/DesertGamer) ([#5226](https://github.com/community-scripts/ProxmoxVE/pull/5226)) - trilium: fix update function after db changes folder [@tjcomserv](https://github.com/tjcomserv) ([#5207](https://github.com/community-scripts/ProxmoxVE/pull/5207)) From acfb9d6ea159b6c86a92500ae5ec94f5f5f4a502 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:14:53 +0200 Subject: [PATCH 057/139] Update versions.json (#5238) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 130 ++++++++++++++--------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 592bae9de..5c413305e 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,64 @@ [ + { + "name": "grafana/grafana", + "version": "v11.5.6", + "date": "2025-06-17T22:00:40Z" + }, + { + "name": "jenkinsci/jenkins", + "version": "jenkins-2.515", + "date": "2025-06-17T19:17:56Z" + }, + { + "name": "coder/code-server", + "version": "v4.100.3", + "date": "2025-06-03T21:06:41Z" + }, + { + "name": "HabitRPG/habitica", + "version": "v5.36.6", + "date": "2025-06-17T18:12:31Z" + }, + { + "name": "fallenbagel/jellyseerr", + "version": "preview-sort-userlist", + "date": "2025-06-17T18:02:25Z" + }, + { + "name": "ollama/ollama", + "version": "v0.9.2", + "date": "2025-06-17T17:51:43Z" + }, + { + "name": "msgbyte/tianji", + "version": "v1.21.16", + "date": "2025-06-17T16:25:53Z" + }, + { + "name": "BookStackApp/BookStack", + "version": "v25.05.1", + "date": "2025-06-17T14:38:04Z" + }, + { + "name": "element-hq/synapse", + "version": "v1.132.0", + "date": "2025-06-17T13:49:30Z" + }, + { + "name": "cloudflare/cloudflared", + "version": "2025.6.1", + "date": "2025-06-17T12:45:39Z" + }, + { + "name": "sabnzbd/sabnzbd", + "version": "4.5.1", + "date": "2025-04-11T09:57:47Z" + }, + { + "name": "crowdsecurity/crowdsec", + "version": "v1.6.9", + "date": "2025-06-17T11:54:50Z" + }, { "name": "glpi-project/glpi", "version": "10.0.18", @@ -44,11 +104,6 @@ "version": "testing", "date": "2025-06-16T18:10:42Z" }, - { - "name": "msgbyte/tianji", - "version": "v1.21.14", - "date": "2025-06-16T17:54:48Z" - }, { "name": "goauthentik/authentik", "version": "version/2025.6.2", @@ -56,8 +111,8 @@ }, { "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" + "version": "nightly", + "date": "2025-06-16T17:35:17Z" }, { "name": "NodeBB/NodeBB", @@ -184,11 +239,6 @@ "version": "v25.0", "date": "2025-05-12T09:12:04Z" }, - { - "name": "ollama/ollama", - "version": "v0.9.1-rc1", - "date": "2025-06-12T21:18:54Z" - }, { "name": "theonedev/onedev", "version": "v11.11.0", @@ -220,14 +270,9 @@ "date": "2025-06-13T15:02:37Z" }, { - "name": "grafana/grafana", - "version": "v11.4.6", - "date": "2025-06-13T14:11:41Z" - }, - { - "name": "jenkinsci/jenkins", - "version": "jenkins-2.514", - "date": "2025-06-10T14:27:57Z" + "name": "wazuh/wazuh", + "version": "coverity-w25-4.13.0", + "date": "2025-06-13T13:58:23Z" }, { "name": "FlowiseAI/Flowise", @@ -274,36 +319,16 @@ "version": "4.8.11.0", "date": "2025-03-10T06:39:11Z" }, - { - "name": "crowdsecurity/crowdsec", - "version": "v1.6.8", - "date": "2025-03-25T13:33:10Z" - }, { "name": "dgtlmoon/changedetection.io", "version": "0.50.3", "date": "2025-06-11T15:19:52Z" }, - { - "name": "cloudflare/cloudflared", - "version": "2025.6.0", - "date": "2025-06-11T11:13:21Z" - }, { "name": "autobrr/autobrr", "version": "v1.63.1", "date": "2025-06-11T11:05:42Z" }, - { - "name": "fallenbagel/jellyseerr", - "version": "preview-forceipv4-axios", - "date": "2025-06-11T09:16:40Z" - }, - { - "name": "wazuh/wazuh", - "version": "coverity-w24-4.13.0", - "date": "2025-06-11T04:04:55Z" - }, { "name": "zabbix/zabbix", "version": "7.2.8rc1", @@ -324,11 +349,6 @@ "version": "v0.107.62", "date": "2025-05-27T12:10:19Z" }, - { - "name": "element-hq/synapse", - "version": "v1.131.0", - "date": "2025-06-03T14:13:00Z" - }, { "name": "OctoPrint/OctoPrint", "version": "1.11.2", @@ -494,11 +514,6 @@ "version": "2.0.0-beta.2-temp", "date": "2025-03-28T08:45:58Z" }, - { - "name": "coder/code-server", - "version": "v4.100.3", - "date": "2025-06-03T21:06:41Z" - }, { "name": "influxdata/influxdb", "version": "v1.12.1rc3", @@ -539,11 +554,6 @@ "version": "v3.4.1", "date": "2025-05-31T13:45:40Z" }, - { - "name": "BookStackApp/BookStack", - "version": "v25.05", - "date": "2025-05-31T13:36:23Z" - }, { "name": "blakeblackshear/frigate", "version": "v0.14.1", @@ -569,11 +579,6 @@ "version": "v0.56.1", "date": "2025-05-29T19:09:16Z" }, - { - "name": "HabitRPG/habitica", - "version": "v5.36.5", - "date": "2025-05-29T17:06:01Z" - }, { "name": "readeck/readeck", "version": "0.19.2", @@ -924,11 +929,6 @@ "version": "v5.5.2", "date": "2025-04-11T22:00:06Z" }, - { - "name": "sabnzbd/sabnzbd", - "version": "4.5.1", - "date": "2025-04-11T09:57:47Z" - }, { "name": "thomiceli/opengist", "version": "v1.10.0", From 654508eb94b460eaf9ed57f5d06c362cb81550cb Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:15:31 +0100 Subject: [PATCH 058/139] Update CHANGELOG.md (#5239) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a217d5847..ed4e42fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-18 + ## 2025-06-17 ### 🚀 Updated Scripts From c11636562c46cc09330738b2108e60c2b4259fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 18 Jun 2025 07:43:31 +0200 Subject: [PATCH 059/139] Update libretranslate.json (#5236) --- frontend/public/json/libretranslate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/libretranslate.json b/frontend/public/json/libretranslate.json index 85eebd688..094916f6e 100644 --- a/frontend/public/json/libretranslate.json +++ b/frontend/public/json/libretranslate.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/LibreTranslate/LibreTranslate?tab=readme-ov-file#settings--flags", "website": "https://libretranslate.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/libretranslate.webp", - "config_path": "", + "config_path": "/opt/libretranslate/.env", "description": "Free and Open Source Machine Translation API, entirely self-hosted. Unlike other APIs, it doesn't rely on proprietary providers such as Google or Azure to perform translations. Instead, its translation engine is powered by the open source Argos Translate library.", "install_methods": [ { From 003422934a42c27a3f94f67454552c44c058cc96 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:03:00 +0200 Subject: [PATCH 060/139] upgrade old Scriptcalls to new tools.func calls (#5242) * Upgraded Function Names related to #5241 * change gh calls * add 2 missing --- ct/2fauth.sh | 6 +++--- ct/actualbudget.sh | 2 +- ct/configarr.sh | 4 ++-- ct/fumadocs.sh | 2 +- ct/gitea-mirror.sh | 10 +++++----- ct/homarr.sh | 8 ++++---- ct/jellyseerr.sh | 4 ++-- ct/linkwarden.sh | 6 +++--- ct/matterbridge.sh | 22 ++++++++++------------ ct/seelf.sh | 2 +- ct/streamlink-webui.sh | 8 ++++---- ct/suwayomiserver.sh | 2 +- install/2fauth-install.sh | 8 ++++---- install/actualbudget-install.sh | 2 +- install/adventurelog-install.sh | 4 ++-- install/apache-guacamole-install.sh | 2 +- install/archivebox-install.sh | 2 +- install/baikal-install.sh | 2 +- install/bitmagnet-install.sh | 4 ++-- install/bookstack-install.sh | 2 +- install/bytestash-install.sh | 2 +- install/caddy-install.sh | 2 +- install/changedetection-install.sh | 2 +- install/cloudflare-ddns-install.sh | 2 +- install/configarr-install.sh | 4 ++-- install/cronicle-install.sh | 2 +- install/cross-seed-install.sh | 2 +- install/cryptpad-install.sh | 2 +- install/dashy-install.sh | 2 +- install/docmost-install.sh | 4 ++-- install/documenso-install.sh | 4 ++-- install/dolibarr-install.sh | 2 +- install/elementsynapse-install.sh | 6 +++--- install/excalidraw-install.sh | 2 +- install/firefly-install.sh | 2 +- install/flowiseai-install.sh | 2 +- install/freshrss-install.sh | 2 +- install/frigate-install.sh | 2 +- install/fumadocs-install.sh | 2 +- install/gatus-install.sh | 2 +- install/ghost-install.sh | 4 ++-- install/gitea-mirror-install.sh | 2 +- install/glpi-install.sh | 2 +- install/gomft-install.sh | 4 ++-- install/graylog-install.sh | 2 +- install/grist-install.sh | 2 +- install/habitica-install.sh | 2 +- install/homarr-install.sh | 4 ++-- install/homepage-install.sh | 2 +- install/immich-install.sh | 4 ++-- install/iobroker-install.sh | 2 +- install/jellyseerr-install.sh | 4 +--- install/karakeep-install.sh | 2 +- install/kimai-install.sh | 2 +- install/koillection-install.sh | 4 ++-- install/libretranslate-install.sh | 2 +- install/linkwarden-install.sh | 8 ++++---- install/mafl-install.sh | 2 +- install/magicmirror-install.sh | 2 +- install/managemydamnlife-install.sh | 4 ++-- install/mariadb-install.sh | 2 +- install/matterbridge-install.sh | 2 +- install/mattermost-install.sh | 2 +- install/meilisearch-install.sh | 2 +- install/memos-install.sh | 4 ++-- install/meshcentral-install.sh | 2 +- install/metube-install.sh | 2 +- install/monica-install.sh | 4 ++-- install/myspeed-install.sh | 2 +- install/n8n-install.sh | 2 +- install/netbox-install.sh | 2 +- install/node-red-install.sh | 2 +- install/nodebb-install.sh | 4 ++-- install/openwebui-install.sh | 2 +- install/outline-install.sh | 4 ++-- install/overseerr-install.sh | 2 +- install/pairdrop-install.sh | 2 +- install/paperless-ai-install.sh | 2 +- install/paperless-gpt-install.sh | 4 ++-- install/paperless-ngx-install.sh | 2 +- install/part-db-install.sh | 4 ++-- install/paymenter-install.sh | 2 +- install/peanut-install.sh | 2 +- install/pelican-panel-install.sh | 2 +- install/pf2etools-install.sh | 2 +- install/phpipam-install.sh | 2 +- install/pingvin-install.sh | 2 +- install/plant-it-install.sh | 14 +++++++------- install/postgresql-install.sh | 2 +- install/projectsend-install.sh | 2 +- install/ps5-mqtt-install.sh | 2 +- install/pterodactyl-panel-install.sh | 2 +- install/pulse-install.sh | 3 +-- install/reactive-resume-install.sh | 4 ++-- install/revealjs-install.sh | 2 +- install/seelf-install.sh | 6 +++--- install/sftpgo-install.sh | 2 +- install/shinobi-install.sh | 4 ++-- install/snipeit-install.sh | 2 +- install/streamlink-webui-install.sh | 4 ++-- install/suwayomiserver-install.sh | 2 +- install/tandoor-install.sh | 2 +- install/tasmocompiler-install.sh | 2 +- install/the-lounge-install.sh | 2 +- install/tianji-install.sh | 4 ++-- install/umami-install.sh | 4 ++-- install/uptimekuma-install.sh | 2 +- install/watcharr-install.sh | 4 ++-- install/wavelog-install.sh | 2 +- install/web-check-install.sh | 2 +- install/wger-install.sh | 8 ++++---- install/wikijs-install.sh | 4 ++-- install/wordpress-install.sh | 2 +- install/zigbee2mqtt-install.sh | 2 +- install/zipline-install.sh | 4 ++-- install/zitadel-install.sh | 2 +- 116 files changed, 185 insertions(+), 190 deletions(-) diff --git a/ct/2fauth.sh b/ct/2fauth.sh index f16c854fb..416f86914 100644 --- a/ct/2fauth.sh +++ b/ct/2fauth.sh @@ -29,7 +29,7 @@ function update_script() { exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/Bubka/2FAuth/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/2fauth_version.txt)" ]] || [[ ! -f /opt/2fauth_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.2fauth 2>/dev/null || cat /opt/2fauth_version.txt 2>/dev/null)" ]]; then msg_info "Updating $APP to ${RELEASE}" $STD apt-get update $STD apt-get -y upgrade @@ -45,10 +45,10 @@ function update_script() { $STD apt-get install -y \ lsb-release \ gnupg2 - PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" install_php + PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" setup_php sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf fi - fetch_and_deploy_gh_release "Bubka/2FAuth" + fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" mv "/opt/2fauth-backup/.env" "/opt/2fauth/.env" mv "/opt/2fauth-backup/storage" "/opt/2fauth/storage" cd "/opt/2fauth" || return diff --git a/ct/actualbudget.sh b/ct/actualbudget.sh index 6cd6188c6..8e1fe7b0d 100644 --- a/ct/actualbudget.sh +++ b/ct/actualbudget.sh @@ -29,7 +29,7 @@ function update_script() { exit fi NODE_VERSION="22" - install_node_and_modules + setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/actualbudget/actual/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ -f /opt/actualbudget-data/config.json ]]; then if [[ ! -f /opt/actualbudget_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/actualbudget_version.txt)" ]]; then diff --git a/ct/configarr.sh b/ct/configarr.sh index 2921f249c..c6268d07a 100644 --- a/ct/configarr.sh +++ b/ct/configarr.sh @@ -28,7 +28,7 @@ function update_script() { exit fi RELEASE=$(curl -fsSL https://api.github.com/repos/raydak-labs/configarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat /opt/configarr_version.txt)" ]] || [[ ! -f /opt/configarr_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.configarr 2>/dev/null || cat /opt/configarr_version.txt 2>/dev/null)" ]]; then msg_info "Stopping $APP" systemctl stop configarr-task.timer msg_ok "Stopped $APP" @@ -37,7 +37,7 @@ function update_script() { mkdir -p /opt/backup/ mv /opt/configarr/{config.yml,secrets.yml,.env} "/opt/backup/" rm -rf /opt/configarr - fetch_and_deploy_gh_release "raydak-labs/configarr" + fetch_and_deploy_gh_release "configarr" "raydak-labs/configarr" mv /opt/backup/* /opt/configarr/ cd /opt/configarr $STD pnpm install diff --git a/ct/fumadocs.sh b/ct/fumadocs.sh index 244f2f2db..a4da269a8 100644 --- a/ct/fumadocs.sh +++ b/ct/fumadocs.sh @@ -34,7 +34,7 @@ function update_script() { exit 1 fi - NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs PROJECT_NAME=$(=")[1] | split(".")[0]') NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" - install_node_and_modules - + setup_nodejs + rm -rf /opt/homarr - fetch_and_deploy_gh_release "homarr-labs/homarr" + fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" msg_info "Updating and rebuilding ${APP} to v${RELEASE} (Patience)" rm /opt/run_homarr.sh diff --git a/ct/jellyseerr.sh b/ct/jellyseerr.sh index d0afda87c..97d92a32b 100644 --- a/ct/jellyseerr.sh +++ b/ct/jellyseerr.sh @@ -53,10 +53,10 @@ function update_script() { if [ -z "$pnpm_current" ]; then msg_error "pnpm not found. Installing version $pnpm_desired..." - NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs elif ! node -e "const semver = require('semver'); process.exit(semver.satisfies('$pnpm_current', '$pnpm_desired') ? 0 : 1)"; then msg_error "Updating pnpm from version $pnpm_current to $pnpm_desired..." - NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs else msg_ok "pnpm is already installed and satisfies version $pnpm_desired." fi diff --git a/ct/linkwarden.sh b/ct/linkwarden.sh index 477209413..a072cf361 100644 --- a/ct/linkwarden.sh +++ b/ct/linkwarden.sh @@ -28,17 +28,17 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/linkwarden/linkwarden/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/linkwarden_version.txt)" ]] || [[ ! -f /opt/linkwarden_version.txt ]]; then - NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Stopping ${APP}" systemctl stop linkwarden msg_ok "Stopped ${APP}" - RUST_CRATES="monolith" install_rust_and_crates + RUST_CRATES="monolith" setup_rust msg_info "Updating ${APP} to ${RELEASE}" mv /opt/linkwarden/.env /opt/.env rm -rf /opt/linkwarden - fetch_and_deploy_gh_release "linkwarden/linkwarden" + fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" cd /opt/linkwarden $STD yarn $STD npx playwright install-deps diff --git a/ct/matterbridge.sh b/ct/matterbridge.sh index 82bd704b5..39b029388 100644 --- a/ct/matterbridge.sh +++ b/ct/matterbridge.sh @@ -20,19 +20,17 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /root/Matterbridge ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - $STD apt-get update - $STD apt-get upgrade -y - NODE_VERSION="22" - NODE_MODULE="matterbridge" - install_node_and_modules + header_info + check_container_storage + check_container_resources + if [[ ! -d /root/Matterbridge ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + $STD apt-get update + $STD apt-get upgrade -y + NODE_VERSION="22" NODE_MODULE="matterbridge" setup_nodejs + exit } start diff --git a/ct/seelf.sh b/ct/seelf.sh index d36de48b9..350e1a687 100644 --- a/ct/seelf.sh +++ b/ct/seelf.sh @@ -28,7 +28,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - if fetch_and_deploy_gh_release "YuukanOO/seelf"; then + if fetch_and_deploy_gh_release "seelf" "YuukanOO/seelf"; then msg_ok "$APP already at the latest version. No update required." else msg_info "Stopping $APP" diff --git a/ct/streamlink-webui.sh b/ct/streamlink-webui.sh index 362f64ba0..d5adc55fe 100644 --- a/ct/streamlink-webui.sh +++ b/ct/streamlink-webui.sh @@ -30,9 +30,9 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/CrazyWolf13/streamlink-webui/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.${APP} 2>/dev/null || cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then msg_info "Starting Update" - + msg_info "Stopping $APP" systemctl stop ${APP} msg_ok "Stopped $APP" @@ -40,9 +40,9 @@ function update_script() { rm -rf /opt/${APP} NODE_VERSION="22" NODE_MODULE="npm,yarn" - install_node_and_modules + setup_nodejs setup_uv - fetch_and_deploy_gh_release "CrazyWolf13/streamlink-webui" + fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" msg_info "Updating $APP to v${RELEASE}" $STD uv venv /opt/"${APP}"/backend/src/.venv diff --git a/ct/suwayomiserver.sh b/ct/suwayomiserver.sh index 6a2dbe762..ce64e1da8 100644 --- a/ct/suwayomiserver.sh +++ b/ct/suwayomiserver.sh @@ -31,7 +31,7 @@ function update_script() { if dpkg -l | grep -q "openjdk-17-jre"; then $STD apt-get remove -y openjdk-17-jre fi - JAVA_VERSION=21 install_java + JAVA_VERSION=21 setup_java RELEASE=$(curl -fsSL https://api.github.com/repos/Suwayomi/Suwayomi-Server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/suwayomi-server_version.txt)" ]] || [[ ! -f /opt/suwayomi-server_version.txt ]]; then msg_info "Updating $APP" diff --git a/install/2fauth-install.sh b/install/2fauth-install.sh index 291d52ca1..d5102d017 100644 --- a/install/2fauth-install.sh +++ b/install/2fauth-install.sh @@ -19,9 +19,9 @@ $STD apt-get install -y \ nginx msg_ok "Installed Dependencies" -PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" install_php -install_composer -install_mariadb +PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,fpm,mysql,cli" setup_php +setup_composer +setup_mariadb msg_info "Setting up Database" DB_NAME=2fauth_db @@ -38,7 +38,7 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/2FAuth.creds msg_ok "Set up Database" -fetch_and_deploy_gh_release "Bubka/2FAuth" +fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" msg_info "Setup 2FAuth" cd /opt/2fauth diff --git a/install/actualbudget-install.sh b/install/actualbudget-install.sh index 51843f6c7..59b2d3870 100644 --- a/install/actualbudget-install.sh +++ b/install/actualbudget-install.sh @@ -22,7 +22,7 @@ msg_info "Installing Actual Budget" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/actualbudget/actual/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') NODE_VERSION="22" -install_node_and_modules +setup_nodejs mkdir -p /opt/actualbudget-data/{server-files,upload,migrate,user-files,migrations,config} chown -R root:root /opt/actualbudget-data chmod -R 755 /opt/actualbudget-data diff --git a/install/adventurelog-install.sh b/install/adventurelog-install.sh index a56cd6949..cdd7f7f07 100644 --- a/install/adventurelog-install.sh +++ b/install/adventurelog-install.sh @@ -23,8 +23,8 @@ $STD apt-get install -y \ python3-pip msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules -PG_VERSION="16" PG_MODULES="postgis" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +PG_VERSION="16" PG_MODULES="postgis" setup_postgresql msg_info "Set up PostgreSQL Database" DB_NAME="adventurelog_db" diff --git a/install/apache-guacamole-install.sh b/install/apache-guacamole-install.sh index fc2c99de3..6b8529bfc 100644 --- a/install/apache-guacamole-install.sh +++ b/install/apache-guacamole-install.sh @@ -38,7 +38,7 @@ $STD apt-get install -y \ default-jdk msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setup Apache Tomcat" RELEASE=$(curl -fsSL https://dlcdn.apache.org/tomcat/tomcat-9/ | grep -oP '(?<=href=")v[^"/]+(?=/")' | sed 's/^v//' | sort -V | tail -n1) diff --git a/install/archivebox-install.sh b/install/archivebox-install.sh index d1700cab2..c09ded01e 100644 --- a/install/archivebox-install.sh +++ b/install/archivebox-install.sh @@ -33,7 +33,7 @@ $STD apt-get install -y \ python3-regex msg_ok "Installed Python Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Playwright" $STD pip install playwright diff --git a/install/baikal-install.sh b/install/baikal-install.sh index 0176c4f48..93d68fabc 100644 --- a/install/baikal-install.sh +++ b/install/baikal-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ php-{pgsql,dom} msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL Database" DB_NAME=baikal diff --git a/install/bitmagnet-install.sh b/install/bitmagnet-install.sh index 7ccdb8c67..69ecde640 100644 --- a/install/bitmagnet-install.sh +++ b/install/bitmagnet-install.sh @@ -20,8 +20,8 @@ $STD apt-get install -y \ musl-dev msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql -install_go +PG_VERSION="16" setup_postgresql +setup_go RELEASE=$(curl -fsSL https://api.github.com/repos/bitmagnet-io/bitmagnet/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing bitmagnet v${RELEASE}" diff --git a/install/bookstack-install.sh b/install/bookstack-install.sh index 3f145ceee..0b3ebbd50 100644 --- a/install/bookstack-install.sh +++ b/install/bookstack-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ make msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" DB_NAME=bookstack diff --git a/install/bytestash-install.sh b/install/bytestash-install.sh index 535ae685b..03689e994 100644 --- a/install/bytestash-install.sh +++ b/install/bytestash-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing ByteStash" JWT_SECRET=$(openssl rand -base64 32 | tr -d '/+=') diff --git a/install/caddy-install.sh b/install/caddy-install.sh index 83298772f..c4f94a9ea 100644 --- a/install/caddy-install.sh +++ b/install/caddy-install.sh @@ -29,7 +29,7 @@ msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - install_go + setup_go msg_info "Setup xCaddy" $STD apt-get install -y git cd /opt diff --git a/install/changedetection-install.sh b/install/changedetection-install.sh index 976b6159d..243775115 100644 --- a/install/changedetection-install.sh +++ b/install/changedetection-install.sh @@ -51,7 +51,7 @@ $STD apt-get install -y \ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Change Detection" mkdir /opt/changedetection diff --git a/install/cloudflare-ddns-install.sh b/install/cloudflare-ddns-install.sh index 26e1b4c65..d3ee19d22 100644 --- a/install/cloudflare-ddns-install.sh +++ b/install/cloudflare-ddns-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -install_go +setup_go msg_info "Configure Application" var_cf_api_token="default" diff --git a/install/configarr-install.sh b/install/configarr-install.sh index c04693806..7ec4411a7 100644 --- a/install/configarr-install.sh +++ b/install/configarr-install.sh @@ -18,8 +18,8 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_MODULE="pnpm@latest" install_node_and_modules -fetch_and_deploy_gh_release "raydak-labs/configarr" +NODE_MODULE="pnpm@latest" setup_nodejs +fetch_and_deploy_gh_release "configarr" "raydak-labs/configarr" msg_info "Setup ${APPLICATION}" cat </opt/configarr/.env diff --git a/install/cronicle-install.sh b/install/cronicle-install.sh index 5cb697f9d..9caee26ee 100644 --- a/install/cronicle-install.sh +++ b/install/cronicle-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Cronicle Primary Server" LATEST=$(curl -fsSL https://api.github.com/repos/jhuckaby/Cronicle/releases/latest | grep '"tag_name":' | cut -d'"' -f4) diff --git a/install/cross-seed-install.sh b/install/cross-seed-install.sh index a4dd77f0c..f100c0847 100644 --- a/install/cross-seed-install.sh +++ b/install/cross-seed-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup Cross-Seed" $STD npm install cross-seed@latest -g diff --git a/install/cryptpad-install.sh b/install/cryptpad-install.sh index 74b7f8074..044c8f3ee 100644 --- a/install/cryptpad-install.sh +++ b/install/cryptpad-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs read -p "${TAB3}Install OnlyOffice components instead of CKEditor? (Y/N): " onlyoffice diff --git a/install/dashy-install.sh b/install/dashy-install.sh index 1d6b779e9..3bda31cfb 100644 --- a/install/dashy-install.sh +++ b/install/dashy-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/Lissy93/dashy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') msg_info "Installing Dashy ${RELEASE} (Patience)" diff --git a/install/docmost-install.sh b/install/docmost-install.sh index a6a74fd5e..e1b3a1368 100644 --- a/install/docmost-install.sh +++ b/install/docmost-install.sh @@ -20,8 +20,8 @@ $STD apt-get install -y \ make msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/docmost/docmost/main/package.json | jq -r '.packageManager | split("@")[1]')" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/docmost/docmost/main/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME="docmost_db" diff --git a/install/documenso-install.sh b/install/documenso-install.sh index 861f3f40e..a481928f0 100644 --- a/install/documenso-install.sh +++ b/install/documenso-install.sh @@ -28,8 +28,8 @@ $STD apt-get install -y \ python3-bcrypt msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="turbo@1.9.3" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="turbo@1.9.3" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME="documenso_db" diff --git a/install/dolibarr-install.sh b/install/dolibarr-install.sh index 0c98f5e74..52c54c5b9 100644 --- a/install/dolibarr-install.sh +++ b/install/dolibarr-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ debconf-utils msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" ROOT_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) diff --git a/install/elementsynapse-install.sh b/install/elementsynapse-install.sh index f9c654f1e..ec8d56b53 100644 --- a/install/elementsynapse-install.sh +++ b/install/elementsynapse-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ debconf-utils msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs read -p "${TAB3}Please enter the name for your server: " servername @@ -57,8 +57,8 @@ cd /opt/synapse-admin $STD yarn global add serve $STD yarn install --ignore-engines $STD yarn build -mv ./dist ../ && \ - rm -rf * && \ +mv ./dist ../ && + rm -rf * && mv ../dist ./ msg_ok "Installed Element Synapse" diff --git a/install/excalidraw-install.sh b/install/excalidraw-install.sh index f5a35e591..885a39c91 100644 --- a/install/excalidraw-install.sh +++ b/install/excalidraw-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ xdg-utils msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Excalidraw" temp_file=$(mktemp) diff --git a/install/firefly-install.sh b/install/firefly-install.sh index 82082d76b..1c8e57041 100644 --- a/install/firefly-install.sh +++ b/install/firefly-install.sh @@ -24,7 +24,7 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up database" DB_NAME=firefly diff --git a/install/flowiseai-install.sh b/install/flowiseai-install.sh index 55e364da4..585157d95 100644 --- a/install/flowiseai-install.sh +++ b/install/flowiseai-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Installing FlowiseAI (Patience)" $STD npm install -g flowise \ diff --git a/install/freshrss-install.sh b/install/freshrss-install.sh index dc2894f61..25251d13c 100644 --- a/install/freshrss-install.sh +++ b/install/freshrss-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ libapache2-mod-php msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=freshrss diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 14458c553..a81b1588c 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y {python3,python3-dev,python3-setuptools,python3-distutil $STD pip install --upgrade pip msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing go2rtc" mkdir -p /usr/local/go2rtc/bin diff --git a/install/fumadocs-install.sh b/install/fumadocs-install.sh index 84c894786..beef80e3e 100644 --- a/install/fumadocs-install.sh +++ b/install/fumadocs-install.sh @@ -17,7 +17,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Installing Fumadocs" mkdir -p /opt/fumadocs diff --git a/install/gatus-install.sh b/install/gatus-install.sh index 8a2fc7a5c..3b5cd563b 100644 --- a/install/gatus-install.sh +++ b/install/gatus-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ libcap2-bin msg_ok "Installed Dependencies" -install_go +setup_go RELEASE=$(curl -s https://api.github.com/repos/TwiN/gatus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Setting up gatus v${RELEASE}" diff --git a/install/ghost-install.sh b/install/ghost-install.sh index 14604051d..cee4f3d58 100644 --- a/install/ghost-install.sh +++ b/install/ghost-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Configuring Database" DB_NAME=ghost @@ -37,7 +37,7 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/ghost.creds msg_ok "Configured MySQL" -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Installing Ghost CLI" $STD npm install ghost-cli@latest -g diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh index 2a44477b9..d1f3dd78f 100644 --- a/install/gitea-mirror-install.sh +++ b/install/gitea-mirror-install.sh @@ -28,7 +28,7 @@ ln -sf /opt/bun/bin/bun /usr/local/bin/bun ln -sf /opt/bun/bin/bun /usr/local/bin/bunx msg_ok "Installed Bun" -fetch_and_deploy_gh_release "arunavo4/gitea-mirror" +fetch_and_deploy_gh_release "gitea-mirror" "arunavo4/gitea-mirror" msg_info "Installing gitea-mirror" cd /opt/gitea-mirror diff --git a/install/glpi-install.sh b/install/glpi-install.sh index 2e9563b6a..25b0d7d7e 100644 --- a/install/glpi-install.sh +++ b/install/glpi-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ libapache2-mod-php msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up database" DB_NAME=glpi_db diff --git a/install/gomft-install.sh b/install/gomft-install.sh index 5b5692d89..2d84ef014 100644 --- a/install/gomft-install.sh +++ b/install/gomft-install.sh @@ -22,8 +22,8 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules +setup_go +NODE_VERSION="22" setup_nodejs msg_info "Setup ${APPLICATION} (Patience)" temp_file=$(mktemp) diff --git a/install/graylog-install.sh b/install/graylog-install.sh index 21bec8777..397d8ebab 100644 --- a/install/graylog-install.sh +++ b/install/graylog-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -MONGO_VERSION="7.0" install_mongodb +MONGO_VERSION="7.0" setup_mongodb msg_info "Setup Graylog Data Node" PASSWORD_SECRET=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) diff --git a/install/grist-install.sh b/install/grist-install.sh index 9649accf4..c69814c70 100644 --- a/install/grist-install.sh +++ b/install/grist-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ python3.11-venv msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Grist" RELEASE=$(curl -fsSL https://api.github.com/repos/gristlabs/grist-core/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') diff --git a/install/habitica-install.sh b/install/habitica-install.sh index 83ebdccfd..17443d621 100644 --- a/install/habitica-install.sh +++ b/install/habitica-install.sh @@ -22,7 +22,7 @@ curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1 $STD dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb msg_ok "Installed Dependencies" -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Setup ${APPLICATION}" temp_file=$(mktemp) diff --git a/install/homarr-install.sh b/install/homarr-install.sh index 1116534bd..0ff212da9 100644 --- a/install/homarr-install.sh +++ b/install/homarr-install.sh @@ -28,8 +28,8 @@ msg_ok "Installed Dependencies" NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" -install_node_and_modules -fetch_and_deploy_gh_release "homarr-labs/homarr" +setup_nodejs +fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" msg_info "Installing Homarr (Patience)" cd /opt diff --git a/install/homepage-install.sh b/install/homepage-install.sh index 7726b5ea1..303648aa3 100644 --- a/install/homepage-install.sh +++ b/install/homepage-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs LOCAL_IP=$(hostname -I | awk '{print $1}') RELEASE=$(curl -fsSL https://api.github.com/repos/gethomepage/homepage/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') diff --git a/install/immich-install.sh b/install/immich-install.sh index be7312767..367e68472 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -84,8 +84,8 @@ ln -s /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -s /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe msg_ok "Dependencies Installed" -NODE_VERSION="22" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql read -r -p "${TAB3}Install OpenVINO dependencies for Intel HW-accelerated machine-learning? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then diff --git a/install/iobroker-install.sh b/install/iobroker-install.sh index c4494b7a4..340836ec3 100644 --- a/install/iobroker-install.sh +++ b/install/iobroker-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing ioBroker (Patience)" $STD bash <(curl -fsSL https://iobroker.net/install.sh) diff --git a/install/jellyseerr-install.sh b/install/jellyseerr-install.sh index 5ceba13a9..1ea42d507 100644 --- a/install/jellyseerr-install.sh +++ b/install/jellyseerr-install.sh @@ -19,14 +19,12 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" - - git clone -q https://github.com/Fallenbagel/jellyseerr.git /opt/jellyseerr cd /opt/jellyseerr $STD git checkout main pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/jellyseerr/package.json) -NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs msg_info "Installing Jellyseerr (Patience)" export CYPRESS_INSTALL_BINARY=0 diff --git a/install/karakeep-install.sh b/install/karakeep-install.sh index f94956757..674333f9e 100644 --- a/install/karakeep-install.sh +++ b/install/karakeep-install.sh @@ -48,7 +48,7 @@ sed -i \ /etc/meilisearch.toml msg_ok "Installed Meilisearch" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs $STD npm install -g corepack@0.31.0 msg_info "Installing karakeep" diff --git a/install/kimai-install.sh b/install/kimai-install.sh index 1df625cba..d4462f4c0 100644 --- a/install/kimai-install.sh +++ b/install/kimai-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ lsb-release msg_ok "Installed Dependencies" -install_mysql +setup_mysql msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/koillection-install.sh b/install/koillection-install.sh index 177dca51d..ca2bddd38 100644 --- a/install/koillection-install.sh +++ b/install/koillection-install.sh @@ -19,8 +19,8 @@ $STD apt-get install -y \ lsb-release msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setup PHP8.4 Repository" $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/libretranslate-install.sh b/install/libretranslate-install.sh index 2b83b1bdf..d6f8dc04f 100644 --- a/install/libretranslate-install.sh +++ b/install/libretranslate-install.sh @@ -29,7 +29,7 @@ $STD apt-get install -y \ msg_ok "Setup Python3" setup_uv -fetch_and_deploy_gh_release "LibreTranslate/LibreTranslate" +fetch_and_deploy_gh_release "LibreTranslate" "LibreTranslate/LibreTranslate" msg_info "Setup LibreTranslate (Patience)" cd /opt/libretranslate diff --git a/install/linkwarden-install.sh b/install/linkwarden-install.sh index 42821232f..241c57f4f 100644 --- a/install/linkwarden-install.sh +++ b/install/linkwarden-install.sh @@ -19,9 +19,9 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql -RUST_CRATES="monolith" install_rust_and_crates +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql +RUST_CRATES="monolith" setup_rust msg_info "Setting up PostgreSQL DB" DB_NAME=linkwardendb @@ -48,7 +48,7 @@ if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then fi msg_info "Installing Linkwarden (Patience)" -fetch_and_deploy_gh_release "linkwarden/linkwarden" +fetch_and_deploy_gh_release "linkwarden" "linkwarden/linkwarden" cd /opt/linkwarden $STD yarn $STD npx playwright install-deps diff --git a/install/mafl-install.sh b/install/mafl-install.sh index b07e56c71..574949dbb 100644 --- a/install/mafl-install.sh +++ b/install/mafl-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y gcc $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs 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}" diff --git a/install/magicmirror-install.sh b/install/magicmirror-install.sh index f58109361..d4f21a008 100644 --- a/install/magicmirror-install.sh +++ b/install/magicmirror-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup MagicMirror" temp_file=$(mktemp) diff --git a/install/managemydamnlife-install.sh b/install/managemydamnlife-install.sh index 41f9f48f3..1c1b1ccc0 100644 --- a/install/managemydamnlife-install.sh +++ b/install/managemydamnlife-install.sh @@ -13,8 +13,8 @@ setting_up_container network_check update_os -NODE_VERSION="20" install_node_and_modules -MYSQL_VERSION="8.0" install_mysql +NODE_VERSION="20" setup_nodejs +MYSQL_VERSION="8.0" setup_mysql msg_info "Setting up Database" DB_NAME="mmdl" diff --git a/install/mariadb-install.sh b/install/mariadb-install.sh index 8dd25864f..9a4777cd3 100644 --- a/install/mariadb-install.sh +++ b/install/mariadb-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -install_mariadb +setup_mariadb msg_info "Setup MariaDB" sed -i 's/^# *\(port *=.*\)/\1/' /etc/mysql/my.cnf diff --git a/install/matterbridge-install.sh b/install/matterbridge-install.sh index fb1aa9124..f1ec5a102 100644 --- a/install/matterbridge-install.sh +++ b/install/matterbridge-install.sh @@ -17,7 +17,7 @@ msg_info "Install Matterbridge" mkdir -p /root/Matterbridge NODE_VERSION="22" NODE_MODULE="matterbridge" -install_node_and_modules +setup_nodejs msg_ok "Installed Matterbridge" msg_info "Creating Service" diff --git a/install/mattermost-install.sh b/install/mattermost-install.sh index 0a2481055..6007e8735 100644 --- a/install/mattermost-install.sh +++ b/install/mattermost-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=mattermost diff --git a/install/meilisearch-install.sh b/install/meilisearch-install.sh index ff76d4f27..ecfc071d9 100644 --- a/install/meilisearch-install.sh +++ b/install/meilisearch-install.sh @@ -35,7 +35,7 @@ msg_ok "Setup ${APPLICATION}" read -r -p "${TAB3}Do you want add meilisearch-ui? [y/n]: " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules + NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setup ${APPLICATION}-ui" tmp_file=$(mktemp) diff --git a/install/memos-install.sh b/install/memos-install.sh index 34c896d96..12d6573f2 100644 --- a/install/memos-install.sh +++ b/install/memos-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ tzdata msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules -install_go +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +setup_go msg_info "Installing Memos (Patience)" mkdir -p /opt/memos_data diff --git a/install/meshcentral-install.sh b/install/meshcentral-install.sh index 8ddc72c5a..9da47f812 100644 --- a/install/meshcentral-install.sh +++ b/install/meshcentral-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MeshCentral" mkdir /opt/meshcentral diff --git a/install/metube-install.sh b/install/metube-install.sh index 34910d449..ea55e5ecb 100644 --- a/install/metube-install.sh +++ b/install/metube-install.sh @@ -36,7 +36,7 @@ $STD apt-get install -y \ python3-venv msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MeTube" $STD git clone https://github.com/alexta69/metube /opt/metube diff --git a/install/monica-install.sh b/install/monica-install.sh index 823150342..1f9ab9154 100644 --- a/install/monica-install.sh +++ b/install/monica-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules +setup_mariadb +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setting up MariaDB" DB_NAME=monica diff --git a/install/myspeed-install.sh b/install/myspeed-install.sh index 4bd6424a9..17bccfa64 100644 --- a/install/myspeed-install.sh +++ b/install/myspeed-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing MySpeed" RELEASE=$(curl -fsSL https://github.com/gnmyt/myspeed/releases/latest | grep "title>Release" | cut -d " " -f 5) diff --git a/install/n8n-install.sh b/install/n8n-install.sh index d36516a76..7496a8595 100644 --- a/install/n8n-install.sh +++ b/install/n8n-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing n8n (Patience)" $STD npm install --global patch-package diff --git a/install/netbox-install.sh b/install/netbox-install.sh index 6a3499c4c..1c9799774 100644 --- a/install/netbox-install.sh +++ b/install/netbox-install.sh @@ -26,7 +26,7 @@ $STD apt-get install -y \ zlib1g-dev msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Installing Python" $STD apt-get install -y \ diff --git a/install/node-red-install.sh b/install/node-red-install.sh index 6588f1b78..3b5331a3b 100644 --- a/install/node-red-install.sh +++ b/install/node-red-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Node-Red" $STD npm install -g --unsafe-perm node-red diff --git a/install/nodebb-install.sh b/install/nodebb-install.sh index d4f1093ae..92c54c30d 100644 --- a/install/nodebb-install.sh +++ b/install/nodebb-install.sh @@ -21,8 +21,8 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -install_mongodb -NODE_VERSION="22" install_node_and_modules +setup_mongodb +NODE_VERSION="22" setup_nodejs msg_info "Configure MongoDB" MONGO_ADMIN_USER="admin" diff --git a/install/openwebui-install.sh b/install/openwebui-install.sh index 5f2908a7d..af2b36113 100644 --- a/install/openwebui-install.sh +++ b/install/openwebui-install.sh @@ -26,7 +26,7 @@ $STD apt-get install -y --no-install-recommends \ python3-pip msg_ok "Setup Python3" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Open WebUI (Patience)" $STD git clone https://github.com/open-webui/open-webui.git /opt/open-webui diff --git a/install/outline-install.sh b/install/outline-install.sh index d733179f4..5715804b7 100644 --- a/install/outline-install.sh +++ b/install/outline-install.sh @@ -20,8 +20,8 @@ $STD apt-get install -y \ redis msg_ok "Installed Dependencies" -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Set up PostgreSQL Database" DB_NAME="outline" diff --git a/install/overseerr-install.sh b/install/overseerr-install.sh index f2701c4f2..64679b3a7 100644 --- a/install/overseerr-install.sh +++ b/install/overseerr-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Overseerr (Patience)" git clone -q https://github.com/sct/overseerr.git /opt/overseerr diff --git a/install/pairdrop-install.sh b/install/pairdrop-install.sh index 8c7724134..751311dd2 100644 --- a/install/pairdrop-install.sh +++ b/install/pairdrop-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing PairDrop" git clone -q https://github.com/schlagmichdoch/PairDrop.git /opt/pairdrop diff --git a/install/paperless-ai-install.sh b/install/paperless-ai-install.sh index f4972ef46..cb8fc4024 100644 --- a/install/paperless-ai-install.sh +++ b/install/paperless-ai-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ python3-pip msg_ok "Installed Python3" -install_node_and_modules +setup_nodejs msg_info "Setup Paperless-AI" cd /opt diff --git a/install/paperless-gpt-install.sh b/install/paperless-gpt-install.sh index 43b994229..a4e02062b 100644 --- a/install/paperless-gpt-install.sh +++ b/install/paperless-gpt-install.sh @@ -23,8 +23,8 @@ $STD apt-get install -y \ musl-tools msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules -install_go +NODE_VERSION="22" setup_nodejs +setup_go msg_info "Setup Paperless-GPT" temp_file=$(mktemp) diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh index 541aa4b2a..676d662da 100644 --- a/install/paperless-ngx-install.sh +++ b/install/paperless-ngx-install.sh @@ -35,7 +35,7 @@ $STD apt-get install -y \ libleptonica-dev msg_ok "Installed Dependencies" -PG_VERSION="16" install_postgresql +PG_VERSION="16" setup_postgresql msg_info "Setup Python3" $STD apt-get install -y \ diff --git a/install/part-db-install.sh b/install/part-db-install.sh index 3fde07809..dae56a5b5 100644 --- a/install/part-db-install.sh +++ b/install/part-db-install.sh @@ -24,8 +24,8 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PHP" PHPVER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "\n";') diff --git a/install/paymenter-install.sh b/install/paymenter-install.sh index c36585341..7c6345870 100644 --- a/install/paymenter-install.sh +++ b/install/paymenter-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ redis-server msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/peanut-install.sh b/install/peanut-install.sh index f2f806467..20af7871e 100644 --- a/install/peanut-install.sh +++ b/install/peanut-install.sh @@ -14,7 +14,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing NUT" $STD apt-get install -y nut-client diff --git a/install/pelican-panel-install.sh b/install/pelican-panel-install.sh index c78013f25..f82db19f7 100644 --- a/install/pelican-panel-install.sh +++ b/install/pelican-panel-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/pf2etools-install.sh b/install/pf2etools-install.sh index ed1e7bbab..4b2f58e86 100644 --- a/install/pf2etools-install.sh +++ b/install/pf2etools-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup Pf2eTools" cd /opt diff --git a/install/phpipam-install.sh b/install/phpipam-install.sh index df903dea3..115a52649 100644 --- a/install/phpipam-install.sh +++ b/install/phpipam-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ php-pear msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up MariaDB" DB_NAME=phpipam diff --git a/install/pingvin-install.sh b/install/pingvin-install.sh index 778788176..216bf2720 100644 --- a/install/pingvin-install.sh +++ b/install/pingvin-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pm2" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pm2" setup_nodejs msg_info "Installing Pingvin Share (Patience)" cd /opt diff --git a/install/plant-it-install.sh b/install/plant-it-install.sh index 99a012b25..73ebf1440 100644 --- a/install/plant-it-install.sh +++ b/install/plant-it-install.sh @@ -15,11 +15,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - redis \ - nginx + redis \ + nginx msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Adoptium Repository" mkdir -p /etc/apt/keyrings @@ -41,10 +41,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "Plant-it Credentials" - echo "Plant-it Database User: $DB_USER" - echo "Plant-it Database Password: $DB_PASS" - echo "Plant-it Database Name: $DB_NAME" + echo "Plant-it Credentials" + echo "Plant-it Database User: $DB_USER" + echo "Plant-it Database Password: $DB_PASS" + echo "Plant-it Database Name: $DB_NAME" } >>~/plant-it.creds msg_ok "Set up MariaDB" diff --git a/install/postgresql-install.sh b/install/postgresql-install.sh index e12462536..332bf7557 100644 --- a/install/postgresql-install.sh +++ b/install/postgresql-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PG_VERSION="17" install_postgresql +PG_VERSION="17" setup_postgresql cat </etc/postgresql/17/main/pg_hba.conf # PostgreSQL Client Authentication Configuration File diff --git a/install/projectsend-install.sh b/install/projectsend-install.sh index 54624323b..4703f460c 100644 --- a/install/projectsend-install.sh +++ b/install/projectsend-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ php8.2-{pdo,mysql,mbstring,gettext,fileinfo,gd,xml,zip} msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up MariaDB" DB_NAME=projectsend diff --git a/install/ps5-mqtt-install.sh b/install/ps5-mqtt-install.sh index 6aa49e2ac..5447fa0ad 100644 --- a/install/ps5-mqtt-install.sh +++ b/install/ps5-mqtt-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="playactor" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="playactor" setup_nodejs msg_info "Installing PS5-MQTT" RELEASE=$(curl -fsSL https://api.github.com/repos/FunkeyFlo/ps5-mqtt/releases/latest | jq -r '.tag_name') diff --git a/install/pterodactyl-panel-install.sh b/install/pterodactyl-panel-install.sh index a0c0608ef..419fd67d9 100644 --- a/install/pterodactyl-panel-install.sh +++ b/install/pterodactyl-panel-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ composer msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Adding PHP8.4 Repository" $STD curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb diff --git a/install/pulse-install.sh b/install/pulse-install.sh index 5fe6b016c..e9e65273e 100644 --- a/install/pulse-install.sh +++ b/install/pulse-install.sh @@ -28,7 +28,7 @@ else exit 1 fi -NODE_VERSION="20" install_node_and_modules +NODE_VERSION="20" setup_nodejs msg_info "Setup Pulse" RELEASE=$(curl -fsSL https://api.github.com/repos/rcourtman/Pulse/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') @@ -41,7 +41,6 @@ chown pulse:pulse /opt/pulse/.env echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed Pulse" - msg_info "Setting permissions for /opt/pulse..." chown -R pulse:pulse "/opt/pulse" find "/opt/pulse" -type d -exec chmod 755 {} \; diff --git a/install/reactive-resume-install.sh b/install/reactive-resume-install.sh index 7c9f06ac3..f961c0915 100644 --- a/install/reactive-resume-install.sh +++ b/install/reactive-resume-install.sh @@ -19,8 +19,8 @@ curl -fsSL https://dl.min.io/server/minio/release/linux-amd64/minio.deb -o minio $STD dpkg -i minio.deb msg_ok "Installed Dependencies" -PG_VERSION="16" PG_MODULES="common" install_postgresql -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +PG_VERSION="16" PG_MODULES="common" setup_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setting up Database" DB_USER="rxresume" diff --git a/install/revealjs-install.sh b/install/revealjs-install.sh index 3db939c3b..eb1665608 100644 --- a/install/revealjs-install.sh +++ b/install/revealjs-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Setup ${APPLICATION}" temp_file=$(mktemp) diff --git a/install/seelf-install.sh b/install/seelf-install.sh index 24bf5740c..174aa18ce 100644 --- a/install/seelf-install.sh +++ b/install/seelf-install.sh @@ -19,9 +19,9 @@ $STD apt-get install -y \ gcc msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules -fetch_and_deploy_gh_release "YuukanOO/seelf" +setup_go +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "seelf" "YuukanOO/seelf" msg_info "Setting up seelf. Patience" cd /opt/seelf diff --git a/install/sftpgo-install.sh b/install/sftpgo-install.sh index 2d0859fcb..91df27d3f 100644 --- a/install/sftpgo-install.sh +++ b/install/sftpgo-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y sqlite3 msg_ok "Installed Dependencies" -install_go +setup_go msg_info "Installing SFTPGo" curl -fsSL https://ftp.osuosl.org/pub/sftpgo/apt/gpg.key | gpg --dearmor -o /usr/share/keyrings/sftpgo-archive-keyring.gpg diff --git a/install/shinobi-install.sh b/install/shinobi-install.sh index 54dcc6b45..03c2b31c9 100644 --- a/install/shinobi-install.sh +++ b/install/shinobi-install.sh @@ -19,8 +19,8 @@ $STD apt-get install -y gcc g++ cmake $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules -install_mariadb +NODE_VERSION="22" setup_nodejs +setup_mariadb msg_info "Installing FFMPEG" $STD apt-get install -y ffmpeg diff --git a/install/snipeit-install.sh b/install/snipeit-install.sh index 372375a07..98abcdacb 100644 --- a/install/snipeit-install.sh +++ b/install/snipeit-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ php8.2-{bcmath,common,ctype,curl,fileinfo,fpm,gd,iconv,intl,mbstring,mysql,soap,xml,xsl,zip,cli} msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up database" DB_NAME=snipeit_db diff --git a/install/streamlink-webui-install.sh b/install/streamlink-webui-install.sh index 2ab9fe6a2..3c61e36ad 100644 --- a/install/streamlink-webui-install.sh +++ b/install/streamlink-webui-install.sh @@ -16,9 +16,9 @@ update_os NODE_VERSION="22" NODE_MODULE="npm@latest,yarn@latest" -install_node_and_modules +setup_nodejs setup_uv -fetch_and_deploy_gh_release "CrazyWolf13/streamlink-webui" +fetch_and_deploy_gh_release "streamlink-webui" "CrazyWolf13/streamlink-webui" msg_info "Setup ${APPLICATION}" mkdir -p "/opt/${APPLICATION}-download" diff --git a/install/suwayomiserver-install.sh b/install/suwayomiserver-install.sh index 18d12aa43..ffe7485c1 100644 --- a/install/suwayomiserver-install.sh +++ b/install/suwayomiserver-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y libc++-dev msg_ok "Installed Dependencies" -JAVA_VERSION=21 install_java +JAVA_VERSION=21 setup_java msg_info "Settting up Suwayomi-Server" temp_file=$(mktemp) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 3673e3013..b50f966fc 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -42,7 +42,7 @@ $STD apt-get install -y \ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Setup Python3" -NODE_VERSION="20" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Installing Tandoor (Patience)" $STD git clone https://github.com/TandoorRecipes/recipes -b master /opt/tandoor diff --git a/install/tasmocompiler-install.sh b/install/tasmocompiler-install.sh index 1ba241f0b..128ea9b0f 100644 --- a/install/tasmocompiler-install.sh +++ b/install/tasmocompiler-install.sh @@ -22,7 +22,7 @@ msg_info "Setup Python3" $STD apt-get install -y python3-venv msg_ok "Setup Python3" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Platformio" curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py diff --git a/install/the-lounge-install.sh b/install/the-lounge-install.sh index 28d7f0862..6758a30e8 100644 --- a/install/the-lounge-install.sh +++ b/install/the-lounge-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest,node-gyp" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest,node-gyp" setup_nodejs msg_info "Installing The Lounge" cd /opt diff --git a/install/tianji-install.sh b/install/tianji-install.sh index 5f7742855..658b1a320 100644 --- a/install/tianji-install.sh +++ b/install/tianji-install.sh @@ -26,8 +26,8 @@ $STD apt-get install -y \ jq msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/msgbyte/tianji/master/package.json | jq -r '.packageManager | split("@")[1]')" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=tianji_db diff --git a/install/umami-install.sh b/install/umami-install.sh index e9fa625f0..d4e9e29ac 100644 --- a/install/umami-install.sh +++ b/install/umami-install.sh @@ -17,8 +17,8 @@ msg_info "Installing Dependencies" $STD apt-get install -y git msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up postgresql" DB_NAME=umamidb diff --git a/install/uptimekuma-install.sh b/install/uptimekuma-install.sh index 91da064b5..3eb9b2b1c 100644 --- a/install/uptimekuma-install.sh +++ b/install/uptimekuma-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y git $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" install_node_and_modules +NODE_VERSION="22" setup_nodejs msg_info "Installing Uptime Kuma" $STD git clone https://github.com/louislam/uptime-kuma.git diff --git a/install/watcharr-install.sh b/install/watcharr-install.sh index ae8219211..7e2b4c15d 100644 --- a/install/watcharr-install.sh +++ b/install/watcharr-install.sh @@ -18,8 +18,8 @@ $STD apt-get install -y \ gcc msg_ok "Installed Dependencies" -install_go -NODE_VERSION="22" install_node_and_modules +setup_go +NODE_VERSION="22" setup_nodejs msg_info "Setup Watcharr" temp_file=$(mktemp) diff --git a/install/wavelog-install.sh b/install/wavelog-install.sh index f2a86514d..b1c54150d 100644 --- a/install/wavelog-install.sh +++ b/install/wavelog-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ php8.2-{curl,mbstring,mysql,xml,zip,gd} msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" DB_NAME=wavelog diff --git a/install/web-check-install.sh b/install/web-check-install.sh index 6ad50f6cf..807ab31f2 100644 --- a/install/web-check-install.sh +++ b/install/web-check-install.sh @@ -35,7 +35,7 @@ $STD apt-get -y install --no-install-recommends \ x11-apps msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="yarn@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs msg_info "Setup Python3" $STD apt-get install -y python3 diff --git a/install/wger-install.sh b/install/wger-install.sh index 69eccaabd..0134b0c3d 100644 --- a/install/wger-install.sh +++ b/install/wger-install.sh @@ -15,9 +15,9 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - apache2 \ - libapache2-mod-wsgi-py3 + git \ + apache2 \ + libapache2-mod-wsgi-py3 msg_ok "Installed Dependencies" msg_info "Installing Python" @@ -25,7 +25,7 @@ $STD apt-get install -y python3-pip rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Installed Python" -NODE_VERSION="22" NODE_MODULE="yarn@latest,sass" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="yarn@latest,sass" setup_nodejs msg_info "Setting up wger" $STD adduser wger --disabled-password --gecos "" diff --git a/install/wikijs-install.sh b/install/wikijs-install.sh index 0b944b1de..5bbfae492 100644 --- a/install/wikijs-install.sh +++ b/install/wikijs-install.sh @@ -18,8 +18,8 @@ $STD apt-get install -y \ git msg_ok "Installed Dependencies" -NODE_VERSION="20" NODE_MODULE="yarn@latest,node-gyp" install_node_and_modules -PG_VERSION="17" install_postgresql +NODE_VERSION="20" NODE_MODULE="yarn@latest,node-gyp" setup_nodejs +PG_VERSION="17" setup_postgresql msg_info "Set up PostgreSQL" DB_NAME="wiki" diff --git a/install/wordpress-install.sh b/install/wordpress-install.sh index a52a3fec8..5cac5e956 100644 --- a/install/wordpress-install.sh +++ b/install/wordpress-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y \ libapache2-mod-php msg_ok "Installed Dependencies" -install_mariadb +setup_mariadb msg_info "Setting up Database" DB_NAME=wordpress_db diff --git a/install/zigbee2mqtt-install.sh b/install/zigbee2mqtt-install.sh index f2f3070f5..0e6ca49ff 100644 --- a/install/zigbee2mqtt-install.sh +++ b/install/zigbee2mqtt-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ ca-certificates msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs msg_info "Setting up Zigbee2MQTT" cd /opt diff --git a/install/zipline-install.sh b/install/zipline-install.sh index c23f685a6..c453d6712 100644 --- a/install/zipline-install.sh +++ b/install/zipline-install.sh @@ -14,8 +14,8 @@ setting_up_container network_check update_os -NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules -PG_VERSION="16" install_postgresql +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME=ziplinedb diff --git a/install/zitadel-install.sh b/install/zitadel-install.sh index a97c68f86..ac26c7207 100644 --- a/install/zitadel-install.sh +++ b/install/zitadel-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies (Patience)" $STD apt-get install -y ca-certificates msg_ok "Installed Dependecies" -PG_VERSION="17" PG_MODULES="common" install_postgresql +PG_VERSION="17" PG_MODULES="common" setup_postgresql msg_info "Installing Postgresql" DB_NAME="zitadel" From 8f0751442d4a09aba24673565edd2f937583e508 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:03:27 +0100 Subject: [PATCH 061/139] Update CHANGELOG.md (#5243) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4e42fe1..78d5363a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-18 +### 🚀 Updated Scripts + + - #### 🔧 Refactor + + - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) + ## 2025-06-17 ### 🚀 Updated Scripts From 93f9291d7c77b444c7a692dd6ca5cdfee2ba9284 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:07:08 +0200 Subject: [PATCH 062/139] tools.func: Standardized and Renamed Setup Functions (#5241) * Upgrade Tools.func * Update tools.func * Update tools.func --- misc/tools.func | 741 +++++++++++++++++++++++++++++------------------- 1 file changed, 457 insertions(+), 284 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 4f64e9e14..fd9eff384 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -12,7 +12,7 @@ # NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") # ------------------------------------------------------------------------------ -install_node_and_modules() { +function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-22}" local NODE_MODULE="${NODE_MODULE:-}" local CURRENT_NODE_VERSION="" @@ -22,20 +22,17 @@ install_node_and_modules() { if command -v node >/dev/null; then CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Node.js version $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" NEED_NODE_INSTALL=true - else - msg_ok "Node.js $NODE_VERSION already installed" fi else - msg_info "Node.js not found, installing version $NODE_VERSION" + msg_info "Setup Node.js $NODE_VERSION" NEED_NODE_INSTALL=true fi if ! command -v jq &>/dev/null; then - $STD msg_info "Installing jq..." - $STD apt-get update -qq &>/dev/null - $STD apt-get install -y jq &>/dev/null || { + $STD apt-get update + $STD apt-get install -y jq || { msg_error "Failed to install jq" return 1 } @@ -67,7 +64,7 @@ install_node_and_modules() { exit 1 fi - msg_ok "Installed Node.js ${NODE_VERSION}" + msg_ok "Setup Node.js ${NODE_VERSION}" fi export NODE_OPTIONS="--max-old-space-size=4096" @@ -106,8 +103,6 @@ install_node_and_modules() { msg_error "Failed to update $MODULE_NAME to latest version" exit 1 fi - else - msg_ok "$MODULE_NAME@$MODULE_INSTALLED_VERSION already installed" fi else msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" @@ -117,7 +112,7 @@ install_node_and_modules() { fi fi done - msg_ok "All requested Node modules have been processed" + msg_ok "Installed Node.js modules: $NODE_MODULE" fi } @@ -135,7 +130,7 @@ install_node_and_modules() { # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) # PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") # ------------------------------------------------------------------------------ -install_postgresql() { +function setup_postgresql() { local PG_VERSION="${PG_VERSION:-16}" local PG_MODULES="${PG_MODULES:-}" local CURRENT_PG_VERSION="" @@ -146,7 +141,7 @@ install_postgresql() { if command -v psql >/dev/null; then CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then - msg_ok "PostgreSQL $PG_VERSION is already installed" + : # PostgreSQL is already at the desired version – no action needed else msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" NEED_PG_INSTALL=true @@ -158,39 +153,41 @@ install_postgresql() { if [[ "$NEED_PG_INSTALL" == true ]]; then if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Dumping all PostgreSQL data from version $CURRENT_PG_VERSION" + msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + msg_ok "Data dump completed" - msg_info "Stopping PostgreSQL service" systemctl stop postgresql fi - msg_info "Removing pgdg repo and old GPG key" rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg - msg_info "Adding PostgreSQL PGDG repository" + $STD msg_info "Adding PostgreSQL PGDG repository" curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ >/etc/apt/sources.list.d/pgdg.list + $STD msg_ok "Repository added" $STD apt-get update - msg_info "Installing PostgreSQL $PG_VERSION" + msg_info "Setup PostgreSQL $PG_VERSION" $STD apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + msg_ok "Setup PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Removing old PostgreSQL $CURRENT_PG_VERSION packages" $STD apt-get purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true fi - msg_info "Starting PostgreSQL $PG_VERSION" + $STD msg_info "Starting PostgreSQL $PG_VERSION" systemctl enable -q --now postgresql + $STD msg_ok "PostgreSQL $PG_VERSION started" if [[ -n "$CURRENT_PG_VERSION" ]]; then msg_info "Restoring dumped data" su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + msg_ok "Data restored" fi msg_ok "PostgreSQL $PG_VERSION installed" @@ -201,13 +198,13 @@ install_postgresql() { IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do local pkg="postgresql-${PG_VERSION}-${module}" - msg_info "Installing PostgreSQL module: $pkg" + msg_info "Setup PostgreSQL module/s: $pkg" $STD apt-get install -y "$pkg" || { msg_error "Failed to install $pkg" continue } done - msg_ok "All requested PostgreSQL modules installed" + msg_ok "Setup PostgreSQL modules" fi } @@ -223,12 +220,13 @@ install_postgresql() { # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) # ------------------------------------------------------------------------------ -install_mariadb() { +setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" local DISTRO_CODENAME DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + 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" @@ -259,12 +257,12 @@ install_mariadb() { fi if [[ -n "$CURRENT_VERSION" ]]; then - $STD msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)" + $STD msg_info "Upgrading MariaDB $CURRENT_VERSION to $MARIADB_VERSION" $STD systemctl stop mariadb >/dev/null 2>&1 || true $STD apt-get purge -y 'mariadb*' || true rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg else - msg_info "Setup MariaDB $MARIADB_VERSION" + $STD msg_info "Setup MariaDB $MARIADB_VERSION" fi $STD msg_info "Setting up MariaDB Repository" @@ -292,46 +290,47 @@ install_mariadb() { # MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) # ------------------------------------------------------------------------------ -install_mysql() { +function setup_mysql() { local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" local CURRENT_VERSION="" local NEED_INSTALL=false + CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" if command -v mysql >/dev/null; then CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION found, replacing with $MYSQL_VERSION" + $STD msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" NEED_INSTALL=true else # Check for patch-level updates if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD msg_info "MySQL $CURRENT_VERSION available for upgrade" $STD apt-get update $STD apt-get install --only-upgrade -y mysql-server - msg_ok "MySQL upgraded" + $STD msg_ok "MySQL upgraded" fi return fi else - msg_info "Installing MySQL $MYSQL_VERSION" + msg_info "Setup MySQL $MYSQL_VERSION" NEED_INSTALL=true fi if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD systemctl stop mysql || true $STD apt-get purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg local DISTRO_CODENAME DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/debian/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/${CURRENT_OS}/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ >/etc/apt/sources.list.d/mysql.list export DEBIAN_FRONTEND=noninteractive $STD apt-get update $STD apt-get install -y mysql-server - msg_ok "Installed MySQL $MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" fi } @@ -354,7 +353,26 @@ install_mysql() { # PHP_MAX_EXECUTION_TIME - (default: 300) # ------------------------------------------------------------------------------ -install_php() { +# ------------------------------------------------------------------------------ +# Installs PHP with selected modules and configures Apache/FPM support. +# +# Description: +# - Adds Sury PHP repo if needed +# - Installs default and user-defined modules +# - Patches php.ini for CLI, Apache, and FPM as needed +# +# Variables: +# PHP_VERSION - PHP version to install (default: 8.4) +# PHP_MODULE - Additional comma-separated modules +# PHP_APACHE - Set YES to enable PHP with Apache +# PHP_FPM - Set YES to enable PHP-FPM +# PHP_MEMORY_LIMIT - (default: 512M) +# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) +# PHP_POST_MAX_SIZE - (default: 128M) +# PHP_MAX_EXECUTION_TIME - (default: 300) +# ------------------------------------------------------------------------------ + +function setup_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" local PHP_APACHE="${PHP_APACHE:-NO}" @@ -386,9 +404,9 @@ install_php() { fi if [[ -z "$CURRENT_PHP" ]]; then - msg_info "No PHP found, setting up PHP $PHP_VERSION" + msg_info "Setup PHP $PHP_VERSION" elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true fi @@ -423,7 +441,7 @@ install_php() { fi $STD apt-get install -y $MODULE_LIST - msg_ok "Installed PHP $PHP_VERSION with selected modules" + msg_ok "Setup PHP $PHP_VERSION" if [[ "$PHP_APACHE" == "YES" ]]; then $STD systemctl restart apache2 || true @@ -450,6 +468,7 @@ install_php() { fi done } + # ------------------------------------------------------------------------------ # Installs or updates Composer globally. # @@ -458,7 +477,7 @@ install_php() { # - Installs to /usr/local/bin/composer # ------------------------------------------------------------------------------ -install_composer() { +function setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" export COMPOSER_ALLOW_SUPERUSER=1 @@ -466,7 +485,7 @@ install_composer() { if [[ -x "$COMPOSER_BIN" ]]; then local CURRENT_VERSION CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') - $STD msg_info "Composer $CURRENT_VERSION found, updating to latest" + $STD msg_info "Old Composer $CURRENT_VERSION found, updating to latest" else msg_info "Setup Composer" fi @@ -483,7 +502,6 @@ install_composer() { chmod +x "$COMPOSER_BIN" composer diagnose >/dev/null 2>&1 msg_ok "Setup Composer" - #msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')" } # ------------------------------------------------------------------------------ @@ -497,7 +515,7 @@ install_composer() { # GO_VERSION - Version to install (e.g. 1.22.2 or latest) # ------------------------------------------------------------------------------ -install_go() { +function setup_go() { local ARCH case "$(uname -m)" in x86_64) ARCH="amd64" ;; @@ -515,7 +533,6 @@ install_go() { msg_error "Could not determine latest Go version" return 1 fi - $STD msg_info "Detected latest Go version: $GO_VERSION" fi local GO_BIN="/usr/local/bin/go" @@ -525,14 +542,13 @@ install_go() { local CURRENT_VERSION CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - $STD msg_ok "Go $GO_VERSION already installed" return 0 else - $STD msg_info "Go $CURRENT_VERSION found, upgrading to $GO_VERSION" + $STD msg_info "Old Go Installation ($CURRENT_VERSION) found, upgrading to $GO_VERSION" rm -rf "$GO_INSTALL_DIR" fi else - msg_info "Installing Go $GO_VERSION" + msg_info "Setup Go $GO_VERSION" fi local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" @@ -549,7 +565,7 @@ install_go() { ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" - msg_ok "Installed Go $GO_VERSION" + msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -563,7 +579,7 @@ install_go() { # JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) # ------------------------------------------------------------------------------ -install_java() { +function setup_java() { local JAVA_VERSION="${JAVA_VERSION:-21}" local DISTRO_CODENAME DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) @@ -571,13 +587,13 @@ install_java() { # Add Adoptium repo if missing if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then - msg_info "Setting up Adoptium Repository" + $STD msg_info "Setting up Adoptium Repository" mkdir -p /etc/apt/keyrings curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ >/etc/apt/sources.list.d/adoptium.list $STD apt-get update - msg_ok "Set up Adoptium Repository" + $STD msg_ok "Set up Adoptium Repository" fi # Detect currently installed temurin version @@ -587,19 +603,19 @@ install_java() { fi if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - msg_info "Temurin JDK $JAVA_VERSION already installed, updating if needed" + $STD msg_info "Upgrading Temurin JDK $JAVA_VERSION" $STD apt-get update $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" - msg_ok "Updated Temurin JDK $JAVA_VERSION (if applicable)" + $STD msg_ok "Upgraded Temurin JDK $JAVA_VERSION" else if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing Temurin JDK $INSTALLED_VERSION" + $STD msg_info "Removing Temurin JDK $INSTALLED_VERSION" $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" fi - msg_info "Installing Temurin JDK $JAVA_VERSION" + msg_info "Setup Temurin JDK $JAVA_VERSION" $STD apt-get install -y "$DESIRED_PACKAGE" - msg_ok "Installed Temurin JDK $JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" fi } @@ -614,7 +630,7 @@ install_java() { # MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) # ------------------------------------------------------------------------------ -install_mongodb() { +function setup_mongodb() { local MONGO_VERSION="${MONGO_VERSION:-8.0}" local DISTRO_ID DISTRO_CODENAME MONGO_BASE_URL DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) @@ -637,21 +653,20 @@ install_mongodb() { fi if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - msg_info "MongoDB $MONGO_VERSION already installed, checking for upgrade" + $STD msg_info "Upgrading MongoDB $MONGO_VERSION" $STD apt-get update $STD apt-get install --only-upgrade -y mongodb-org - msg_ok "MongoDB $MONGO_VERSION upgraded if needed" + $STD msg_ok "Upgraded MongoDB $MONGO_VERSION" return 0 fi if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Replacing MongoDB $INSTALLED_VERSION with $MONGO_VERSION (data will be preserved)" $STD systemctl stop mongod || true $STD apt-get purge -y mongodb-org || true rm -f /etc/apt/sources.list.d/mongodb-org-*.list rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg else - msg_info "Installing MongoDB $MONGO_VERSION" + msg_info "Setup MongoDB $MONGO_VERSION" fi curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" @@ -670,177 +685,243 @@ install_mongodb() { $STD systemctl enable mongod $STD systemctl start mongod - msg_ok "MongoDB $MONGO_VERSION installed and started" + msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ -# Downloads and deploys latest GitHub release tarball. +# Downloads and deploys latest GitHub release (source, binary, tarball, asset). # # Description: -# - Fetches latest release from GitHub API -# - Detects matching asset by architecture -# - Extracts to /opt/ and saves version +# - Fetches latest release metadata from GitHub API +# - Supports the following modes: +# - tarball: Source code tarball (default if omitted) +# - source: Alias for tarball (same behavior) +# - binary: .deb package install (arch-dependent) +# - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries) +# - singlefile: Standalone binary (no archive, direct chmod +x install) +# - Handles download, extraction/installation and version tracking in ~/. # -# Variables: -# APP - Override default application name (optional) -# GITHUB_TOKEN - (optional) GitHub token for private rate limits +# Parameters: +# $1 APP - Application name (used for install path and version file) +# $2 REPO - GitHub repository in form user/repo +# $3 MODE - Release type: +# tarball → source tarball (.tar.gz) +# binary → .deb file (auto-arch matched) +# prebuild → prebuilt archive (e.g. tar.gz) +# singlefile→ standalone binary (chmod +x) +# $4 VERSION - Optional release tag (default: latest) +# $5 TARGET_DIR - Optional install path (default: /opt/) +# $6 ASSET_FILENAME - Required for: +# - prebuild → archive filename or pattern +# - singlefile→ binary filename or pattern +# +# Optional: +# - Set GITHUB_TOKEN env var to increase API rate limit (recommended for CI/CD). +# +# Examples: +# # 1. Minimal: Fetch and deploy source tarball +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" +# +# # 2. Binary install via .deb asset (architecture auto-detected) +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" "binary" +# +# # 3. Prebuilt archive (.tar.gz) with asset filename match +# fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" +# +# # 4. Single binary (chmod +x) like Argus, Promtail etc. +# fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "0.26.3" "/opt/argus" "Argus-.*linux-amd64" # ------------------------------------------------------------------------------ -fetch_and_deploy_gh_release() { - local repo="$1" - local raw_app="${APP:-$APPLICATION}" - local app=$(echo "${raw_app,,}" | tr -d ' ') - local api_url="https://api.github.com/repos/$repo/releases/latest" - local header=() - local attempt=0 - local max_attempts=3 - local api_response tag http_code - local current_version="" +function fetch_and_deploy_gh_release() { + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" local curl_timeout="--connect-timeout 10 --max-time 30" - # Check if the app directory exists and if there's a version file - if [[ -f "/opt/${app}_version.txt" ]]; then - current_version=$(cat "/opt/${app}_version.txt") - $STD msg_info "Current version: $current_version" + + local current_version="" + if [[ -f "$version_file" ]]; then + current_version=$(<"$version_file") fi - # ensure that jq is installed + if ! command -v jq &>/dev/null; then - $STD msg_info "Installing jq..." - $STD apt-get update -qq &>/dev/null - $STD apt-get install -y jq &>/dev/null || { - msg_error "Failed to install jq" - return 1 - } + $STD apt-get install -y jq &>/dev/null fi + + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - until [[ $attempt -ge $max_attempts ]]; do - ((attempt++)) || true - $STD msg_info "[$attempt/$max_attempts] Fetching GitHub release for $repo...\n" - api_response=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_resp.json "${header[@]}" "$api_url") - http_code="${api_response:(-3)}" - if [[ "$http_code" == "404" ]]; then - msg_error "Repository $repo has no Release candidate (404)" - return 1 - fi - if [[ "$http_code" != "200" ]]; then - $STD msg_info "Request failed with HTTP $http_code, retrying...\n" - sleep $((attempt * 2)) - continue - fi - api_response=$(/dev/null; then - msg_error "Repository not found: $repo" - return 1 - fi - tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty') - [[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}" - version="${tag#v}" - if [[ -z "$tag" ]]; then - $STD msg_info "Empty tag received, retrying...\n" - sleep $((attempt * 2)) - continue - fi - $STD msg_ok "Found release: $tag for $repo" - break - done - if [[ -z "$tag" ]]; then - msg_error "Failed to fetch release for $repo after $max_attempts attempts." - exit 1 - fi - # Version comparison (if we already have this version, skip) - if [[ "$current_version" == "$tag" ]]; then - $STD msg_info "Already running the latest version ($tag). Skipping update." + + local resp http_code + resp=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "Failed to fetch release: HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null; then - arch=$(dpkg --print-architecture) - elif command -v uname &>/dev/null; then - case "$(uname -m)" in - x86_64) arch="amd64" ;; - aarch64) arch="arm64" ;; - armv7l) arch="armv7" ;; - armv6l) arch="armv6" ;; - *) arch="unknown" ;; - esac + local filename="" url="" + + msg_info "Setup $app ($version)" + + if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then + url=$(echo "$json" | jq -r '.tarball_url // empty') + [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + filename="${app_lc}-${version}.tar.gz" + + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url" || { + msg_error "Download failed: $url" + rm -rf "$tmpdir" + return 1 + } + + mkdir -p "$target" + tar -xzf "$tmpdir/$filename" -C "$tmpdir" + local unpack_dir + unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + + shopt -s dotglob nullglob + cp -r "$unpack_dir"/* "$target/" + shopt -u dotglob nullglob + + elif [[ "$mode" == "binary" ]]; then + local arch + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="x86_64" + [[ "$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 [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi + + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi + + filename="${url_match##*/}" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + + chmod 644 "$tmpdir/$filename" + $STD apt-get install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + elif [[ "$mode" == "prebuild" ]]; then + local pattern="$6" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + [[ "$u" =~ $pattern || "$u" == *"$pattern" ]] && asset_url="$u" && break + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + mkdir -p "$target" + if [[ "$filename" == *.zip ]]; then + if ! command -v unzip &>/dev/null; then + $STD apt-get install -y unzip + fi + $STD unzip "$tmpdir/$filename" -d "$target" + elif [[ "$filename" == *.tar.gz ]]; then + tar -xzf "$tmpdir/$filename" -C "$target" + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" + return 1 + fi + + elif [[ "$mode" == "singlefile" ]]; then + local pattern="$6" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + [[ "$u" =~ $pattern || "$u" == *"$pattern" ]] && asset_url="$u" && break + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + curl $curl_timeout -fsSL -o "$target/$app" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + chmod +x "$target/$app" + else - arch="unknown" - fi - $STD msg_info "Detected system architecture: $arch" - # Try to find a matching asset for our architecture - local url="" - for u in $assets; do - if [[ "$u" =~ $arch.*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Found matching architecture asset: $url" - break - fi - done - # Fallback to other architectures if our specific one isn't found - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ (x86_64|amd64|arm64|armv7|armv6).*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Architecture-specific asset not found, using: $url" - break - fi - done - fi - # Fallback to any tar.gz - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ \.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Using generic tarball: $url" - break - fi - done - fi - # Final fallback to GitHub source tarball - if [[ -z "$url" ]]; then - # Use tarball_url directly from API response instead of constructing our own URL - url=$(echo "$api_response" | jq -r '.tarball_url // empty') - - # If tarball_url is empty for some reason, fall back to a constructed URL as before - if [[ -z "$url" ]]; then - url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" - fi - - $STD msg_info "Using GitHub source tarball: $url" - fi - local filename="${url##*/}" - $STD msg_info "Downloading $url" - if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then - msg_error "Failed to download release asset from $url" + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 1 fi - mkdir -p "/opt/$app" - tar -xzf "$tmpdir/$filename" -C "$tmpdir" - local content_root - content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) - if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then - shopt -s dotglob nullglob - cp -r "$content_root"/* "/opt/$app/" - shopt -u dotglob nullglob - else - shopt -s dotglob nullglob - cp -r "$tmpdir"/* "/opt/$app/" - shopt -u dotglob nullglob - fi - echo "$version" >"/opt/${app}_version.txt" - $STD msg_ok "Deployed $app v$version to /opt/$app" + + echo "$version" >"$version_file" + msg_ok "Setup $app ($version)" rm -rf "$tmpdir" } @@ -852,7 +933,7 @@ fetch_and_deploy_gh_release() { # - Automatically runs on network changes # ------------------------------------------------------------------------------ -setup_local_ip_helper() { +function setup_local_ip_helper() { local BASE_DIR="/usr/local/community-scripts/ip-management" local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" local IP_FILE="/run/local-ip.env" @@ -862,8 +943,8 @@ setup_local_ip_helper() { # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt-get update -qq - $STD apt-get install -yq networkd-dispatcher + $STD apt-get update + $STD apt-get install -y networkd-dispatcher fi # Write update_local_ip.sh @@ -920,8 +1001,6 @@ EOF chmod +x "$DISPATCHER_SCRIPT" systemctl enable -q --now networkd-dispatcher.service - - $STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher" } # ------------------------------------------------------------------------------ @@ -931,7 +1010,7 @@ EOF # - Loads from /run/local-ip.env or performs runtime lookup # ------------------------------------------------------------------------------ -import_local_ip() { +function import_local_ip() { local IP_FILE="/run/local-ip.env" if [[ -f "$IP_FILE" ]]; then # shellcheck disable=SC1090 @@ -991,7 +1070,6 @@ function download_with_progress() { content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) if [[ -z "$content_length" ]]; then - #msg_warn "Content-Length not available, falling back to plain download" if ! curl -fL# -o "$output" "$url"; then msg_error "Download failed" return 1 @@ -1006,63 +1084,102 @@ function download_with_progress() { # ------------------------------------------------------------------------------ # Installs or upgrades uv (Python package manager) from GitHub releases. -# -# Description: -# - Downloads architecture-specific tarball -# - Places binary in /usr/local/bin +# - Downloads platform-specific tarball (no install.sh!) +# - Extracts uv binary +# - Places it in /usr/local/bin +# - Optionally installs a specific Python version via uv # ------------------------------------------------------------------------------ function setup_uv() { - $STD msg_info "Checking uv installation..." - UV_BIN="/usr/local/bin/uv" + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR TMP_DIR=$(mktemp -d) - ARCH=$(uname -m) - if [[ "$ARCH" == "x86_64" ]]; then - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - elif [[ "$ARCH" == "aarch64" ]]; then - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - else + # Determine system architecture + local ARCH + ARCH=$(uname -m) + local UV_TAR + + case "$ARCH" in + x86_64) UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" ;; + aarch64) UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" ;; + *) msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" return 1 - fi + ;; + esac + + # Get latest version from GitHub + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | + grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') - # get current github version - LATEST_VERSION=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not fetch latest uv version from GitHub." rm -rf "$TMP_DIR" return 1 fi - # check if uv exists + # Check if uv is already up to date if [[ -x "$UV_BIN" ]]; then + local INSTALLED_VERSION INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - $STD msg_ok "uv is already at the latest version ($INSTALLED_VERSION)" rm -rf "$TMP_DIR" - # set path - if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then - export PATH="/usr/local/bin:$PATH" - fi + [[ ":$PATH:" != *":/usr/local/bin:"* ]] && export PATH="/usr/local/bin:$PATH" return 0 else - $STD msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" + msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" fi else - $STD msg_info "uv not found. Installing version $LATEST_VERSION" + msg_info "Setup uv $LATEST_VERSION" fi - # install or update uv - curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" -o "$TMP_DIR/uv.tar.gz" - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" - rm -rf "$TMP_DIR" + # Download and install manually + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + if ! curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz"; then + msg_error "Failed to download $UV_URL" + rm -rf "$TMP_DIR" + return 1 + fi - # set path + if ! tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract uv archive" + rm -rf "$TMP_DIR" + return 1 + fi + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" ensure_usr_local_bin_persist - msg_ok "uv installed/updated to $LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" + + # Optional: install specific Python version + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found via uv" + return 1 + fi + + if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + if ! $STD uv python install "$VERSION_MATCH"; then + msg_error "Failed to install Python $VERSION_MATCH via uv" + return 1 + fi + msg_ok "Setup Python $VERSION_MATCH via uv" + fi + fi } # ------------------------------------------------------------------------------ @@ -1090,7 +1207,6 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function setup_gs() { - msg_info "Setup Ghostscript" mkdir -p /tmp TMP_DIR=$(mktemp -d) CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") @@ -1106,12 +1222,11 @@ function setup_gs() { fi if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED"; then - msg_ok "Ghostscript is already at version $CURRENT_VERSION" rm -rf "$TMP_DIR" return fi - msg_info "Installing/Updating Ghostscript to $LATEST_VERSION_DOTTED" + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then @@ -1137,7 +1252,7 @@ function setup_gs() { rm -rf "$TMP_DIR" if [[ $EXIT_CODE -eq 0 ]]; then - msg_ok "Ghostscript installed/updated to version $LATEST_VERSION_DOTTED" + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" else msg_error "Ghostscript installation failed" fi @@ -1156,7 +1271,7 @@ function setup_gs() { # RUBY_INSTALL_RAILS - true/false to install Rails (default: true) # ------------------------------------------------------------------------------ -setup_rbenv_stack() { +function setup_ruby() { local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" @@ -1166,9 +1281,8 @@ setup_rbenv_stack() { local TMP_DIR TMP_DIR=$(mktemp -d) - $STD msg_info "Installing rbenv + ruby-build + Ruby $RUBY_VERSION" + msg_info "Setup Ruby $RUBY_VERSION" - # Fetch latest rbenv release tag from GitHub (e.g. v1.3.2 → 1.3.2) local RBENV_RELEASE RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$RBENV_RELEASE" ]]; then @@ -1177,14 +1291,12 @@ setup_rbenv_stack() { return 1 fi - # Download and extract rbenv release curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" mkdir -p "$RBENV_DIR" cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && make -C src + cd "$RBENV_DIR" && src/configure && $STD make -C src - # Fetch latest ruby-build plugin release tag (e.g. v20250507 → 20250507) local RUBY_BUILD_RELEASE RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') if [[ -z "$RUBY_BUILD_RELEASE" ]]; then @@ -1193,44 +1305,35 @@ setup_rbenv_stack() { return 1 fi - # Download and install ruby-build plugin curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" mkdir -p "$RBENV_DIR/plugins/ruby-build" cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" - # Persist rbenv init to user's profile if ! grep -q 'rbenv init' "$PROFILE_FILE"; then echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" fi - # Activate rbenv in current shell export PATH="$RBENV_DIR/bin:$PATH" eval "$("$RBENV_BIN" init - bash)" - # Install Ruby version if not already present - if "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - msg_ok "Ruby $RUBY_VERSION already installed" - else - $STD msg_info "Installing Ruby $RUBY_VERSION" + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then $STD "$RBENV_BIN" install "$RUBY_VERSION" fi - # Set Ruby version globally "$RBENV_BIN" global "$RUBY_VERSION" hash -r - # Optionally install Rails via gem if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD msg_info "Installing latest Rails via gem" + msg_info "Setup Rails via gem" gem install rails - msg_ok "Rails $(rails -v) installed" + msg_ok "Setup Rails $(rails -v)" fi rm -rf "$TMP_DIR" - msg_ok "rbenv stack ready (Ruby $RUBY_VERSION)" + msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -1242,14 +1345,12 @@ setup_rbenv_stack() { # Variables: # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ -create_selfsigned_certs() { +function create_selfsigned_certs() { local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')} - $STD msg_info "Creating Self-Signed Certificate" $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 \ -keyout /etc/ssl/private/"$app"-selfsigned.key \ -out /etc/ssl/certs/"$app"-selfsigned.crt \ -subj "/C=US/O=$app/OU=Domain Control Validated/CN=localhost" - $STD msg_ok "Created Self-Signed Certificate" } # ------------------------------------------------------------------------------ @@ -1269,18 +1370,18 @@ create_selfsigned_certs() { # RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") # ------------------------------------------------------------------------------ -install_rust_and_crates() { +function setup_rust() { local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" local RUST_CRATES="${RUST_CRATES:-}" local CARGO_BIN="${HOME}/.cargo/bin" # rustup & toolchain if ! command -v rustup &>/dev/null; then - msg_info "Installing rustup" + msg_info "Setup Rust" curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" export PATH="$CARGO_BIN:$PATH" echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - msg_ok "Installed rustup with $RUST_TOOLCHAIN" + msg_ok "Setup Rust" else $STD rustup install "$RUST_TOOLCHAIN" $STD rustup default "$RUST_TOOLCHAIN" @@ -1305,20 +1406,21 @@ install_rust_and_crates() { if [[ -n "$INSTALLED_VER" ]]; then if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Updating $NAME from $INSTALLED_VER to $VER" + msg_info "Update $NAME: $INSTALLED_VER → $VER" $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" elif [[ -z "$VER" ]]; then - msg_info "Updating $NAME to latest" + msg_info "Update $NAME: $INSTALLED_VER → latest" $STD cargo install "$NAME" --force - else - msg_ok "$NAME@$INSTALLED_VER already up to date" + msg_ok "Updated $NAME to latest" fi else - msg_info "Installing $NAME ${VER:+($VER)}" + msg_info "Setup $NAME ${VER:+($VER)}" $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Setup $NAME ${VER:-latest}" fi done - msg_ok "All requested Rust crates processed" + msg_ok "Setup Rust" fi } @@ -1330,9 +1432,9 @@ install_rust_and_crates() { # - Supports Alpine and Debian-based systems # ------------------------------------------------------------------------------ -install_adminer() { +function setup_adminer() { if grep -qi alpine /etc/os-release; then - msg_info "Installing Adminer (Alpine)" + msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer if ! curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ -o /var/www/localhost/htdocs/adminer/index.php; then @@ -1341,10 +1443,81 @@ install_adminer() { fi msg_ok "Adminer available at /adminer (Alpine)" else - msg_info "Installing Adminer (Debian/Ubuntu)" + msg_info "Setup Adminer (Debian/Ubuntu)" $STD apt-get install -y adminer $STD a2enconf adminer $STD systemctl reload apache2 msg_ok "Adminer available at /adminer (Debian/Ubuntu)" fi } + +# ------------------------------------------------------------------------------ +# Installs or updates yq (mikefarah/yq - Go version). +# +# Description: +# - Checks if yq is installed and from correct source +# - Compares with latest release on GitHub +# - Updates if outdated or wrong implementation +# ------------------------------------------------------------------------------ + +function setup_yq() { + local TMP_DIR + TMP_DIR=$(mktemp -d) + local CURRENT_VERSION="" + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" + + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq || { + msg_error "Failed to install jq" + rm -rf "$TMP_DIR" + return 1 + } + fi + + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + else + CURRENT_VERSION=$(yq --version | awk '{print $NF}' | sed 's/^v//') + fi + fi + + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^v//') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest yq version from GitHub." + rm -rf "$TMP_DIR" + return 1 + fi + + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + return + fi + + msg_info "Setup yq ($LATEST_VERSION)" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "Failed to install yq to $BINARY_PATH" + rm -rf "$TMP_DIR" + return 1 + fi + + rm -rf "$TMP_DIR" + hash -r + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}') + if [[ "$FINAL_VERSION" == "v$LATEST_VERSION" ]]; then + msg_ok "Setup yq ($LATEST_VERSION)" + else + msg_error "yq installation incomplete or version mismatch" + fi +} From e9afe958b111ee79b10b70a00831545abf9862cf Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:07:44 +0100 Subject: [PATCH 063/139] Update CHANGELOG.md (#5244) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d5363a2..2e9af697c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts + - tools.func: Standardized and Renamed Setup Functions [@MickLesk](https://github.com/MickLesk) ([#5241](https://github.com/community-scripts/ProxmoxVE/pull/5241)) + - #### 🔧 Refactor - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) From bece1c574f69e84bbeb1fd3c9146dc4bbdfd4144 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 18 Jun 2025 06:34:33 -0400 Subject: [PATCH 064/139] Immich: fix prompt clobber issue (#5231) * Immich: fix prompt clobber issue * change functions to new tools.func * duplicate --------- Co-authored-by: CanbiZ <47820557+MickLesk@users.noreply.github.com> --- install/immich-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/immich-install.sh b/install/immich-install.sh index 367e68472..65f130310 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -84,10 +84,7 @@ ln -s /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/bin/ffmpeg ln -s /usr/lib/jellyfin-ffmpeg/ffprobe /usr/bin/ffprobe msg_ok "Dependencies Installed" -NODE_VERSION="22" setup_nodejs -PG_VERSION="16" setup_postgresql - -read -r -p "${TAB3}Install OpenVINO dependencies for Intel HW-accelerated machine-learning? " prompt +read -r -p "Install OpenVINO dependencies for Intel HW-accelerated machine-learning? y/N " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing OpenVINO dependencies" touch ~/.openvino @@ -112,6 +109,9 @@ if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_ok "Installed OpenVINO dependencies" fi +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql + msg_info "Setting up Postgresql Database" $STD apt-get install postgresql-16-pgvector curl -fsSL https://github.com/tensorchord/VectorChord/releases/download/0.3.0/postgresql-16-vchord_0.3.0-1_amd64.deb -o vchord.deb From a377640d1654cd90c4f594b7290cfeb62b17d0c1 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 11:35:09 +0100 Subject: [PATCH 065/139] Update CHANGELOG.md (#5246) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e9af697c..88bf8be8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ All LXC instances created using this repository come pre-installed with Midnight - tools.func: Standardized and Renamed Setup Functions [@MickLesk](https://github.com/MickLesk) ([#5241](https://github.com/community-scripts/ProxmoxVE/pull/5241)) + - #### 🐞 Bug Fixes + + - Immich: fix prompt clobber issue [@vhsdream](https://github.com/vhsdream) ([#5231](https://github.com/community-scripts/ProxmoxVE/pull/5231)) + - #### 🔧 Refactor - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) From 3ee4ece04d7568c2053b942bfc615f10f31f1cd0 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:07:56 +0200 Subject: [PATCH 066/139] Update versions.json (#5247) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 94 +++++++++++++++--------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 5c413305e..a2b87e24f 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,9 +1,54 @@ [ + { + "name": "esphome/esphome", + "version": "2025.6.0", + "date": "2025-06-18T09:41:11Z" + }, + { + "name": "forgejo/forgejo", + "version": "v11.0.2", + "date": "2025-06-18T09:38:19Z" + }, + { + "name": "evcc-io/evcc", + "version": "0.204.3", + "date": "2025-06-18T08:31:58Z" + }, + { + "name": "silverbulletmd/silverbullet", + "version": "2.0.0-pre3", + "date": "2025-06-18T08:01:24Z" + }, + { + "name": "mattermost/mattermost", + "version": "v9.11.17", + "date": "2025-06-18T08:12:05Z" + }, + { + "name": "zabbix/zabbix", + "version": "7.4.0rc2", + "date": "2025-06-18T07:23:07Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2026", + "date": "2025-06-18T05:51:39Z" + }, + { + "name": "cross-seed/cross-seed", + "version": "v6.12.7", + "date": "2025-06-18T03:44:24Z" + }, { "name": "grafana/grafana", "version": "v11.5.6", "date": "2025-06-17T22:00:40Z" }, + { + "name": "keycloak/keycloak", + "version": "26.2.5", + "date": "2025-05-28T06:49:43Z" + }, { "name": "jenkinsci/jenkins", "version": "jenkins-2.515", @@ -64,21 +109,11 @@ "version": "10.0.18", "date": "2025-02-12T11:07:02Z" }, - { - "name": "mattermost/mattermost", - "version": "v10.9.1", - "date": "2025-06-17T07:06:51Z" - }, { "name": "morpheus65535/bazarr", "version": "v1.5.2", "date": "2025-05-11T16:40:55Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2024", - "date": "2025-06-17T05:51:55Z" - }, { "name": "donaldzou/WGDashboard", "version": "v4.2.4", @@ -89,11 +124,6 @@ "version": "2.402", "date": "2025-06-17T05:20:42Z" }, - { - "name": "keycloak/keycloak", - "version": "26.2.5", - "date": "2025-05-28T06:49:43Z" - }, { "name": "kimai/kimai", "version": "2.36.1", @@ -111,8 +141,8 @@ }, { "name": "runtipi/runtipi", - "version": "nightly", - "date": "2025-06-16T17:35:17Z" + "version": "v4.2.1", + "date": "2025-06-03T20:04:28Z" }, { "name": "NodeBB/NodeBB", @@ -159,11 +189,6 @@ "version": "v1.18.3", "date": "2025-06-16T07:03:46Z" }, - { - "name": "esphome/esphome", - "version": "2025.5.2", - "date": "2025-06-03T08:45:14Z" - }, { "name": "firefly-iii/firefly-iii", "version": "v6.2.17", @@ -329,11 +354,6 @@ "version": "v1.63.1", "date": "2025-06-11T11:05:42Z" }, - { - "name": "zabbix/zabbix", - "version": "7.2.8rc1", - "date": "2025-06-11T06:50:19Z" - }, { "name": "openobserve/openobserve", "version": "v0.15.0-rc2", @@ -374,11 +394,6 @@ "version": "v4.0.14.2939", "date": "2025-03-17T19:12:37Z" }, - { - "name": "cross-seed/cross-seed", - "version": "v6.12.6", - "date": "2025-05-28T00:13:19Z" - }, { "name": "tailscale/tailscale", "version": "v1.84.2", @@ -409,11 +424,6 @@ "version": "v0.10.0", "date": "2025-06-09T13:37:07Z" }, - { - "name": "silverbulletmd/silverbullet", - "version": "0.10.4", - "date": "2025-02-25T18:13:42Z" - }, { "name": "neo4j/neo4j", "version": "5.26.8", @@ -434,11 +444,6 @@ "version": "v1.5.8", "date": "2025-06-07T11:39:10Z" }, - { - "name": "evcc-io/evcc", - "version": "0.204.2", - "date": "2025-06-07T11:38:28Z" - }, { "name": "homebridge/homebridge", "version": "v1.10.0", @@ -794,11 +799,6 @@ "version": "3.5.0", "date": "2025-05-05T16:28:24Z" }, - { - "name": "forgejo/forgejo", - "version": "v11.0.1", - "date": "2025-05-02T17:10:30Z" - }, { "name": "jhuckaby/Cronicle", "version": "v0.9.80", From 4351218f8bde216cfa8dc55652c8365558b4ba23 Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:16:48 +0200 Subject: [PATCH 067/139] 'Add new script' (#5249) Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> --- ct/headers/huntarr | 6 +++ ct/huntarr.sh | 63 +++++++++++++++++++++++++++++++ frontend/public/json/huntarr.json | 35 +++++++++++++++++ install/huntarr-install.sh | 49 ++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 ct/headers/huntarr create mode 100644 ct/huntarr.sh create mode 100644 frontend/public/json/huntarr.json create mode 100644 install/huntarr-install.sh diff --git a/ct/headers/huntarr b/ct/headers/huntarr new file mode 100644 index 000000000..d81fc93cf --- /dev/null +++ b/ct/headers/huntarr @@ -0,0 +1,6 @@ + __ __ + / /_ __ ______ / /_____ ___________ + / __ \/ / / / __ \/ __/ __ `/ ___/ ___/ + / / / / /_/ / / / / /_/ /_/ / / / / +/_/ /_/\__,_/_/ /_/\__/\__,_/_/ /_/ + diff --git a/ct/huntarr.sh b/ct/huntarr.sh new file mode 100644 index 000000000..0a3023f38 --- /dev/null +++ b/ct/huntarr.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BiluliB +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plexguide/Huntarr.io + +APP="huntarr" +var_tags="${var_tags:-arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +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/huntarr/main.py ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + setup_uv + RELEASE=$(curl -fsSL https://api.github.com/repos/plexguide/Huntarr.io/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + if [[ -f ~/.huntarr && "${RELEASE}" == "$(cat ~/.huntarr)" ]]; then + msg_ok "No update required. ${APP} is already at ${RELEASE}" + exit + fi + msg_info "Stopping huntarr service" + systemctl stop huntarr + msg_ok "Stopped huntarr service" + + fetch_and_deploy_gh_release "huntarr" "plexguide/Huntarr.io" + msg_info "Updating $APP to v${RELEASE}" + cd /opt/huntarr + $STD uv pip install -r requirements.txt --python /opt/huntarr/.venv/bin/python + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start huntarr + msg_ok "Started $APP" + + msg_ok "Updated $APP to v${RELEASE}" + 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}:9705${CL}" diff --git a/frontend/public/json/huntarr.json b/frontend/public/json/huntarr.json new file mode 100644 index 000000000..08117a053 --- /dev/null +++ b/frontend/public/json/huntarr.json @@ -0,0 +1,35 @@ +{ + "name": "Huntarr", + "slug": "huntarr", + "categories": [ + 14 + ], + "date_created": "2025-05-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9705, + "documentation": "https://github.com/plexguide/Huntarr.io/wiki", + "config_path": "/opt/huntarr", + "website": "https://github.com/plexguide/Huntarr.io", + "logo": "https://raw.githubusercontent.com/plexguide/Huntarr.io/refs/heads/main/frontend/static/logo/Huntarr.svg", + "description": "Huntarr is a tool that automates the search for missing or low-quality media content in your collection. It works seamlessly with applications like Sonarr, Radarr, Lidarr, Readarr, and Whisparr, enhancing their functionality with continuous background scans to identify and update missed or outdated content. Through a user-friendly web interface accessible on port 9705, Huntarr provides real-time statistics, log views, and extensive configuration options. The software is especially useful for users who want to keep their media library up to date by automatically searching for missing episodes or higher-quality versions. Huntarr is well-suited for self-hosted environments and can easily run in LXC containers or Docker setups.", + "install_methods": [ + { + "type": "default", + "script": "ct/huntarr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/huntarr-install.sh b/install/huntarr-install.sh new file mode 100644 index 000000000..668ee870b --- /dev/null +++ b/install/huntarr-install.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BiluliB +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plexguide/Huntarr.io + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y jq +msg_ok "Installed Dependencies" + +setup_uv +fetch_and_deploy_gh_release "huntarr" "plexguide/Huntarr.io" + +msg_info "Configure Huntarr" +$STD uv venv /opt/huntarr/.venv +$STD uv pip install --python /opt/huntarr/.venv/bin/python -r /opt/huntarr/requirements.txt +msg_ok "Configured Huntrarr" + +msg_info "Creating Service" +cat </etc/systemd/system/huntarr.service +[Unit] +Description=Huntarr Service +After=network.target +[Service] +WorkingDirectory=/opt/huntarr +ExecStart=/opt/huntarr/.venv/bin/python /opt/huntarr/main.py +Restart=always +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now huntarr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 0dc533cb8203a6ba332e8330fafb98cc8c544b7a Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:17:18 +0100 Subject: [PATCH 068/139] Update CHANGELOG.md (#5250) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88bf8be8f..38eb2bffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-18 +### 🆕 New Scripts + + - Huntarr ([#5249](https://github.com/community-scripts/ProxmoxVE/pull/5249)) + ### 🚀 Updated Scripts - tools.func: Standardized and Renamed Setup Functions [@MickLesk](https://github.com/MickLesk) ([#5241](https://github.com/community-scripts/ProxmoxVE/pull/5241)) From 9ab2089d0195222ca231c444d17a0aef618791f2 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:17:22 +0100 Subject: [PATCH 069/139] Update date in json (#5251) Co-authored-by: GitHub Actions --- frontend/public/json/huntarr.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/public/json/huntarr.json b/frontend/public/json/huntarr.json index 08117a053..ea450a815 100644 --- a/frontend/public/json/huntarr.json +++ b/frontend/public/json/huntarr.json @@ -2,9 +2,9 @@ "name": "Huntarr", "slug": "huntarr", "categories": [ - 14 + 14 ], - "date_created": "2025-05-12", + "date_created": "2025-06-18", "type": "ct", "updateable": true, "privileged": false, @@ -15,21 +15,21 @@ "logo": "https://raw.githubusercontent.com/plexguide/Huntarr.io/refs/heads/main/frontend/static/logo/Huntarr.svg", "description": "Huntarr is a tool that automates the search for missing or low-quality media content in your collection. It works seamlessly with applications like Sonarr, Radarr, Lidarr, Readarr, and Whisparr, enhancing their functionality with continuous background scans to identify and update missed or outdated content. Through a user-friendly web interface accessible on port 9705, Huntarr provides real-time statistics, log views, and extensive configuration options. The software is especially useful for users who want to keep their media library up to date by automatically searching for missing episodes or higher-quality versions. Huntarr is well-suited for self-hosted environments and can easily run in LXC containers or Docker setups.", "install_methods": [ - { - "type": "default", - "script": "ct/huntarr.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } + { + "type": "default", + "script": "ct/huntarr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" } + } ], "default_credentials": { - "username": null, - "password": null + "username": null, + "password": null }, "notes": [] } From a402b45b1776728bd16d520e63b2ebab2fe0910b Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:51:02 +0200 Subject: [PATCH 070/139] Update versions.json (#5253) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 160 +++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 30 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index a2b87e24f..83c97e41a 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,34 @@ [ + { + "name": "plexguide/Huntarr.io", + "version": "8.0.9", + "date": "2025-06-18T13:12:23Z" + }, + { + "name": "ollama/ollama", + "version": "v0.9.2", + "date": "2025-06-18T12:45:00Z" + }, + { + "name": "Bubka/2FAuth", + "version": "v5.6.0", + "date": "2025-06-18T12:19:54Z" + }, + { + "name": "zwave-js/zwave-js-ui", + "version": "v10.7.0", + "date": "2025-06-18T11:57:05Z" + }, + { + "name": "zabbix/zabbix", + "version": "7.0.14", + "date": "2025-06-18T11:30:20Z" + }, + { + "name": "rclone/rclone", + "version": "v1.70.0", + "date": "2025-06-18T10:31:28Z" + }, { "name": "esphome/esphome", "version": "2025.6.0", @@ -24,11 +54,6 @@ "version": "v9.11.17", "date": "2025-06-18T08:12:05Z" }, - { - "name": "zabbix/zabbix", - "version": "7.4.0rc2", - "date": "2025-06-18T07:23:07Z" - }, { "name": "Jackett/Jackett", "version": "v0.22.2026", @@ -39,6 +64,11 @@ "version": "v6.12.7", "date": "2025-06-18T03:44:24Z" }, + { + "name": "rcourtman/Pulse", + "version": "v3.30.0", + "date": "2025-06-17T16:00:01Z" + }, { "name": "grafana/grafana", "version": "v11.5.6", @@ -64,16 +94,16 @@ "version": "v5.36.6", "date": "2025-06-17T18:12:31Z" }, + { + "name": "project-zot/zot", + "version": "v2.1.5", + "date": "2025-06-17T18:04:11Z" + }, { "name": "fallenbagel/jellyseerr", "version": "preview-sort-userlist", "date": "2025-06-17T18:02:25Z" }, - { - "name": "ollama/ollama", - "version": "v0.9.2", - "date": "2025-06-17T17:51:43Z" - }, { "name": "msgbyte/tianji", "version": "v1.21.16", @@ -99,6 +129,11 @@ "version": "4.5.1", "date": "2025-04-11T09:57:47Z" }, + { + "name": "arunavo4/gitea-mirror", + "version": "v2.16.2", + "date": "2025-06-17T11:59:34Z" + }, { "name": "crowdsecurity/crowdsec", "version": "v1.6.9", @@ -154,6 +189,11 @@ "version": "e5.9.1-alpha.1", "date": "2025-06-16T15:34:01Z" }, + { + "name": "fuma-nama/fumadocs", + "version": "fumadocs-openapi@9.0.12", + "date": "2025-06-16T15:09:27Z" + }, { "name": "open-webui/open-webui", "version": "v0.6.15", @@ -309,6 +349,11 @@ "version": "v2025-06-12", "date": "2025-06-12T20:59:47Z" }, + { + "name": "meilisearch/meilisearch", + "version": "latest", + "date": "2025-06-12T19:09:18Z" + }, { "name": "Checkmk/checkmk", "version": "v2.3.0p34", @@ -354,6 +399,11 @@ "version": "v1.63.1", "date": "2025-06-11T11:05:42Z" }, + { + "name": "steveiliop56/tinyauth", + "version": "v3.4.1", + "date": "2025-06-11T07:53:44Z" + }, { "name": "openobserve/openobserve", "version": "v0.15.0-rc2", @@ -514,6 +564,11 @@ "version": "340", "date": "2025-06-04T16:41:44Z" }, + { + "name": "intri-in/manage-my-damn-life-nextjs", + "version": "v0.7.1", + "date": "2025-06-04T04:39:36Z" + }, { "name": "louislam/uptime-kuma", "version": "2.0.0-beta.2-temp", @@ -554,6 +609,11 @@ "version": "2.4.0", "date": "2025-06-01T18:08:44Z" }, + { + "name": "TwiN/gatus", + "version": "v5.18.1", + "date": "2025-05-31T23:06:08Z" + }, { "name": "prometheus/prometheus", "version": "v3.4.1", @@ -574,6 +634,11 @@ "version": "v2.12.0", "date": "2025-05-30T00:26:27Z" }, + { + "name": "release-argus/Argus", + "version": "0.26.3", + "date": "2025-05-29T21:18:15Z" + }, { "name": "gristlabs/grist-core", "version": "v1.6.0", @@ -589,11 +654,6 @@ "version": "0.19.2", "date": "2025-05-29T14:39:17Z" }, - { - "name": "zwave-js/zwave-js-ui", - "version": "v10.6.1", - "date": "2025-05-29T13:36:09Z" - }, { "name": "duplicati/duplicati", "version": "v2.1.0.119-2.1.0.119_canary_2025-05-29", @@ -674,6 +734,11 @@ "version": "v0.26.2", "date": "2025-05-22T05:24:42Z" }, + { + "name": "CrazyWolf13/streamlink-webui", + "version": "0.5", + "date": "2025-05-21T20:19:14Z" + }, { "name": "apache/tika", "version": "3.2.0-rc2", @@ -774,6 +839,11 @@ "version": "v1.0.0-beta21", "date": "2025-05-09T23:14:23Z" }, + { + "name": "getumbrel/umbrel", + "version": "1.4.2", + "date": "2025-05-09T08:54:49Z" + }, { "name": "ZoeyVid/NPMplus", "version": "2025-05-07-r1", @@ -784,6 +854,11 @@ "version": "v0.19.0", "date": "2025-05-06T18:05:42Z" }, + { + "name": "garethgeorge/backrest", + "version": "v1.8.1", + "date": "2025-05-06T04:27:00Z" + }, { "name": "linkwarden/linkwarden", "version": "v2.10.2", @@ -799,6 +874,11 @@ "version": "3.5.0", "date": "2025-05-05T16:28:24Z" }, + { + "name": "raydak-labs/configarr", + "version": "v1.13.5", + "date": "2025-05-03T09:48:44Z" + }, { "name": "jhuckaby/Cronicle", "version": "v0.9.80", @@ -839,6 +919,11 @@ "version": "deluge-2.2.0", "date": "2025-04-28T21:31:06Z" }, + { + "name": "oauth2-proxy/oauth2-proxy", + "version": "v7.9.0", + "date": "2025-04-28T16:28:42Z" + }, { "name": "gotify/server", "version": "v2.6.3", @@ -924,11 +1009,6 @@ "version": "v0.2.11", "date": "2025-04-12T21:13:08Z" }, - { - "name": "Bubka/2FAuth", - "version": "v5.5.2", - "date": "2025-04-11T22:00:06Z" - }, { "name": "thomiceli/opengist", "version": "v1.10.0", @@ -989,6 +1069,11 @@ "version": "v1.34.0", "date": "2025-03-26T08:48:34Z" }, + { + "name": "LibreTranslate/LibreTranslate", + "version": "v1.6.5", + "date": "2025-03-25T20:27:29Z" + }, { "name": "nextcloud/nextcloudpi", "version": "v1.55.4", @@ -1054,6 +1139,11 @@ "version": "tc_v0.6.4", "date": "2025-03-05T15:43:40Z" }, + { + "name": "bitmagnet-io/bitmagnet", + "version": "v0.10.0", + "date": "2025-03-02T15:13:47Z" + }, { "name": "heiher/hev-socks5-server", "version": "2.8.0", @@ -1074,6 +1164,11 @@ "version": "v1.1.05", "date": "2025-02-24T11:53:12Z" }, + { + "name": "babybuddy/babybuddy", + "version": "v2.7.1", + "date": "2025-02-22T01:14:41Z" + }, { "name": "typesense/typesense", "version": "v28.0", @@ -1109,6 +1204,11 @@ "version": "1.7.6", "date": "2025-02-01T09:50:52Z" }, + { + "name": "AmruthPillai/Reactive-Resume", + "version": "v4.4.4", + "date": "2025-01-30T16:57:47Z" + }, { "name": "rustdesk/rustdesk-server", "version": "1.1.14", @@ -1119,6 +1219,11 @@ "version": "v0.5.7", "date": "2025-01-17T15:57:17Z" }, + { + "name": "PCJones/UmlautAdaptarr", + "version": "0.7.2", + "date": "2025-01-13T22:17:18Z" + }, { "name": "ErsatzTV/ErsatzTV", "version": "v25.1.0", @@ -1129,6 +1234,11 @@ "version": "v0.24.6", "date": "2024-12-22T20:24:35Z" }, + { + "name": "favonia/cloudflare-ddns", + "version": "v1.15.1", + "date": "2024-12-16T13:00:05Z" + }, { "name": "ArchiveBox/ArchiveBox", "version": "v0.7.3", @@ -1238,15 +1348,5 @@ "name": "thelounge/thelounge-deb", "version": "v4.4.3", "date": "2024-04-06T12:24:35Z" - }, - { - "name": "deepch/RTSPtoWeb", - "version": "v2.4.3", - "date": "2023-03-29T12:05:02Z" - }, - { - "name": "Shinobi-Systems/Shinobi", - "version": "furrykitten-3", - "date": "2022-07-15T05:20:17Z" } ] From fc4a46b72d4b169745f2dcdb8239962a335ab975 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:17:40 +0200 Subject: [PATCH 071/139] FileBrowser Quantum (#5248) --- frontend/public/json/filebrowser-quantum.json | 47 ++++ tools/addon/filebrowser-quantum.sh | 244 ++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 frontend/public/json/filebrowser-quantum.json create mode 100644 tools/addon/filebrowser-quantum.sh diff --git a/frontend/public/json/filebrowser-quantum.json b/frontend/public/json/filebrowser-quantum.json new file mode 100644 index 000000000..7f749a5f4 --- /dev/null +++ b/frontend/public/json/filebrowser-quantum.json @@ -0,0 +1,47 @@ +{ + "name": "FileBrowser Quantum", + "slug": "filebrowser-quantum", + "categories": [ + 1, + 11 + ], + "date_created": "2025-06-16", + "type": "addon", + "updateable": false, + "privileged": false, + "interface_port": 8080, + "documentation": "https://github.com/gtsteffaniak/filebrowser/wiki/Getting-Started", + "website": "https://github.com/gtsteffaniak/filebrowser", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/filebrowser-quantum.webp", + "config_path": "/usr/local/community-scripts/fq-config.yaml", + "description": "FileBrowser Quantum provides an easy way to access and manage your files from the web. It has has a web page interface that allows you to create secure shared links, users with their own specific permissions and settings, and offers a great viewing experience for many file types. This version is called Quantum because it packs tons of advanced features into a tiny easy to run file. Unlike the majority of alternative options, FileBrowser Quantum is simple to install and easy to configure.", + "install_methods": [ + { + "type": "default", + "script": "tools/addon/filebrowser-quantum.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + }, + { + "type": "alpine", + "script": "tools/addon/filebrowser-quantum.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": "admin", + "password": "helper-scripts.com" + }, + "notes": [] +} diff --git a/tools/addon/filebrowser-quantum.sh b/tools/addon/filebrowser-quantum.sh new file mode 100644 index 000000000..74911a788 --- /dev/null +++ b/tools/addon/filebrowser-quantum.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Author: MickLesk +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + _______ __ ____ ____ __ + / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ + +EOF +} + +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +BL=$(echo "\033[36m") +CL=$(echo "\033[m") +CM="${GN}✔️${CL}" +CROSS="${RD}✖️${CL}" +INFO="${BL}ℹ️${CL}" + +APP="FileBrowser Quantum" +INSTALL_PATH="/usr/local/bin/filebrowser" +CONFIG_PATH="/usr/local/community-scripts/fq-config.yaml" +DEFAULT_PORT=8080 +SRC_DIR="/" + +# Get primary IP +IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') +IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) +[[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') +[[ -z "$IP" ]] && IP="127.0.0.1" + +# OS Detection +if [[ -f "/etc/alpine-release" ]]; then + OS="Alpine" + SERVICE_PATH="/etc/init.d/filebrowser" + PKG_MANAGER="apk add --no-cache" +elif [[ -f "/etc/debian_version" ]]; then + OS="Debian" + SERVICE_PATH="/etc/systemd/system/filebrowser.service" + PKG_MANAGER="apt-get install -y" +else + echo -e "${CROSS} Unsupported OS detected. Exiting." + exit 1 +fi + +header_info + +function msg_info() { + local msg="$1" + echo -e "${INFO} ${YW}${msg}...${CL}" +} + +function msg_ok() { + local msg="$1" + echo -e "${CM} ${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${CROSS} ${RD}${msg}${CL}" +} + +# Detect legacy FileBrowser installation +LEGACY_DB="/usr/local/community-scripts/filebrowser.db" +LEGACY_BIN="/usr/local/bin/filebrowser" +LEGACY_SERVICE_DEB="/etc/systemd/system/filebrowser.service" +LEGACY_SERVICE_ALP="/etc/init.d/filebrowser" + +if [[ -f "$LEGACY_DB" || -f "$LEGACY_BIN" && ! -f "$CONFIG_PATH" ]]; then + echo -e "${YW}⚠️ Detected legacy FileBrowser installation.${CL}" + echo -n "Uninstall legacy FileBrowser and continue with Quantum install? (y/n): " + read -r remove_legacy + if [[ "${remove_legacy,,}" =~ ^(y|yes)$ ]]; then + msg_info "Uninstalling legacy FileBrowser" + if [[ -f "$LEGACY_SERVICE_DEB" ]]; then + systemctl disable --now filebrowser.service &>/dev/null + rm -f "$LEGACY_SERVICE_DEB" + elif [[ -f "$LEGACY_SERVICE_ALP" ]]; then + rc-service filebrowser stop &>/dev/null + rc-update del filebrowser &>/dev/null + rm -f "$LEGACY_SERVICE_ALP" + fi + rm -f "$LEGACY_BIN" "$LEGACY_DB" + msg_ok "Legacy FileBrowser removed" + else + echo -e "${YW}❌ Installation aborted by user.${CL}" + exit 0 + fi +fi + +# Check existing installation +if [[ -f "$INSTALL_PATH" ]]; then + echo -e "${YW}⚠️ ${APP} is already installed.${CL}" + echo -n "Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Uninstalling ${APP}" + if [[ "$OS" == "Debian" ]]; then + systemctl disable --now filebrowser.service &>/dev/null + rm -f "$SERVICE_PATH" + else + rc-service filebrowser stop &>/dev/null + rc-update del filebrowser &>/dev/null + rm -f "$SERVICE_PATH" + fi + rm -f "$INSTALL_PATH" "$CONFIG_PATH" + msg_ok "${APP} has been uninstalled." + exit 0 + fi + + echo -n "Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Updating ${APP}" + curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-amd64-filebrowser -o "$INSTALL_PATH" + chmod +x "$INSTALL_PATH" + msg_ok "Updated ${APP}" + exit 0 + else + echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" + exit 0 + fi +fi + +echo -e "${YW}⚠️ ${APP} is not installed.${CL}" +echo -n "Enter port number (Default: ${DEFAULT_PORT}): " +read -r PORT +PORT=${PORT:-$DEFAULT_PORT} + +echo -n "Install ${APP}? (y/n): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + msg_info "Installing ${APP} on ${OS}" + $PKG_MANAGER curl ffmpeg &>/dev/null + curl -fsSL https://github.com/gtsteffaniak/filebrowser/releases/latest/download/linux-amd64-filebrowser -o "$INSTALL_PATH" + chmod +x "$INSTALL_PATH" + msg_ok "Installed ${APP}" + + msg_info "Preparing configuration directory" + mkdir -p /usr/local/community-scripts + chown root:root /usr/local/community-scripts + chmod 755 /usr/local/community-scripts + msg_ok "Directory prepared" + + echo -n "Use No Authentication? (y/N): " + read -r noauth_prompt + + if [[ "${noauth_prompt,,}" =~ ^(y|yes)$ ]]; then + cat <"$CONFIG_PATH" +server: + port: $PORT + sources: + - path: "$SRC_DIR" + config: + disableIndexing: false + indexingIntervalMinutes: 240 + exclude: + folders: + - "/proc" + - "/sys" + - "/dev" + - "/run" + - "/tmp" + - "/lost+found" +auth: + methods: + noauth: true +EOF + msg_ok "Configured with no authentication" + else + cat <"$CONFIG_PATH" +server: + port: $PORT + sources: + - path: "$SRC_DIR" + config: + disableIndexing: false + indexingIntervalMinutes: 240 + exclude: + folders: + - "/proc" + - "/sys" + - "/dev" + - "/run" + - "/tmp" + - "/lost+found" +auth: + adminUsername: admin + adminPassword: helper-scripts.com +EOF + msg_ok "Configured with default admin (admin / helper-scripts.com)" + fi + + msg_info "Creating service" + + if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH" +[Unit] +Description=FileBrowser Quantum +After=network.target + +[Service] +User=root +WorkingDirectory=/usr/local/community-scripts +ExecStart=/usr/local/bin/filebrowser -c $CONFIG_PATH +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable --now filebrowser &>/dev/null + else + cat <"$SERVICE_PATH" +#!/sbin/openrc-run + +command="/usr/local/bin/filebrowser" +command_args="-c $CONFIG_PATH" +command_background=true +directory="/usr/local/community-scripts" +pidfile="/usr/local/community-scripts/pidfile" + +depend() { + need net +} +EOF + chmod +x "$SERVICE_PATH" + rc-update add filebrowser default &>/dev/null + rc-service filebrowser start &>/dev/null + fi + + msg_ok "Service created successfully" + echo -e "${CM} ${GN}${APP} is reachable at: ${BL}http://$IP:$PORT${CL}" +else + echo -e "${YW}⚠️ Installation skipped. Exiting.${CL}" + exit 0 +fi From e7bb270c34b2de59b0b749298cf4759c9bec65d2 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:18:15 +0100 Subject: [PATCH 072/139] Update date in json (#5256) Co-authored-by: GitHub Actions --- frontend/public/json/filebrowser-quantum.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/filebrowser-quantum.json b/frontend/public/json/filebrowser-quantum.json index 7f749a5f4..dae307edb 100644 --- a/frontend/public/json/filebrowser-quantum.json +++ b/frontend/public/json/filebrowser-quantum.json @@ -5,7 +5,7 @@ 1, 11 ], - "date_created": "2025-06-16", + "date_created": "2025-06-18", "type": "addon", "updateable": false, "privileged": false, From 2f326ffb9792de522de1d121f9bfe7b74807080e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:19:15 +0100 Subject: [PATCH 073/139] Update CHANGELOG.md (#5257) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38eb2bffa..10539fdfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🆕 New Scripts - - Huntarr ([#5249](https://github.com/community-scripts/ProxmoxVE/pull/5249)) + - FileBrowser Quantum [@MickLesk](https://github.com/MickLesk) ([#5248](https://github.com/community-scripts/ProxmoxVE/pull/5248)) +- Huntarr ([#5249](https://github.com/community-scripts/ProxmoxVE/pull/5249)) ### 🚀 Updated Scripts From 6c345af691eee8f6b91832b4a83cf11519ba9e70 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 18 Jun 2025 22:02:02 +0200 Subject: [PATCH 074/139] quickfix after failed mig --- install/libretranslate-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/libretranslate-install.sh b/install/libretranslate-install.sh index d6f8dc04f..8c64cc5a9 100644 --- a/install/libretranslate-install.sh +++ b/install/libretranslate-install.sh @@ -29,7 +29,7 @@ $STD apt-get install -y \ msg_ok "Setup Python3" setup_uv -fetch_and_deploy_gh_release "LibreTranslate" "LibreTranslate/LibreTranslate" +fetch_and_deploy_gh_release "libretranslate" "LibreTranslate/LibreTranslate" msg_info "Setup LibreTranslate (Patience)" cd /opt/libretranslate From badd63d4a756ac33dad54b30f42716adbba4c5e9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 18 Jun 2025 22:08:23 +0200 Subject: [PATCH 075/139] Refactor all VM's to same logic & functions (#5254) --- vm/archlinux-vm.sh | 9 +- vm/docker-vm.sh | 7 +- vm/haos-vm.sh | 225 ++++++++++++------- vm/mikrotik-routeros.sh | 480 ++++++++++++++++++++++++++++++---------- vm/nextcloud-vm.sh | 204 +++++++++++------ vm/openwrt.sh | 37 +++- vm/owncloud-vm.sh | 227 +++++++++++++------ vm/pimox-haos-vm.sh | 207 ++++++++++++----- 8 files changed, 988 insertions(+), 408 deletions(-) diff --git a/vm/archlinux-vm.sh b/vm/archlinux-vm.sh index 223c5796a..30f61e960 100644 --- a/vm/archlinux-vm.sh +++ b/vm/archlinux-vm.sh @@ -19,13 +19,12 @@ EOF } header_info echo -e "\n Loading..." -#API VARIABLES +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="arch-linux-vm" var_os="arch-linux" -var_version=" " -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +var_version="n.d." YW=$(echo "\033[33m") BL=$(echo "\033[36m") @@ -58,6 +57,7 @@ MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" THIN="discard=on,ssd=1," set -e @@ -69,8 +69,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${commad}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -101,6 +101,7 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 3eab0aac9..bf5e802fe 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -29,7 +29,6 @@ DISK_SIZE="10G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") @@ -59,6 +58,8 @@ MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR @@ -69,8 +70,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -182,7 +183,7 @@ function default_settings() { FORMAT=",efitype=4m" MACHINE="" DISK_CACHE="" - DISK_SIZE="8G" + DISK_SIZE="10G" HN="docker" CPU_TYPE="" CORE_COUNT="2" diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index a64147446..902ce984b 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -2,8 +2,7 @@ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) @@ -21,8 +20,8 @@ EOF header_info echo -e "\n Loading..." GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -VERSIONS=(stable beta dev) RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +VERSIONS=(stable beta dev) METHOD="" NSAPP="homeassistant-os" var_os="homeassistant" @@ -39,26 +38,43 @@ BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" HOLD=" " -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," -SPINNER_PID="" -set -Eeuo pipefail +set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM - function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -89,46 +105,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOME ASSISTANT OS VM" --yesno "This will create a New Home Assistant OS VM. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS VM" --yesno "This will create a New Homeassistant OS VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi -function spinner() { - local chars="/-\|" - local spin_i=0 - printf "\e[?25l" - while true; do - printf "\r \e[36m%s\e[0m" "${chars:spin_i++%${#chars}:1}" - sleep 0.1 - done -} - function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg} " - spinner & - SPINNER_PID=$! + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -143,7 +144,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -153,7 +154,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit @@ -175,7 +177,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -195,26 +197,25 @@ function default_settings() { MTU="" START_VM="yes" METHOD="default" - var_version="${stable}" - echo -e "${DGN}Using HAOS Version: ${BGN}${BRANCH}${CL}" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${BL}Creating a HAOS VM using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Homeassistant OS VM using the above default settings${CL}" } function advanced_settings() { METHOD="advanced" - if BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HAOS VERSION" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ + if BRANCH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Homeassistant OS Version" --radiolist "Choose Version" --cancel-button Exit-Script 10 58 3 \ "$stable" "Stable " ON \ "$beta" "Beta " OFF \ "$dev" "Dev " OFF \ @@ -229,14 +230,14 @@ function advanced_settings() { while true; do if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z "$VMID" ]; then - VMID="$VMID" + VMID=$(get_valid_nextid) fi if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -248,11 +249,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -260,15 +261,30 @@ function advanced_settings() { exit-script fi - if DISK_CACHE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None" OFF \ "1" "Write Through (Default)" ON \ 3>&1 1>&2 2>&3); then - if [ $DISK_CACHE1 = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else @@ -278,10 +294,10 @@ function advanced_settings() { if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 haos${BRANCH} --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then HN="haos${BRANCH}" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -292,10 +308,10 @@ function advanced_settings() { "1" "Host (Default)" ON \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -305,20 +321,20 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script fi - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="4096" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -327,9 +343,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -338,10 +354,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -351,10 +367,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -364,10 +380,10 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script @@ -381,8 +397,8 @@ function advanced_settings() { START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create HAOS ${BRANCH} VM?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a HAOS VM using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create Homeassistant OS ${BRANCH} VM?" --no-button Do-Over 10 58); then + echo -e "${RD}Creating a Homeassistant OS VM using the above advanced settings${CL}" else header_info echo -e "${RD}Using Advanced Settings${CL}" @@ -440,6 +456,8 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + +var_version="${BRANCH}" msg_info "Retrieving the URL for Home Assistant ${BRANCH} Disk Image" if [ "$BRANCH" == "$dev" ]; then URL=https://os-artifacts.home-assistant.io/${BRANCH}/haos_ova-${BRANCH}.qcow2.xz @@ -452,6 +470,7 @@ curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}haos_ova-${BRANCH}.qcow2.xz${CL}" + msg_info "Extracting KVM Disk Image" unxz $FILE STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') @@ -476,7 +495,8 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done msg_ok "Extracted KVM Disk Image" -msg_info "Creating HAOS VM" + +msg_info "Creating Homeassistant OS VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -484,14 +504,49 @@ qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=32G \ - -boot order=scsi0 \ - -description "
" >/dev/null -msg_ok "Created HAOS VM ${CL}${BL}(${HN})" +

Homeassistant OS VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) + +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created Homeassistant OS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" qm start $VMID diff --git a/vm/mikrotik-routeros.sh b/vm/mikrotik-routeros.sh index bf1f77c32..ad740ca22 100644 --- a/vm/mikrotik-routeros.sh +++ b/vm/mikrotik-routeros.sh @@ -29,33 +29,51 @@ var_version=" " DISK_SIZE="1G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}✓${CL}" -set -o errexit -set -o errtrace -set -o nounset -set -o pipefail -shopt -s expand_aliases -alias die='EXIT=$? LINE=$LINENO error_exit' -trap die ERR +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM -function error_exit() { - trap - ERR - local reason="Unknown failure occurred." - local msg="${1:-$reason}" - local flag="${RD}‼ ERROR ${CL}$EXIT@$LINE" - echo -e "$flag $msg" 1>&2 - [ ! -z ${VMID-} ] && cleanup_vmid - exit $EXIT +function error_handler() { + 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}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { @@ -76,162 +94,352 @@ function get_valid_nextid() { } function cleanup_vmid() { - if $(qm status $VMID &>/dev/null); then - if [ "$(qm status $VMID | awk '{print $2}')" == "running" ]; then - qm stop $VMID - fi - qm destroy $VMID + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null fi } + function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } + TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit -fi -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a New Mikrotik RouterOS CHR VM. Proceed?" 10 58); then - echo "User selected Yes" +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then + : else - clear - echo -e "⚠ User exited script \n" - exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } + function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + function default_settings() { - METHOD="default" VMID=$(get_valid_nextid) - echo -e "${DGN}Using Virtual Machine ID: ${BGN}$VMID${CL}" - echo -e "${DGN}Using Hostname: ${BGN}mikrotik-routeros-chr${CL}" - HN=mikrotik-routeros-chr - echo -e "${DGN}Allocated Cores: ${BGN}1${CL}" + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="8G" + DISK_CACHE="" + HN="mikrotik-routeros-chr" + CPU_TYPE="" CORE_COUNT="2" - echo -e "${DGN}Allocated RAM: ${BGN}256${CL}" RAM_SIZE="512" - echo -e "${DGN}Using Bridge: ${BGN}vmbr0${CL}" BRG="vmbr0" - echo -e "${DGN}Using MAC Address: ${BGN}$GEN_MAC${CL}" - MAC=$GEN_MAC - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" + MAC="$GEN_MAC" VLAN="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" MTU="" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - echo -e "${BL}Creating a Mikrotik RouterOS CHR VM using the above default settings${CL}" + START_VM="yes" + CLOUD_INIT="no" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS VM using the above default settings${CL}" } + +function get_mikrotik_version() { + local mode="$1" + local tree_name + + case "$mode" in + s) tree_name="Stable release tree" ;; + d) tree_name="Development release tree" ;; + l) tree_name="Long-term release tree" ;; + t) tree_name="Testing release tree" ;; + *) return 0 ;; # not an error, just no-op + esac + + local html + html=$(curl -fsSL "https://mikrotik.com/download/changelogs") || return 0 + [ -z "$html" ] && return 0 + + local start_line + start_line=$(echo "$html" | grep -n "$tree_name$" | cut -d: -f1 | head -n1) + [[ "$start_line" =~ ^[0-9]+$ ]] || return 0 + + local line + line=$( (echo "$html" | tail -n +"$start_line" | grep -m 1 "c-\(stable\|longTerm\|testing\|development\)-v") 2>/dev/null || true) + + local version + version=$(echo "$line" | sed -n 's/.*c-[^"]*-v\([0-9_.a-zA-Z-]\+\).*/\1/p' | tr '_' '.') + + [[ "$version" =~ ^[0-9]+\.[0-9]+.*$ ]] && echo "$version" + + return 0 +} + function advanced_settings() { METHOD="advanced" [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Using Virtual Machine ID: ${BGN}$VMID${CL}" + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi else - exit + exit-script fi - VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi else - exit + exit-script fi - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi else - exit + exit-script fi - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 512 --title "RAM" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="debian" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi else - exit + exit-script fi - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi else - exit + exit-script fi - MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then - MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi else - exit + exit-script fi - VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VLAN1 ]; then - VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi + else + exit-script fi - MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3) - exitstatus=$? - if [ $exitstatus = 0 ]; then + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MTU1 ]; then - MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi + else + exit-script fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start Mikrotik RouterOS CHR VM when completed?" 10 58); then - echo -e "${DGN}Start Mikrotik RouterOS CHR VM when completed: ${BGN}yes${CL}" + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start Mikrotik RouterOS CHR VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create Mikrotik RouterOS VM?" 10 58); then - echo -e "${RD}Creating Mikrotik RouterOS CHR VM using the above advanced settings${CL}" + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Mikrotik RouterOS CHR VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS CHR VM using the above advanced settings${CL}" else - clear header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } + function start_script() { if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then - clear header_info - echo -e "${BL}Using Default Settings${CL}" + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" default_settings else - clear header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } + +check_root +arch_check +pve_check +ssh_check start_script post_to_api_vm @@ -264,12 +472,22 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Getting URL for Mikrotik RouterOS CHR Disk Image" +msg_info "Getting URL for Latest Mikrotik RouterOS CHR Disk Image" -URL=https://download.mikrotik.com/routeros/7.15.3/chr-7.15.3.img.zip +MIK_VER=$(get_mikrotik_version s) + +if [ -n "$MIK_VER" ]; then + msg_ok "Latest stable version: ${CL}${BL}$MIK_VER${CL}." +else + msg_error "Could not get latest version" + msg_ok "Defaulting to version 7.19" + ver="7.19" +fi + +URL=https://download.mikrotik.com/routeros/$MIK_VER/chr-$MIK_VER.img.zip sleep 2 -msg_ok "${CL}${BL}${URL}${CL}" +msg_ok "Downloading from URL: ${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) @@ -306,13 +524,47 @@ qm create $VMID -tablet 0 -localtime 1 -cores $CORE_COUNT -memory $RAM_SIZE -nam qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -scsi0 "$DISK_REF" \ - -boot order=scsi0 \ - -description "
+ -boot order=scsi0 >/dev/null - # Mikrotik RouterOS CHR +DESCRIPTION=$( + cat < + + Logo + + +

Mikrotik RouterOS CHR

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Mikrotik RouterOS CHR VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Mikrotik RouterOS CHR VM" diff --git a/vm/nextcloud-vm.sh b/vm/nextcloud-vm.sh index 1b2703933..8d6c0fe3b 100644 --- a/vm/nextcloud-vm.sh +++ b/vm/nextcloud-vm.sh @@ -19,27 +19,47 @@ EOF } header_info echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="turnkey-nextcloud" var_os="turnkey-nextcloud" -var_version=" " -DISK_SIZE="12G" -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -NAME="TurnKey Nextcloud VM" +var_version="n.d." + YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" -THIN="discard=on,ssd=1" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT @@ -49,8 +69,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "${command}" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -81,30 +101,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Nextcloud VM" --yesno "This will create a New Nextcloud VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -119,7 +140,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -129,7 +150,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit @@ -151,7 +173,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -159,8 +181,9 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" + DISK_SIZE="10G" DISK_CACHE="" - HN="turnkey-nextcloud-vm" + HN="nextcloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" @@ -168,21 +191,22 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + START_VM="yes" METHOD="default" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - echo -e "${BL}Creating a $NAME using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above default settings${CL}" } function advanced_settings() { @@ -198,7 +222,7 @@ function advanced_settings() { sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -210,11 +234,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -222,28 +246,43 @@ function advanced_settings() { exit-script fi + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 turnkey-nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="$HN" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + HN="nextcloud-vm" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -254,10 +293,10 @@ function advanced_settings() { "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -267,9 +306,9 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script @@ -278,9 +317,9 @@ function advanced_settings() { if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -289,9 +328,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -300,10 +339,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -313,10 +352,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -326,28 +365,28 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a $NAME using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Nextcloud VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above advanced settings${CL}" else header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } @@ -441,13 +480,46 @@ qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ - -boot order='scsi1;scsi0' \ - -description "
+ -boot order='scsi1;scsi0' >/dev/null +DESCRIPTION=$( + cat < + + Logo + - # $NAME +

Nextcloud VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Created a $NAME ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting $NAME" diff --git a/vm/openwrt.sh b/vm/openwrt.sh index 6d832e5a9..86f6e4e4f 100644 --- a/vm/openwrt.sh +++ b/vm/openwrt.sh @@ -507,13 +507,40 @@ qm set $VMID \ -efidisk0 ${DISK0_REF},efitype=4m,size=4M \ -scsi0 ${DISK1_REF},size=512M \ -boot order=scsi0 \ - -tags community-script \ - -description "
+ -tags community-script >/dev/null - # OpenWrt +DESCRIPTION=$( + cat < + + Logo + + +

OpenWRT VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null - -
" >/dev/null msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})" msg_info "OpenWrt is being started in order to configure the network interfaces." qm start $VMID diff --git a/vm/owncloud-vm.sh b/vm/owncloud-vm.sh index dcc06fdfb..536ee3d82 100644 --- a/vm/owncloud-vm.sh +++ b/vm/owncloud-vm.sh @@ -19,28 +19,48 @@ EOF } header_info echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" NSAPP="turnkey-owncloud-vm" var_os="owncloud" -var_version="12" -DISK_SIZE="12G" -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -NAME="TurnKey ownCloud VM" +var_version="18.0" +APP="TurnKey ownCloud VM" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" -THIN="discard=on,ssd=1" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT @@ -50,8 +70,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - post_update_to_api "failed" "$command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } @@ -82,30 +102,31 @@ function cleanup_vmid() { function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Owncloud VM" --yesno "This will create a New Owncloud VM. Proceed?" 10 58; then : else - header_info && echo -e "⚠ User exited script \n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" } function msg_ok() { local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + echo -e "${BFR}${CM}${GN}${msg}${CL}" } function msg_error() { local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } function check_root() { @@ -120,7 +141,7 @@ function check_root() { function pve_check() { if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 @@ -130,7 +151,8 @@ function pve_check() { function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then - msg_error "This script will not work with PiMox! \n" + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" echo -e "Exiting..." sleep 2 exit @@ -152,7 +174,7 @@ function ssh_check() { function exit-script() { clear - echo -e "⚠ User exited script \n" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } @@ -160,8 +182,9 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" + DISK_SIZE="10G" DISK_CACHE="" - HN="turnkey-owncloud-vm" + HN="owncloud-vm" CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="2048" @@ -169,21 +192,22 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + START_VM="yes" METHOD="default" - echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${DGN}Using Machine Type: ${BGN}i440fx${CL}" - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" - echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" - echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" - echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" - echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" - echo -e "${BL}Creating a $NAME using the above default settings${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above default settings${CL}" } function advanced_settings() { @@ -199,7 +223,7 @@ function advanced_settings() { sleep 2 continue fi - echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else exit-script @@ -211,11 +235,11 @@ function advanced_settings() { "q35" "Machine q35" OFF \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -223,28 +247,43 @@ function advanced_settings() { exit-script fi + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON \ "1" "Write Through" OFF \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then - echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," else - echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" fi else exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 turnkey-owncloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 owncloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="turnkey-owncloud-vm" - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + HN="owncloud-vm" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else exit-script @@ -255,10 +294,10 @@ function advanced_settings() { "1" "Host" OFF \ 3>&1 1>&2 2>&3); then if [ $CPU_TYPE1 = "1" ]; then - echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" else - echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" fi else @@ -268,9 +307,9 @@ function advanced_settings() { if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $CORE_COUNT ]; then CORE_COUNT="2" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else exit-script @@ -279,9 +318,9 @@ function advanced_settings() { if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $RAM_SIZE ]; then RAM_SIZE="2048" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else exit-script @@ -290,9 +329,9 @@ function advanced_settings() { if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $BRG ]; then BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else exit-script @@ -301,10 +340,10 @@ function advanced_settings() { if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" else MAC="$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else exit-script @@ -314,10 +353,10 @@ function advanced_settings() { if [ -z $VLAN1 ]; then VLAN1="Default" VLAN="" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" else VLAN=",tag=$VLAN1" - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else exit-script @@ -327,28 +366,40 @@ function advanced_settings() { if [ -z $MTU1 ]; then MTU1="Default" MTU="" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" else MTU=",mtu=$MTU1" - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else exit-script fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" else - echo -e "${DGN}Start VM when completed: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then - echo -e "${RD}Creating a $NAME using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Owncloud VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Owncloud VM using the above advanced settings${CL}" else header_info - echo -e "${RD}Using Advanced Settings${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" advanced_settings fi } @@ -401,7 +452,7 @@ else 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 $NAME Disk Image" +msg_info "Retrieving the URL for the $APP Disk Image" URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-owncloud-18.0-bookworm-amd64.iso sleep 2 msg_ok "${CL}${BL}${URL}${CL}" @@ -432,7 +483,7 @@ for i in {0,1,2}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a $NAME" +msg_info "Creating a $APP" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -442,18 +493,52 @@ qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \ -scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \ - -boot order='scsi1;scsi0' \ - -description "
+ -boot order='scsi1;scsi0' >/dev/null - # $NAME +DESCRIPTION=$( + cat < + + Logo + - -
" >/dev/null -msg_ok "Created a $NAME ${CL}${BL}(${HN})" +

Owncloud VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a $APP ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then - msg_info "Starting $NAME" + msg_info "Starting $APP" qm start $VMID - msg_ok "Started $NAME" + msg_ok "Started $APP" fi post_update_to_api "done" "none" msg_ok "Completed Successfully!\n" diff --git a/vm/pimox-haos-vm.sh b/vm/pimox-haos-vm.sh index 8613ab982..0f9dca7bb 100644 --- a/vm/pimox-haos-vm.sh +++ b/vm/pimox-haos-vm.sh @@ -23,49 +23,65 @@ EOF } clear header_info -echo -e "Loading..." +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +VERSIONS=(stable beta dev) METHOD="" NSAPP="pimox-haos-vm" var_os="pimox-haos" -var_version=" " DISK_SIZE="32G" -GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') -USEDID=$(pvesh get /cluster/resources --type vm --output-format yaml | egrep -i 'vmid' | awk '{print substr($2, 1, length($2)-0) }') -STABLE=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') -BETA=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/beta.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') -DEV=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/dev.json | grep "ova" | awk '{print substr($2, 2, length($2)-3) }') + +for version in "${VERSIONS[@]}"; do + eval "$version=$(curl -fsSL https://raw.githubusercontent.com/home-assistant/version/master/stable.json | grep '"ova"' | cut -d '"' -f 4)" +done YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") BFR="\\r\\033[K" -HOLD="-" -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" -set -o errexit -set -o errtrace -set -o nounset -set -o pipefail -shopt -s expand_aliases -alias die='EXIT=$? LINE=$LINENO error_exit' -trap die ERR +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM -function error_exit() { - trap - ERR - local reason="Unknown failure occurred." - local msg="${1:-$reason}" - local flag="${RD}‼ ERROR ${CL}$EXIT@$LINE" - post_update_to_api "failed" "unknown" - echo -e "$flag $msg" 1>&2 - [ ! -z ${VMID-} ] && cleanup_vmid - exit $EXIT +function error_handler() { + 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}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { @@ -86,51 +102,88 @@ function get_valid_nextid() { } function cleanup_vmid() { - if $(qm status $VMID &>/dev/null); then - if [ "$(qm status $VMID | awk '{print $2}')" == "running" ]; then - qm stop $VMID - fi - qm destroy $VMID + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null fi } + function cleanup() { popd >/dev/null + post_update_to_api "done" "none" rm -rf $TEMP_DIR } + TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if ! command -v whiptail &>/dev/null; then - echo "Installing whiptail..." - apt-get update &>/dev/null - apt-get install -y whiptail &>/dev/null -fi -if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "PiMox HAOS VM" --yesno "This will create a New PiMox HAOS VM. Proceed?" 10 58); then - echo "User selected Yes" +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then + : else - clear - echo -e "⚠ User exited script \n" - exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi -function ARCH_CHECK() { - ARCH=$(dpkg --print-architecture) - if [[ "$ARCH" == "amd64" ]]; then - echo -e "\n ❌ This script only works with PiMox! \n" + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." echo -e "Exiting..." sleep 2 exit fi } -function msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } -function msg_ok() { - local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi } -function msg_error() { - local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } function default_settings() { @@ -341,13 +394,47 @@ qm set $VMID \ -efidisk0 ${DISK0_REF},efitype=4m,size=64M \ -scsi0 ${DISK1_REF},size=32G >/dev/null qm set $VMID \ - -boot order=scsi0 \ - -description "
+ -boot order=scsi0 >/dev/null - # Home Assistant OS +DESCRIPTION=$( + cat < + + Logo + + +

OpenWRT VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi - -
" >/dev/null msg_ok "Created HAOS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Home Assistant OS VM" From 2255600c3b0661d6516643604b8b97eaf32c7bd9 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:08:59 +0100 Subject: [PATCH 076/139] Update CHANGELOG.md (#5261) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10539fdfc..5476f0102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🔧 Refactor + - Refactor all VM's to same logic & functions [@MickLesk](https://github.com/MickLesk) ([#5254](https://github.com/community-scripts/ProxmoxVE/pull/5254)) - upgrade old Scriptcalls to new tools.func calls [@MickLesk](https://github.com/MickLesk) ([#5242](https://github.com/community-scripts/ProxmoxVE/pull/5242)) ## 2025-06-17 From 14077eddcda4c641f89fd107c7f543e19d67af33 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 02:15:24 +0200 Subject: [PATCH 077/139] Update versions.json (#5265) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 112 ++++++++++++++--------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 83c97e41a..4c8215f24 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,13 +1,63 @@ [ { - "name": "plexguide/Huntarr.io", - "version": "8.0.9", - "date": "2025-06-18T13:12:23Z" + "name": "docmost/docmost", + "version": "v0.21.0", + "date": "2025-06-18T21:43:27Z" + }, + { + "name": "immich-app/immich", + "version": "v1.135.0", + "date": "2025-06-18T20:37:30Z" + }, + { + "name": "ipfs/kubo", + "version": "v0.35.0", + "date": "2025-05-21T18:00:32Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.98.2", + "date": "2025-06-18T18:20:16Z" + }, + { + "name": "pterodactyl/panel", + "version": "v1.11.11", + "date": "2025-06-18T18:04:50Z" + }, + { + "name": "msgbyte/tianji", + "version": "v1.22.0", + "date": "2025-06-18T16:21:38Z" }, { "name": "ollama/ollama", "version": "v0.9.2", - "date": "2025-06-18T12:45:00Z" + "date": "2025-06-18T14:29:39Z" + }, + { + "name": "NodeBB/NodeBB", + "version": "v3.12.7", + "date": "2025-06-18T14:22:53Z" + }, + { + "name": "clusterzx/paperless-ai", + "version": "v3.0.6", + "date": "2025-06-18T14:18:13Z" + }, + { + "name": "openhab/openhab-core", + "version": "5.0.0.M3", + "date": "2025-06-18T14:18:12Z" + }, + { + "name": "zabbix/zabbix", + "version": "7.2.8", + "date": "2025-06-18T13:46:00Z" + }, + { + "name": "plexguide/Huntarr.io", + "version": "8.0.9", + "date": "2025-06-18T13:12:23Z" }, { "name": "Bubka/2FAuth", @@ -19,11 +69,6 @@ "version": "v10.7.0", "date": "2025-06-18T11:57:05Z" }, - { - "name": "zabbix/zabbix", - "version": "7.0.14", - "date": "2025-06-18T11:30:20Z" - }, { "name": "rclone/rclone", "version": "v1.70.0", @@ -104,11 +149,6 @@ "version": "preview-sort-userlist", "date": "2025-06-17T18:02:25Z" }, - { - "name": "msgbyte/tianji", - "version": "v1.21.16", - "date": "2025-06-17T16:25:53Z" - }, { "name": "BookStackApp/BookStack", "version": "v25.05.1", @@ -176,13 +216,8 @@ }, { "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" - }, - { - "name": "NodeBB/NodeBB", - "version": "v2.8.20", - "date": "2025-06-16T17:03:52Z" + "version": "nightly", + "date": "2025-06-16T17:35:17Z" }, { "name": "emqx/emqx", @@ -204,16 +239,6 @@ "version": "v8.1.16", "date": "2025-06-16T13:49:37Z" }, - { - "name": "clusterzx/paperless-ai", - "version": "v3.0.5", - "date": "2025-06-16T13:25:55Z" - }, - { - "name": "n8n-io/n8n", - "version": "n8n@1.95.3", - "date": "2025-06-03T11:09:42Z" - }, { "name": "Graylog2/graylog2-server", "version": "6.3.0-rc.1", @@ -684,11 +709,6 @@ "version": "1.2.34", "date": "2025-05-27T18:18:00Z" }, - { - "name": "immich-app/immich", - "version": "v1.134.0", - "date": "2025-05-27T17:28:27Z" - }, { "name": "traefik/traefik", "version": "v3.4.1", @@ -744,11 +764,6 @@ "version": "3.2.0-rc2", "date": "2025-05-21T20:09:07Z" }, - { - "name": "ipfs/kubo", - "version": "v0.35.0", - "date": "2025-05-21T18:00:32Z" - }, { "name": "Stirling-Tools/Stirling-PDF", "version": "v0.46.2", @@ -894,11 +909,6 @@ "version": "6.8.1", "date": "2025-04-30T16:44:16Z" }, - { - "name": "docmost/docmost", - "version": "v0.20.4", - "date": "2025-04-30T14:15:16Z" - }, { "name": "hivemq/hivemq-community-edition", "version": "2025.3", @@ -934,11 +944,6 @@ "version": "release-5.1.0", "date": "2025-04-27T08:53:48Z" }, - { - "name": "openhab/openhab-core", - "version": "4.3.5", - "date": "2025-04-26T13:52:07Z" - }, { "name": "photoprism/photoprism", "version": "250426-27ec7a128", @@ -1269,11 +1274,6 @@ "version": "0.6.24", "date": "2024-11-16T06:47:56Z" }, - { - "name": "pterodactyl/panel", - "version": "v1.11.10", - "date": "2024-11-15T02:29:18Z" - }, { "name": "sabre-io/Baikal", "version": "0.10.1", From 601e1853e985a18515104e657782c57b2000c9fa Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:15:57 +0100 Subject: [PATCH 078/139] Update CHANGELOG.md (#5266) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5476f0102..9e8786b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-19 + ## 2025-06-18 ### 🆕 New Scripts From 4190582659848a0f91d5f1c21fac89aa5c49d85e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 07:29:00 +0200 Subject: [PATCH 079/139] fix small typos after refactor --- vm/mikrotik-routeros.sh | 8 ++++---- vm/pimox-haos-vm.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vm/mikrotik-routeros.sh b/vm/mikrotik-routeros.sh index ad740ca22..3a4f91d0a 100644 --- a/vm/mikrotik-routeros.sh +++ b/vm/mikrotik-routeros.sh @@ -8,6 +8,7 @@ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { + clear cat <<"EOF" __ ____ __ __ _ __ ____ __ ____ _____ ________ ______ / |/ (_) /___________ / /_(_) /__ / __ \____ __ __/ /____ _____/ __ \/ ___/ / ____/ / / / __ \ @@ -17,7 +18,6 @@ function header_info { EOF } -clear header_info echo -e "Loading..." GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]') @@ -108,7 +108,7 @@ function cleanup() { TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a Mikrotik RouterOS CHR VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit @@ -309,9 +309,9 @@ function advanced_settings() { exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="debian" + HN="mikrotik-routeros-chr" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') diff --git a/vm/pimox-haos-vm.sh b/vm/pimox-haos-vm.sh index 0f9dca7bb..c4e328a79 100644 --- a/vm/pimox-haos-vm.sh +++ b/vm/pimox-haos-vm.sh @@ -116,7 +116,7 @@ function cleanup() { TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Pimox Homeassistant OS VM" --yesno "This will create a New Pimox Homeassistant OS VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit From 791eca99b99a67d500d65ba2a8e402ab77f31849 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 19 Jun 2025 01:55:39 -0400 Subject: [PATCH 080/139] immich: prepare for new version (#5025) - Remove pgvecto.rs > vchord migration operation - Check for vchord update - only apply if Immich has update - Additional operations for vchord 0.3.0 to 0.4.x - Use UV - Move libopencl to dependencies - Use vchord 0.3.0 only if Immich <= 1.134.0 - vchord 0.4.x for 1.134.1+ - Use UV --- ct/immich.sh | 88 +++++++++++++------------------- frontend/public/json/immich.json | 4 ++ install/immich-install.sh | 49 +++++++++--------- 3 files changed, 62 insertions(+), 79 deletions(-) diff --git a/ct/immich.sh b/ct/immich.sh index eddf23ae8..7d421ed9a 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -27,6 +27,9 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + setup_uv + STAGING_DIR=/opt/staging BASE_DIR=${STAGING_DIR}/base-images SOURCE_DIR=${STAGING_DIR}/image-source @@ -40,7 +43,7 @@ function update_script() { for url in "${INTEL_URLS[@]}"; do curl -fsSLO "$url" done - $STD dpkg -i ./*.deb + $STD apt install -y ./*.deb rm ./*.deb msg_ok "Intel iGPU dependencies updated" fi @@ -177,54 +180,42 @@ function update_script() { msg_ok "Image-processing libraries compiled" fi fi - RELEASE=$(curl -s https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + RELEASE=$(curl -fsSL https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then msg_info "Stopping ${APP} services" systemctl stop immich-web systemctl stop immich-ml msg_ok "Stopped ${APP}" - if [[ "$(cat /opt/${APP}_version.txt)" < "1.133.0" ]]; then - msg_info "Upgrading to the VectorChord PostgreSQL extension" - NUMBER="$( - sed -n '2p' <( - sudo -u postgres psql -A -d immich < 0 - AND c.relname = 'smart_search'::text - AND f.attname = 'embedding'::text; -EOF - ) - )" - $STD sudo -u postgres psql -d immich < "0.3.0" ]]; then + $STD sudo -u postgres pg_dumpall --clean --if-exists --username=postgres | gzip >/etc/postgresql/immich-db-vchord0.3.0.sql.gz + chown postgres /etc/postgresql/immich-db-vchord0.3.0.sql.gz + $STD sudo -u postgres gunzip --stdout /etc/postgresql/immich-db-vchord0.3.0.sql.gz | + sed -e "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \ + -e "/vchordrq.prewarm_dim/d" | + sudo -u postgres psql + fi + curl -fsSL "https://github.com/tensorchord/vectorchord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb + $STD apt install -y ./vchord.deb + $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" + systemctl restart postgresql + if [[ ! -f ~/.vchord-version ]] || [[ ! "$(cat ~/.vchord_version)" > "0.3.0" ]]; then + $STD sudo -u postgres psql -d immich -c "REINDEX DATABASE;" + fi + echo "$VCHORD_RELEASE" >~/.vchord_version + rm ./vchord.deb + msg_ok "Updated VectorChord to v${VCHORD_RELEASE}" + fi + cp "$ML_DIR"/ml_start.sh "$INSTALL_DIR" rm -rf "${APP_DIR:?}"/* rm -rf "$SRC_DIR" @@ -252,28 +243,21 @@ EOF msg_ok "Updated ${APP} web and microservices" cd "$SRC_DIR"/machine-learning - $STD python3 -m venv "$ML_DIR"/ml-venv + export VIRTUAL_ENV="${ML_DIR}"/ml-venv + $STD /usr/local/bin/uv venv "$VIRTUAL_ENV" if [[ -f ~/.openvino ]]; then msg_info "Updating HW-accelerated machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install -U uv - uv -q sync --extra openvino --no-cache --active - ) - patchelf --clear-execstack "$ML_DIR"/ml-venv/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so + /usr/local/bin/uv -q sync --extra openvino --no-cache --active + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so" msg_ok "Updated HW-accelerated machine-learning" else msg_info "Updating machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install -U uv - uv -q sync --extra cpu --no-cache --active - ) + /usr/local/bin/uv -q sync --extra cpu --no-cache --active msg_ok "Updated machine-learning" fi cd "$SRC_DIR" cp -a machine-learning/{ann,immich_ml} "$ML_DIR" - cp "$INSTALL_DIR"/ml_start.sh "$ML_DIR" + mv "$INSTALL_DIR"/ml_start.sh "$ML_DIR" if [[ -f ~/.openvino ]]; then sed -i "/intra_op/s/int = 0/int = os.cpu_count() or 0/" "$ML_DIR"/immich_ml/config.py fi @@ -292,8 +276,6 @@ EOF $STD npm i -g @immich/cli msg_ok "Updated Immich CLI" - sed -i "s|pgvecto.rs|vectorchord|" /opt/"${APP}"/.env - chown -R immich:immich "$INSTALL_DIR" echo "$RELEASE" >/opt/"${APP}"_version.txt msg_ok "Updated ${APP} to v${RELEASE}" diff --git a/frontend/public/json/immich.json b/frontend/public/json/immich.json index b29bb2c35..337511778 100644 --- a/frontend/public/json/immich.json +++ b/frontend/public/json/immich.json @@ -43,6 +43,10 @@ { "text": "To change upload location, edit 'IMMICH_MEDIA_LOCATION' in `/opt/immich/.env`, and create the symlink 'upload' in /opt/immich/app & /opt/immich/app/machine-learning to your new upload location", "type": "info" + }, + { + "text": "Logs: `/var/log/immich`", + "type": "info" } ] } diff --git a/install/immich-install.sh b/install/immich-install.sh index 65f130310..08f75cf41 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -13,6 +13,8 @@ setting_up_container network_check update_os +setup_uv + msg_info "Configuring apt and installing dependencies" echo "deb http://deb.debian.org/debian testing main contrib" >/etc/apt/sources.list.d/immich.list cat </etc/apt/preferences.d/immich @@ -27,7 +29,6 @@ $STD apt-get install --no-install-recommends -y \ redis \ autoconf \ build-essential \ - python3-venv \ python3-dev \ cmake \ jq \ @@ -63,6 +64,7 @@ $STD apt-get install --no-install-recommends -y \ mesa-utils \ mesa-va-drivers \ mesa-vulkan-drivers \ + ocl-icd-libopencl1 \ tini \ zlib1g $STD apt-get install -y \ @@ -88,14 +90,13 @@ read -r -p "Install OpenVINO dependencies for Intel HW-accelerated machine-learn if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing OpenVINO dependencies" touch ~/.openvino - $STD apt-get -y install --no-install-recommends ocl-icd-libopencl1 tmp_dir=$(mktemp -d) $STD pushd "$tmp_dir" - curl -fsSL https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-core_1.0.17384.11_amd64.deb -O - curl -fsSL https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-opencl_1.0.17384.11_amd64.deb -O - curl -fsSL https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/intel-opencl-icd_24.31.30508.7_amd64.deb -O - curl -fsSL https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/libigdgmm12_22.4.1_amd64.deb -O - $STD dpkg -i ./*.deb + curl -fsSLO https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-core_1.0.17384.11_amd64.deb + curl -fsSLO https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17384.11/intel-igc-opencl_1.0.17384.11_amd64.deb + curl -fsSLO https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/intel-opencl-icd_24.31.30508.7_amd64.deb + curl -fsSLO https://github.com/intel/compute-runtime/releases/download/24.31.30508.7/libigdgmm12_22.4.1_amd64.deb + $STD apt install -y ./*.deb $STD popd rm -rf "$tmp_dir" dpkg -l | grep "intel-opencl-icd" | awk '{print $3}' >~/.intel_version @@ -113,10 +114,11 @@ NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql msg_info "Setting up Postgresql Database" -$STD apt-get install postgresql-16-pgvector -curl -fsSL https://github.com/tensorchord/VectorChord/releases/download/0.3.0/postgresql-16-vchord_0.3.0-1_amd64.deb -o vchord.deb -$STD dpkg -i vchord.deb +VCHORD_RELEASE="$(curl -fsSL https://api.github.com/repos/tensorchord/vectorchord/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')" +curl -fsSL "https://github.com/tensorchord/VectorChord/releases/download/${VCHORD_RELEASE}/postgresql-16-vchord_${VCHORD_RELEASE}-1_amd64.deb" -o vchord.deb +$STD apt install -y ./vchord.deb rm vchord.deb +echo "$VCHORD_RELEASE" >~/.vchord_version DB_NAME="immich" DB_USER="immich" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c18) @@ -273,7 +275,7 @@ msg_ok "Custom Photo-processing Library Compiled" msg_info "Installing ${APPLICATION} (more patience please)" tmp_file=$(mktemp) -RELEASE=$(curl -s https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +RELEASE=$(curl -fsSL https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') curl -fsSL "https://github.com/immich-app/immich/archive/refs/tags/v${RELEASE}.zip" -o "$tmp_file" unzip -q "$tmp_file" INSTALL_DIR="/opt/${APPLICATION}" @@ -304,23 +306,16 @@ cp LICENSE "$APP_DIR" msg_ok "Installed Immich Web Components" cd "$SRC_DIR"/machine-learning -$STD python3 -m venv "$ML_DIR/ml-venv" +export VIRTUAL_ENV="${ML_DIR}/ml-venv" +$STD uv venv "$VIRTUAL_ENV" if [[ -f ~/.openvino ]]; then msg_info "Installing HW-accelerated machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install uv - uv -q sync --extra openvino --no-cache --active - ) - patchelf --clear-execstack "$ML_DIR"/ml-venv/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so + uv -q sync --extra openvino --no-cache --active + patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so" msg_ok "Installed HW-accelerated machine-learning" else msg_info "Installing machine-learning" - ( - source "$ML_DIR"/ml-venv/bin/activate - $STD pip3 install uv - uv -q sync --extra cpu --no-cache --active - ) + uv -q sync --extra cpu --no-cache --active msg_ok "Installed machine-learning" fi cd "$SRC_DIR" @@ -351,7 +346,9 @@ URL_LIST=( https://download.geonames.org/export/dump/cities500.zip https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson ) -echo "${URL_LIST[@]}" | xargs -n1 -P 8 wget -q +for geo in "${URL_LIST[@]}"; do + curl -fsSLO "$geo" +done unzip -q cities500.zip date --iso-8601=seconds | tr -d "\n" >geodata-date.txt rm cities500.zip @@ -390,13 +387,13 @@ cat <"${ML_DIR}"/ml_start.sh #!/usr/bin/env bash cd ${ML_DIR} -. ml-venv/bin/activate +. ${VIRTUAL_ENV}/bin/activate set -a . ${INSTALL_DIR}/.env set +a -python -m immich_ml +python3 -m immich_ml EOF chmod +x "$ML_DIR"/ml_start.sh cat </etc/systemd/system/"${APPLICATION}"-web.service From aec9af49a0026f331ee6f116adc4a022fe4a6dbd Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 06:56:04 +0100 Subject: [PATCH 081/139] Update CHANGELOG.md (#5268) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e8786b43..53935dfef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-19 +### 🚀 Updated Scripts + + - #### 🐞 Bug Fixes + + - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) + ## 2025-06-18 ### 🆕 New Scripts From 86183c638d1811098a630b42495d8a74245dccfa Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 08:08:09 +0200 Subject: [PATCH 082/139] Update .app files (#5269) Co-authored-by: GitHub Actions --- tools/headers/filebrowser-quantum | 6 ++++++ vm/headers/owncloud-vm | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 tools/headers/filebrowser-quantum create mode 100644 vm/headers/owncloud-vm diff --git a/tools/headers/filebrowser-quantum b/tools/headers/filebrowser-quantum new file mode 100644 index 000000000..95c8b4b87 --- /dev/null +++ b/tools/headers/filebrowser-quantum @@ -0,0 +1,6 @@ + _______ __ ____ ____ __ + / ____(_) /__ / __ )_________ _ __________ _____ / __ \__ ______ _____ / /___ ______ ___ + / /_ / / / _ \/ __ / ___/ __ \ | /| / / ___/ _ \/ ___/ / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ + / __/ / / / __/ /_/ / / / /_/ / |/ |/ (__ ) __/ / / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / +/_/ /_/_/\___/_____/_/ \____/|__/|__/____/\___/_/ \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ + diff --git a/vm/headers/owncloud-vm b/vm/headers/owncloud-vm new file mode 100644 index 000000000..54d663d75 --- /dev/null +++ b/vm/headers/owncloud-vm @@ -0,0 +1,6 @@ + ______ __ __ ________ __ _ ____ ___ + /_ __/_ ___________ / //_/__ __ __ ____ _ ______ / ____/ /___ __ ______/ / | | / / |/ / + / / / / / / ___/ __ \/ ,< / _ \/ / / / / __ \ | /| / / __ \/ / / / __ \/ / / / __ / | | / / /|_/ / + / / / /_/ / / / / / / /| / __/ /_/ / / /_/ / |/ |/ / / / / /___/ / /_/ / /_/ / /_/ / | |/ / / / / +/_/ \__,_/_/ /_/ /_/_/ |_\___/\__, / \____/|__/|__/_/ /_/\____/_/\____/\__,_/\__,_/ |___/_/ /_/ + /____/ From dbd2af91b584b352cbd5463031253ebd75161521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20=C5=BDagar?= Date: Thu, 19 Jun 2025 08:27:43 +0200 Subject: [PATCH 083/139] fix typo (#5263) --- frontend/public/json/cloudflare-ddns.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/cloudflare-ddns.json b/frontend/public/json/cloudflare-ddns.json index d867a9dbb..54fb39952 100644 --- a/frontend/public/json/cloudflare-ddns.json +++ b/frontend/public/json/cloudflare-ddns.json @@ -37,7 +37,7 @@ "type": "warning" }, { - "text": "To update the configuration edit `/etc/systemd/system/cloudflare-ddns.service`. After edit please restard with `systemctl restart cloudflare-ddns`", + "text": "To update the configuration edit `/etc/systemd/system/cloudflare-ddns.service`. After edit please restart with `systemctl restart cloudflare-ddns`", "type": "info" } ] From c46d543c43f4a1fbed7430f025d4c46b50a0b07c Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 07:28:27 +0100 Subject: [PATCH 084/139] Update CHANGELOG.md (#5272) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53935dfef..c6487b845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ All LXC instances created using this repository come pre-installed with Midnight - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) +### 🌐 Website + + - #### 📝 Script Information + + - cloudflare-ddns: fix typo in info-text [@LukaZagar](https://github.com/LukaZagar) ([#5263](https://github.com/community-scripts/ProxmoxVE/pull/5263)) + ## 2025-06-18 ### 🆕 New Scripts From 49153887dcafd6fc79fb78386a56b03a7e043cf8 Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 09:49:27 +0200 Subject: [PATCH 085/139] 'Add new script' (#5273) Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> --- ct/headers/wizarr | 6 +++ ct/wizarr.sh | 79 ++++++++++++++++++++++++++++++++ frontend/public/json/wizarr.json | 36 +++++++++++++++ install/wizarr-install.sh | 79 ++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 ct/headers/wizarr create mode 100644 ct/wizarr.sh create mode 100644 frontend/public/json/wizarr.json create mode 100644 install/wizarr-install.sh diff --git a/ct/headers/wizarr b/ct/headers/wizarr new file mode 100644 index 000000000..2ce3850af --- /dev/null +++ b/ct/headers/wizarr @@ -0,0 +1,6 @@ + _ ___ +| | / (_)___ ____ ___________ +| | /| / / /_ / / __ `/ ___/ ___/ +| |/ |/ / / / /_/ /_/ / / / / +|__/|__/_/ /___/\__,_/_/ /_/ + diff --git a/ct/wizarr.sh b/ct/wizarr.sh new file mode 100644 index 000000000..26c8b66e0 --- /dev/null +++ b/ct/wizarr.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wizarrrr/wizarr + +APP="Wizarr" +var_tags="${var_tags:-media;arr}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/wizarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/wizarrrr/wizarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.wizarr 2>/dev/null)" ]] || [[ ! -f ~/.wizarr ]]; then + msg_info "Stopping $APP" + systemctl stop wizarr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/wizarr_backup_$(date +%F).tar.gz" + $STD tar -czf "$BACKUP_FILE" /opt/wizarr/{.env,start.sh} /opt/wizarr/database/ &>/dev/null + msg_ok "Backup Created" + + setup_uv + fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" + + msg_info "Updating $APP to v${RELEASE}" + cd /opt/wizarr + uv -q sync --locked + $STD uv -q run pybabel compile -d app/translations + $STD npm --prefix app/static install + $STD npm --prefix app/static run build:css + mkdir -p ./.cache + $STD tar -xf "$BACKUP_FILE" --directory=/ + $STD uv -q run flask db upgrade + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start wizarr + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf "$BACKUP_FILE" + rm /tmp/"$RELEASE".zip + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + 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}:5690${CL}" diff --git a/frontend/public/json/wizarr.json b/frontend/public/json/wizarr.json new file mode 100644 index 000000000..98f161514 --- /dev/null +++ b/frontend/public/json/wizarr.json @@ -0,0 +1,36 @@ +{ + "name": "Wizarr", + "slug": "wizarr", + "categories": [ + 14, + 13 + ], + "date_created": "2025-06-05", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5690, + "documentation": "https://docs.wizarr.dev/", + "website": "https://docs.wizarr.dev/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/wizarr.webp", + "config_path": "/opt/wizarr/.env", + "description": "Wizarr is an automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your media Server", + "install_methods": [ + { + "type": "default", + "script": "ct/wizarr.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/wizarr-install.sh b/install/wizarr-install.sh new file mode 100644 index 000000000..c9cae2a15 --- /dev/null +++ b/install/wizarr-install.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/wizarrrr/wizarr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y sqlite3 +msg_ok "Installed Dependencies" + +setup_uv +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr" + +msg_info "Configure ${APPLICATION}" +cd /opt/wizarr +uv -q sync --locked +$STD uv -q run pybabel compile -d app/translations +$STD npm --prefix app/static install +$STD npm --prefix app/static run build:css +mkdir -p ./.cache +$STD uv -q run flask db upgrade +msg_ok "Configure ${APPLICATION}" + +msg_info "Creating env, start script and service" +LOCAL_IP="$(hostname -I | awk '{print $1}')" +cat </opt/wizarr/.env +APP_URL=http://${LOCAL_IP} +DISABLE_BUILTIN_AUTH=false +LOG_LEVEL=INFO +EOF + +cat </opt/wizarr/start.sh +#!/usr/bin/env bash + +uv run gunicorn \ + --config gunicorn.conf.py \ + --preload \ + --workers 4 \ + --bind 0.0.0.0:5690 \ + --umask 007 \ + run:app +EOF +chmod u+x /opt/wizarr/start.sh + +cat </etc/systemd/system/wizarr.service +[Unit] +Description=${APPLICATION} Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/wizarr +EnvironmentFile=/opt/wizarr/.env +ExecStart=/opt/wizarr/start.sh +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now wizarr +msg_ok "Created env, start script and service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 63713c6489686b1f2109e5ce70d3b03da86cf351 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 08:49:52 +0100 Subject: [PATCH 086/139] Update date in json (#5274) Co-authored-by: GitHub Actions --- frontend/public/json/wizarr.json | 68 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/frontend/public/json/wizarr.json b/frontend/public/json/wizarr.json index 98f161514..5cd5ca8e3 100644 --- a/frontend/public/json/wizarr.json +++ b/frontend/public/json/wizarr.json @@ -1,36 +1,36 @@ { - "name": "Wizarr", - "slug": "wizarr", - "categories": [ - 14, - 13 - ], - "date_created": "2025-06-05", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 5690, - "documentation": "https://docs.wizarr.dev/", - "website": "https://docs.wizarr.dev/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/wizarr.webp", - "config_path": "/opt/wizarr/.env", - "description": "Wizarr is an automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your media Server", - "install_methods": [ - { - "type": "default", - "script": "ct/wizarr.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] + "name": "Wizarr", + "slug": "wizarr", + "categories": [ + 14, + 13 + ], + "date_created": "2025-06-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5690, + "documentation": "https://docs.wizarr.dev/", + "website": "https://docs.wizarr.dev/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/wizarr.webp", + "config_path": "/opt/wizarr/.env", + "description": "Wizarr is an automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your media Server", + "install_methods": [ + { + "type": "default", + "script": "ct/wizarr.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] } From 0cd347d3b67fe07fae749b41ece57a1bacb6cc61 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 08:49:55 +0100 Subject: [PATCH 087/139] Update CHANGELOG.md (#5275) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6487b845..69c7b06d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-19 +### 🆕 New Scripts + + - Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) + ### 🚀 Updated Scripts - #### 🐞 Bug Fixes From 1c42a2016e7977921715b64b4d923819e82e0ae6 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 08:51:19 +0100 Subject: [PATCH 088/139] Update CHANGELOG.md (#5276) Co-authored-by: github-actions[bot] From bada204f64fe483d44a55358cc8d924db5c68b65 Mon Sep 17 00:00:00 2001 From: "push-app-to-main[bot]" <203845782+push-app-to-main[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:09:16 +0200 Subject: [PATCH 089/139] planka (#5277) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 'Add new script' * Update planka.sh * Update planka.sh * Update planka.json --------- Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com> Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/headers/planka | 6 ++ ct/planka.sh | 77 +++++++++++++++++++++++ frontend/public/json/planka.json | 40 ++++++++++++ install/planka-install.sh | 103 +++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 ct/headers/planka create mode 100644 ct/planka.sh create mode 100644 frontend/public/json/planka.json create mode 100644 install/planka-install.sh diff --git a/ct/headers/planka b/ct/headers/planka new file mode 100644 index 000000000..852d475d3 --- /dev/null +++ b/ct/headers/planka @@ -0,0 +1,6 @@ + __ __ + ____ / /___ _____ / /______ _ + / __ \/ / __ `/ __ \/ //_/ __ `/ + / /_/ / / /_/ / / / / ,< / /_/ / + / .___/_/\__,_/_/ /_/_/|_|\__,_/ +/_/ diff --git a/ct/planka.sh b/ct/planka.sh new file mode 100644 index 000000000..4a8a35a1b --- /dev/null +++ b/ct/planka.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plankanban/planka + +APP="PLANKA" +var_tags="${var_tags:-Arr}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +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/planka.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -s https://api.github.com/repos/plankanban/planka/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.planka 2>/dev/null)" ]] || [[ ! -f ~/.planka ]]; then + msg_info "Stopping $APP" + systemctl stop planka + msg_ok "Stopped $APP" + + msg_info "Updating $APP to ${RELEASE}" + mkdir -p /opt/planka-backup + mkdir -p /opt/planka-backup/favicons + mkdir -p /opt/planka-backup/user-avatars + mkdir -p /opt/planka-backup/background-images + mkdir -p /opt/planka-backup/attachments + mv /opt/planka/planka/.env /opt/planka-backup + mv /opt/planka/planka/public/favicons/* /opt/planka-backup/favicons/ + mv /opt/planka/planka/public/user-avatars/* /opt/planka-backup/user-avatars/ + mv /opt/planka/planka/public/background-images/* /opt/planka-backup/background-images/ + mv /opt/planka/planka/private/attachments/* /opt/planka-backup/attachments/ + rm -rf /opt/planka + fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" + cd /opt/planka/planka + $STD npm install + mv /opt/planka-backup/.env /opt/planka/planka/ + mv /opt/planka-backup/favicons/* /opt/planka/planka/public/favicons/ + mv /opt/planka-backup/user-avatars/* /opt/planka/planka/public/user-avatars/ + mv /opt/planka-backup/background-images/* /opt/planka/planka/public/background-images/ + mv /opt/planka-backup/attachments/* /opt/planka/planka/private/attachments/ + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start planka + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + 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}:1337${CL}" diff --git a/frontend/public/json/planka.json b/frontend/public/json/planka.json new file mode 100644 index 000000000..a3cba89e0 --- /dev/null +++ b/frontend/public/json/planka.json @@ -0,0 +1,40 @@ +{ + "name": "PLANKA", + "slug": "planka", + "categories": [ + 12 + ], + "date_created": "2025-06-09", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 1337, + "documentation": "https://docs.planka.cloud/", + "website": "https://planka.app/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/planka.webp", + "config_path": "/opt/planka/planka/.env", + "description": "Planka is a powerful, project management platform that transforms how teams collaborate. Create projects with multiple boards, organize tasks with intuitive drag-and-drop cards, attach files, write rich markdown descriptions, set due dates, assign team members, and keep conversations flowing with comments and labels—all with seamless real-time updates and smart notifications.", + "install_methods": [ + { + "type": "default", + "script": "ct/planka.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Type `cat ~/planka.creds` inside LXC to see admin user and database credentials.", + "type": "info" + } + ] +} diff --git a/install/planka-install.sh b/install/planka-install.sh new file mode 100644 index 000000000..b0cd1309c --- /dev/null +++ b/install/planka-install.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/plankanban/planka + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y \ + unzip \ + build-essential \ + python3-venv +msg_ok "Installed dependencies" + +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql + +msg_info "Setting up PostgreSQL Database" +DB_NAME=planka +DB_USER=planka +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +{ + echo "PLANKA DB Credentials" + echo "PLANKA Database User: $DB_USER" + echo "PLANKA Database Password: $DB_PASS" + echo "PLANKA Database Name: $DB_NAME" +} >>~/planka.creds +msg_ok "Set up PostgreSQL Database" + +fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" + +msg_info "Configuring PLANKA" +LOCAL_IP=$(hostname -I | awk '{print $1}') +SECRET_KEY=$(openssl rand -hex 64) +cd /opt/planka/planka +$STD npm install +cp .env.sample .env +sed -i "s#http://localhost:1337#http://$LOCAL_IP:1337#g" /opt/planka/planka/.env +sed -i "s#postgres@localhost#planka:$DB_PASS@localhost#g" /opt/planka/planka/.env +sed -i "s#notsecretkey#$SECRET_KEY#g" /opt/planka/planka/.env +$STD npm run db:init +msg_ok "Configured PLANKA" + +msg_info "Creating Admin User" +ADMIN_EMAIL="admin@planka.local" +ADMIN_PASSWORD="$(openssl rand -base64 12)" +ADMIN_NAME="Administrator" +ADMIN_USERNAME="admin" +echo "" >>.env +echo "# Temporary admin user creation settings" >>.env +echo "DEFAULT_ADMIN_EMAIL=$ADMIN_EMAIL" >>.env +echo "DEFAULT_ADMIN_PASSWORD=$ADMIN_PASSWORD" >>.env +echo "DEFAULT_ADMIN_NAME=$ADMIN_NAME" >>.env +echo "DEFAULT_ADMIN_USERNAME=$ADMIN_USERNAME" >>.env +$STD npm run db:seed +sed -i '/# Temporary admin user creation settings/,$d' .env +{ + echo "" + echo "PLANKA Admin Credentials" + echo "Admin Email: $ADMIN_EMAIL" + echo "Admin Password: $ADMIN_PASSWORD" + echo "Admin Name: $ADMIN_NAME" + echo "Admin Username: $ADMIN_USERNAME" +} >>~/planka.creds +msg_ok "Created Admin User" + +msg_info "Creating Service" +cat </etc/systemd/system/planka.service +[Unit] +Description=planka Service +After=network.target + +[Service] +WorkingDirectory=/opt/planka/planka +ExecStart=/usr/bin/npm start --prod +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now planka +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 14ffff4c6a54273bf0969fc5731dcb3a6e728528 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:09:53 +0100 Subject: [PATCH 090/139] Update date in json (#5279) Co-authored-by: GitHub Actions --- frontend/public/json/planka.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/planka.json b/frontend/public/json/planka.json index a3cba89e0..b8d7a32af 100644 --- a/frontend/public/json/planka.json +++ b/frontend/public/json/planka.json @@ -4,7 +4,7 @@ "categories": [ 12 ], - "date_created": "2025-06-09", + "date_created": "2025-06-19", "type": "ct", "updateable": true, "privileged": false, From ab1f4c119676b7ab7152c4d5c74ce5aee5a0b3bf Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:09:58 +0100 Subject: [PATCH 091/139] Update CHANGELOG.md (#5280) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c7b06d6..235284c78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🆕 New Scripts - - Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) + - PLANKA ([#5277](https://github.com/community-scripts/ProxmoxVE/pull/5277)) +- Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) ### 🚀 Updated Scripts From db7a13fcedaa8fc65a9995d288334f8cc04cbd85 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:11:56 +0100 Subject: [PATCH 092/139] Update CHANGELOG.md (#5282) Co-authored-by: github-actions[bot] From 3ca014b757857fe1cf55844147ecb610b5ab4c65 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:13:36 +0200 Subject: [PATCH 093/139] Update .app files (#5281) Co-authored-by: GitHub Actions --- ct/headers/planka | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/headers/planka b/ct/headers/planka index 852d475d3..cf874359b 100644 --- a/ct/headers/planka +++ b/ct/headers/planka @@ -1,6 +1,6 @@ - __ __ - ____ / /___ _____ / /______ _ - / __ \/ / __ `/ __ \/ //_/ __ `/ - / /_/ / / /_/ / / / / ,< / /_/ / - / .___/_/\__,_/_/ /_/_/|_|\__,_/ -/_/ + ____ __ ___ _ ____ __ ___ + / __ \/ / / | / | / / //_// | + / /_/ / / / /| | / |/ / ,< / /| | + / ____/ /___/ ___ |/ /| / /| |/ ___ | +/_/ /_____/_/ |_/_/ |_/_/ |_/_/ |_| + From fe1df0b344d0c4708462c5cf60570fe02af4fcc3 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:07:49 +0200 Subject: [PATCH 094/139] Update versions.json (#5285) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 80 +++++++++++++++--------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 4c8215f24..5f854aabe 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,39 @@ [ + { + "name": "rclone/rclone", + "version": "v1.70.1", + "date": "2025-06-19T10:48:53Z" + }, + { + "name": "fallenbagel/jellyseerr", + "version": "preview-dns-cache-manager", + "date": "2025-06-19T10:11:12Z" + }, + { + "name": "mattermost/mattermost", + "version": "v9.11.17", + "date": "2025-06-18T08:12:05Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.98.2", + "date": "2025-06-18T18:20:16Z" + }, + { + "name": "redis/redis", + "version": "8.2-m01-int2", + "date": "2025-06-12T08:52:10Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2029", + "date": "2025-06-19T05:56:19Z" + }, + { + "name": "prometheus-pve/prometheus-pve-exporter", + "version": "v3.5.5", + "date": "2025-06-19T05:43:47Z" + }, { "name": "docmost/docmost", "version": "v0.21.0", @@ -15,9 +50,9 @@ "date": "2025-05-21T18:00:32Z" }, { - "name": "n8n-io/n8n", - "version": "n8n@1.98.2", - "date": "2025-06-18T18:20:16Z" + "name": "keycloak/keycloak", + "version": "26.2.5", + "date": "2025-05-28T06:49:43Z" }, { "name": "pterodactyl/panel", @@ -69,11 +104,6 @@ "version": "v10.7.0", "date": "2025-06-18T11:57:05Z" }, - { - "name": "rclone/rclone", - "version": "v1.70.0", - "date": "2025-06-18T10:31:28Z" - }, { "name": "esphome/esphome", "version": "2025.6.0", @@ -94,16 +124,6 @@ "version": "2.0.0-pre3", "date": "2025-06-18T08:01:24Z" }, - { - "name": "mattermost/mattermost", - "version": "v9.11.17", - "date": "2025-06-18T08:12:05Z" - }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2026", - "date": "2025-06-18T05:51:39Z" - }, { "name": "cross-seed/cross-seed", "version": "v6.12.7", @@ -119,11 +139,6 @@ "version": "v11.5.6", "date": "2025-06-17T22:00:40Z" }, - { - "name": "keycloak/keycloak", - "version": "26.2.5", - "date": "2025-05-28T06:49:43Z" - }, { "name": "jenkinsci/jenkins", "version": "jenkins-2.515", @@ -144,11 +159,6 @@ "version": "v2.1.5", "date": "2025-06-17T18:04:11Z" }, - { - "name": "fallenbagel/jellyseerr", - "version": "preview-sort-userlist", - "date": "2025-06-17T18:02:25Z" - }, { "name": "BookStackApp/BookStack", "version": "v25.05.1", @@ -216,8 +226,8 @@ }, { "name": "runtipi/runtipi", - "version": "nightly", - "date": "2025-06-16T17:35:17Z" + "version": "v4.2.1", + "date": "2025-06-03T20:04:28Z" }, { "name": "emqx/emqx", @@ -389,11 +399,6 @@ "version": "v2.37.1", "date": "2025-06-12T09:00:21Z" }, - { - "name": "redis/redis", - "version": "8.2-m01-int2", - "date": "2025-06-12T08:52:10Z" - }, { "name": "zitadel/zitadel", "version": "v3.3.0", @@ -899,11 +904,6 @@ "version": "v0.9.80", "date": "2025-05-02T16:48:15Z" }, - { - "name": "prometheus-pve/prometheus-pve-exporter", - "version": "v3.5.4", - "date": "2025-05-02T13:42:06Z" - }, { "name": "WordPress/WordPress", "version": "6.8.1", From 0c1fad54b698d692164ff9e32d44115e1169a3e5 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 19 Jun 2025 08:21:12 -0400 Subject: [PATCH 095/139] immich-install.sh: restore pgvector module install (#5286) --- install/immich-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/immich-install.sh b/install/immich-install.sh index 08f75cf41..ccc8dd648 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -111,7 +111,7 @@ if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then fi NODE_VERSION="22" setup_nodejs -PG_VERSION="16" setup_postgresql +PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql msg_info "Setting up Postgresql Database" VCHORD_RELEASE="$(curl -fsSL https://api.github.com/repos/tensorchord/vectorchord/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')" From 5042f6edb0d74c16b8b4c58ada0d98264b7e5c92 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:21:39 +0100 Subject: [PATCH 096/139] Update CHANGELOG.md (#5287) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 235284c78..a8db0bd12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - immich-install.sh: restore pgvector module install [@vhsdream](https://github.com/vhsdream) ([#5286](https://github.com/community-scripts/ProxmoxVE/pull/5286)) - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) ### 🌐 Website From 796aaaa1d4903cf6f28e985f5f7ba80e5010533b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:39:30 +0200 Subject: [PATCH 097/139] improve .gitattributes for linguist --- .gitattributes | Bin 42 -> 1160 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.gitattributes b/.gitattributes index 713f7330977dacbcf6fab1cd4a9fe8de2029cf45..d2573f2f29370004f8fc2aac227088663e2dec27 100644 GIT binary patch literal 1160 zcmbVL!EVAZ482$CKSbifD$#yH4;w=jlg1=k4m+B}ZNox>8W+LeFM&Z}0|e`(N#C>m zp6&M7;JzmedaP&=h~+gZ#Y(BrGO+H@SQ>Bz)-rAcdg6U42VoG!wq{D|s+BH@KtfJ1 zSC~@Mx=c|#hf{(A$Ak*god%DWt>#^Vzqr(vz{x$<_)U38?x6*l@oBmqWQO%i)35|q zGI4mNP$b;wQvU3ZcfPQ$8OIep@;syr z|GENgmnVCD7PN&cP+Uo>+xb~5Pa@;B^}p~n_(9(}ffmpRMz|qXyNkEzOzl3T(2`+j JRI17__y)%#TAu&_ literal 42 rcmezWPm4i;p@boop#n(SG9)sT0_l99SSpar0gB`>q%rU^a4`S?;*to7 From 5db3d92286939fc5b6cf9753a4e41eb886419ff1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:48:35 +0200 Subject: [PATCH 098/139] Add hostname validation and replace recursion for invalid inputs in advanced settings (#5291) --- misc/build.func | 97 +++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/misc/build.func b/misc/build.func index 265c32a56..2e053ff7f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -370,7 +370,7 @@ base_settings() { write_config() { mkdir -p /opt/community-scripts # This function writes the configuration to a file. - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then FILEPATH="/opt/community-scripts/${NSAPP}.conf" if [[ ! -f $FILEPATH ]]; then cat <"$FILEPATH" @@ -403,7 +403,7 @@ EOF echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" else echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then rm -f "$FILEPATH" cat <"$FILEPATH" # ${NSAPP} Configuration File @@ -558,53 +558,72 @@ advanced_settings() { exit_script fi - if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi + while true; do + if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then if [ -z "$DISK_SIZE" ]; then DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings - fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi - else - exit_script - fi - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + if [ -z "$CORE_COUNT" ]; then CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi - else - exit_script - fi - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + if [ -z "$RAM_SIZE" ]; then RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" fi - else - exit_script - fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done BRIDGES="" IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) @@ -779,14 +798,14 @@ advanced_settings() { exit_script fi - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then SSH_AUTHORIZED_KEY="" fi if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then SSH="yes" else SSH="no" From 94ad4f2e4096e81baf93a63f72070b595020ecf1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:48:51 +0200 Subject: [PATCH 099/139] Ubuntu 25.04 VM (#5284) --- frontend/public/json/ubuntu2504-vm.json | 40 ++ vm/ubuntu2504-vm.sh | 528 ++++++++++++++++++++++++ 2 files changed, 568 insertions(+) create mode 100644 frontend/public/json/ubuntu2504-vm.json create mode 100644 vm/ubuntu2504-vm.sh diff --git a/frontend/public/json/ubuntu2504-vm.json b/frontend/public/json/ubuntu2504-vm.json new file mode 100644 index 000000000..8b6c6838f --- /dev/null +++ b/frontend/public/json/ubuntu2504-vm.json @@ -0,0 +1,40 @@ +{ + "name": "Ubuntu 25.04", + "slug": "ubuntu2504-vm", + "categories": [ + 2 + ], + "date_created": "2025-06-19", + "type": "vm", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": "https://ubuntu.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ubuntu.webp", + "config_path": "", + "description": "Ubuntu is a distribution based on Debian, designed to have regular releases and a consistent user experience.", + "install_methods": [ + { + "type": "default", + "script": "vm/ubuntu2504-vm.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "after installation, checkout: ´https://github.com/community-scripts/ProxmoxVE/discussions/272´", + "type": "info" + } + ] +} diff --git a/vm/ubuntu2504-vm.sh b/vm/ubuntu2504-vm.sh new file mode 100644 index 000000000..39e32d0ee --- /dev/null +++ b/vm/ubuntu2504-vm.sh @@ -0,0 +1,528 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) + +function header_info { + clear + cat <<"EOF" + __ ____ __ ___ ______ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (Plucky Puffin) + +EOF +} +header_info +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +NSAPP="ubuntu-2504-vm" +var_os="ubuntu" +var_version="2504" + +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") +BFR="\\r\\033[K" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + post_update_to_api "failed" "$command" + 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" + cleanup_vmid +} + +function get_valid_nextid() { + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" +} + +function cleanup_vmid() { + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null + fi +} + +function cleanup() { + popd >/dev/null + rm -rf $TEMP_DIR +} + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Ubuntu 25.04 VM" --yesno "This will create a New Ubuntu 25.04 VM. Proceed?" 10 58; then + : +else + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit +fi + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +function pve_check() { + if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then + msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +function default_settings() { + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="7G" + DISK_CACHE="" + HN="ubuntu" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi + else + exit-script + fi + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi + else + exit-script + fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 ubuntu --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="ubuntu" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi + else + exit-script + fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else + exit-script + fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit-script + fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + fi + else + exit-script + fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit-script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Ubuntu 25.04 VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 25.04 VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} +check_root +arch_check +pve_check +ssh_check +start_script +post_to_api_vm + +msg_info "Validating Storage" +while read -r line; do + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") +done < <(pvesm status -content images | awk 'NR>1') +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} +else + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done +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 +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +curl -f#SL -o "$(basename "$URL")" "$URL" +echo -en "\e[1A\e[0K" +FILE=$(basename $URL) +msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir | cifs) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT="-format qcow2" + THIN="" + ;; +btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT="-format raw" + FORMAT=",efitype=4m" + THIN="" + ;; +esac +for i in {0,1}; do + disk="DISK$i" + eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} + eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} +done + +msg_info "Creating a Ubuntu 25.04 VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -ide2 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +DESCRIPTION=$( + cat < + + Logo + + +

ubuntu VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + +
+EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a Ubuntu 25.04 VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Ubuntu 25.04 VM" + qm start $VMID + msg_ok "Started Ubuntu 25.04 VM" +fi +post_update_to_api "done" "none" +msg_ok "Completed Successfully!\n" +echo -e "Setup Cloud-Init before starting \n +More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" From 451608b4451b48e8612b87e1c8e107fa308a708d Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:49:00 +0100 Subject: [PATCH 100/139] Update CHANGELOG.md (#5293) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8db0bd12..7ab115ed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,12 @@ All LXC instances created using this repository come pre-installed with Midnight - immich-install.sh: restore pgvector module install [@vhsdream](https://github.com/vhsdream) ([#5286](https://github.com/community-scripts/ProxmoxVE/pull/5286)) - Immich: prepare for v1.135.0 [@vhsdream](https://github.com/vhsdream) ([#5025](https://github.com/community-scripts/ProxmoxVE/pull/5025)) +### 🧰 Maintenance + + - #### ✨ New Features + + - [core]: add validation and replace recursion for invalid inputs in adv. settings [@MickLesk](https://github.com/MickLesk) ([#5291](https://github.com/community-scripts/ProxmoxVE/pull/5291)) + ### 🌐 Website - #### 📝 Script Information From 204ec64f14380cb2f4295506470952c7a3065e9f Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:49:54 +0100 Subject: [PATCH 101/139] Update CHANGELOG.md (#5294) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab115ed8..cb15cbcde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🆕 New Scripts - - PLANKA ([#5277](https://github.com/community-scripts/ProxmoxVE/pull/5277)) + - Ubuntu 25.04 VM [@MickLesk](https://github.com/MickLesk) ([#5284](https://github.com/community-scripts/ProxmoxVE/pull/5284)) +- PLANKA ([#5277](https://github.com/community-scripts/ProxmoxVE/pull/5277)) - Wizarr ([#5273](https://github.com/community-scripts/ProxmoxVE/pull/5273)) ### 🚀 Updated Scripts From b5012c4225622074c910b4edfa509004fb915fa9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:50:23 +0200 Subject: [PATCH 102/139] Feat: Quorum Status (#5278) --- .github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md | 4 +- .github/autolabeler-config.json | 61 +++++++++++++++----- .github/workflows/validate-filenames.yml | 4 +- misc/build.func | 2 +- {ct => misc}/create_lxc.sh | 11 ++++ 5 files changed, 64 insertions(+), 18 deletions(-) rename {ct => misc}/create_lxc.sh (95%) diff --git a/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md b/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md index 158a5c6ce..17a1ff4a4 100644 --- a/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md +++ b/.github/CONTRIBUTOR_AND_GUIDES/CODE-AUDIT.md @@ -5,10 +5,10 @@ 1) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh): This script collects system parameters. (Also holds the function to update the application.) 2) [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func): Adds user settings and integrates collected information. -3) [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/create_lxc.sh): Constructs the LXC container. +3) [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh): Constructs the LXC container. 4) [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh): Executes functions from [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), and installs the application. 5) [adguard.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/adguard.sh) (again): To display the completion message. -The installation process uses reusable scripts: [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func), [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/ct/create_lxc.sh), and [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), which are not specific to any particular application. +The installation process uses reusable scripts: [build.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/build.func), [create_lxc.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/create_lxc.sh), and [install.func](https://github.com/community-scripts/ProxmoxVE/blob/main/misc/install.func), which are not specific to any particular application. To gain a better understanding, focus on reviewing [adguard-install.sh](https://github.com/community-scripts/ProxmoxVE/blob/main/install/adguard-install.sh). This script contains the commands and configurations for installing and configuring AdGuard Home within the LXC container. diff --git a/.github/autolabeler-config.json b/.github/autolabeler-config.json index 068d559fb..ad9ce69e5 100644 --- a/.github/autolabeler-config.json +++ b/.github/autolabeler-config.json @@ -2,21 +2,43 @@ "new script": [ { "fileStatus": "added", - "includeGlobs": ["ct/**", "install/**", "misc/**", "turnkey/**", "vm/**"], + "includeGlobs": [ + "ct/**", + "install/**", + "misc/**", + "turnkey/**", + "vm/**" + ], "excludeGlobs": [] } ], "update script": [ { "fileStatus": "modified", - "includeGlobs": ["ct/**", "install/**", "misc/**", "turnkey/**", "vm/**"], - "excludeGlobs": ["misc/build.func", "misc/install.func", "misc/api.func"] + "includeGlobs": [ + "ct/**", + "install/**", + "misc/**", + "turnkey/**", + "vm/**" + ], + "excludeGlobs": [ + "misc/build.func", + "misc/install.func", + "misc/api.func" + ] } ], "delete script": [ { "fileStatus": "removed", - "includeGlobs": ["ct/**", "install/**", "misc/**", "turnkey/**", "vm/**"], + "includeGlobs": [ + "ct/**", + "install/**", + "misc/**", + "turnkey/**", + "vm/**" + ], "excludeGlobs": [] } ], @@ -27,7 +49,7 @@ "*.md", ".github/**", "misc/*.func", - "ct/create_lxc.sh", + "misc/create_lxc.sh", "api/**" ], "excludeGlobs": [] @@ -36,46 +58,57 @@ "core": [ { "fileStatus": null, - "includeGlobs": ["misc/*.func", "ct/create_lxc.sh"], + "includeGlobs": [ + "misc/*.func", + "misc/create_lxc.sh" + ], "excludeGlobs": [] } ], "website": [ { "fileStatus": null, - "includeGlobs": ["frontend/**"], + "includeGlobs": [ + "frontend/**" + ], "excludeGlobs": [] } ], "api": [ { "fileStatus": null, - "includeGlobs": ["api/**", "misc/api.func"], + "includeGlobs": [ + "api/**", + "misc/api.func" + ], "excludeGlobs": [] } ], "github": [ { "fileStatus": null, - "includeGlobs": [".github/**"], + "includeGlobs": [ + ".github/**" + ], "excludeGlobs": [] } ], "json": [ { "fileStatus": "modified", - "includeGlobs": ["frontend/public/json/**"], + "includeGlobs": [ + "frontend/public/json/**" + ], "excludeGlobs": [] } ], - "high risk": [ { "fileStatus": null, "includeGlobs": [ "misc/build.func", "misc/install.func", - "ct/create_lxc.sh" + "misc/create_lxc.sh" ], "excludeGlobs": [] } @@ -83,7 +116,9 @@ "documentation": [ { "fileStatus": null, - "includeGlobs": ["*.md"], + "includeGlobs": [ + "*.md" + ], "excludeGlobs": [] } ] diff --git a/.github/workflows/validate-filenames.yml b/.github/workflows/validate-filenames.yml index 1c2ae38b1..d881be586 100644 --- a/.github/workflows/validate-filenames.yml +++ b/.github/workflows/validate-filenames.yml @@ -51,8 +51,8 @@ jobs: NON_COMPLIANT_FILES="" for FILE in $CHANGED_FILES; do - # Datei "ct/create_lxc.sh" explizit überspringen - if [[ "$FILE" == "ct/create_lxc.sh" ]]; then + # Skip File "misc/create_lxc.sh" + if [[ "$FILE" == "misc/create_lxc.sh" ]]; then continue fi BASENAME=$(echo "$(basename "${FILE%.*}")") diff --git a/misc/build.func b/misc/build.func index 2e053ff7f..57d325182 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1143,7 +1143,7 @@ build_container() { $PW " # This executes create_lxc.sh and creates the container and .conf file - bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/create_lxc.sh)" $? + bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/create_lxc.sh)" $? LXC_CONFIG=/etc/pve/lxc/${CTID}.conf if [ "$CT_TYPE" == "0" ]; then diff --git a/ct/create_lxc.sh b/misc/create_lxc.sh similarity index 95% rename from ct/create_lxc.sh rename to misc/create_lxc.sh index 7d0779919..17ee1e516 100644 --- a/ct/create_lxc.sh +++ b/misc/create_lxc.sh @@ -207,6 +207,17 @@ 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." +# Check Cluster Quorum if in Cluster +if [ -f /etc/pve/corosync.conf ]; then + msg_info "Checking Proxmox cluster quorum status" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + printf "\e[?25h" + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" +fi + # Update LXC template list msg_info "Updating LXC Template List" #check_network From 9bbc0c90e38a869b43d9346bd9e6302da2b25022 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:50:25 +0100 Subject: [PATCH 103/139] Update CHANGELOG.md (#5295) Co-authored-by: github-actions[bot] From 8fb0a165689e258e59652a9aa5b190401e14e012 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 17:55:45 +0100 Subject: [PATCH 104/139] Update CHANGELOG.md (#5296) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb15cbcde..3eda99198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### ✨ New Features + - [core]: Feature - Check Quorum Status in create_lxc to prevent issues [@MickLesk](https://github.com/MickLesk) ([#5278](https://github.com/community-scripts/ProxmoxVE/pull/5278)) - [core]: add validation and replace recursion for invalid inputs in adv. settings [@MickLesk](https://github.com/MickLesk) ([#5291](https://github.com/community-scripts/ProxmoxVE/pull/5291)) ### 🌐 Website From 76c99518b11d98887e16a75ea4776cf476a3f002 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:03:06 +0200 Subject: [PATCH 105/139] remove wrong package-lock.json in main path --- package-lock.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index acd06abd6..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ProxmoxVE", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} From 39b18ea01144ecf406c72f76c2f261cba6b2cf4d Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 02:15:04 +0200 Subject: [PATCH 106/139] Update versions.json (#5306) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 74 +++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 5f854aabe..38f367e22 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,13 +1,48 @@ [ + { + "name": "plexguide/Huntarr.io", + "version": "8.1.3", + "date": "2025-06-19T23:45:15Z" + }, + { + "name": "go-gitea/gitea", + "version": "v1.24.1", + "date": "2025-06-19T19:38:29Z" + }, + { + "name": "paperless-ngx/paperless-ngx", + "version": "v2.17.1", + "date": "2025-06-19T19:35:01Z" + }, + { + "name": "pocket-id/pocket-id", + "version": "v1.4.0", + "date": "2025-06-19T18:30:11Z" + }, + { + "name": "immich-app/immich", + "version": "v1.135.1", + "date": "2025-06-19T17:55:06Z" + }, { "name": "rclone/rclone", "version": "v1.70.1", - "date": "2025-06-19T10:48:53Z" + "date": "2025-06-19T13:19:02Z" }, { "name": "fallenbagel/jellyseerr", "version": "preview-dns-cache-manager", - "date": "2025-06-19T10:11:12Z" + "date": "2025-06-19T12:05:10Z" + }, + { + "name": "icereed/paperless-gpt", + "version": "v0.21.0", + "date": "2025-06-19T11:54:59Z" + }, + { + "name": "neo4j/neo4j", + "version": "2025.05.1", + "date": "2025-06-19T11:28:36Z" }, { "name": "mattermost/mattermost", @@ -39,11 +74,6 @@ "version": "v0.21.0", "date": "2025-06-18T21:43:27Z" }, - { - "name": "immich-app/immich", - "version": "v1.135.0", - "date": "2025-06-18T20:37:30Z" - }, { "name": "ipfs/kubo", "version": "v0.35.0", @@ -89,11 +119,6 @@ "version": "7.2.8", "date": "2025-06-18T13:46:00Z" }, - { - "name": "plexguide/Huntarr.io", - "version": "8.0.9", - "date": "2025-06-18T13:12:23Z" - }, { "name": "Bubka/2FAuth", "version": "v5.6.0", @@ -464,11 +489,6 @@ "version": "v0.8.4", "date": "2025-06-10T07:57:14Z" }, - { - "name": "go-gitea/gitea", - "version": "v1.24.0", - "date": "2025-06-10T02:00:38Z" - }, { "name": "Sonarr/Sonarr", "version": "v4.0.14.2939", @@ -479,11 +499,6 @@ "version": "v1.84.2", "date": "2025-06-09T23:43:27Z" }, - { - "name": "pocket-id/pocket-id", - "version": "v1.3.1", - "date": "2025-06-09T21:07:27Z" - }, { "name": "pocketbase/pocketbase", "version": "v0.28.3", @@ -504,11 +519,6 @@ "version": "v0.10.0", "date": "2025-06-09T13:37:07Z" }, - { - "name": "neo4j/neo4j", - "version": "5.26.8", - "date": "2025-06-08T22:50:58Z" - }, { "name": "Forceu/Gokapi", "version": "v2.0.1", @@ -554,11 +564,6 @@ "version": "10.1.42", "date": "2025-06-05T22:39:40Z" }, - { - "name": "paperless-ngx/paperless-ngx", - "version": "v2.16.3", - "date": "2025-06-05T21:16:59Z" - }, { "name": "netbox-community/netbox", "version": "v4.3.2", @@ -654,11 +659,6 @@ "version": "v0.14.1", "date": "2024-08-29T22:32:51Z" }, - { - "name": "icereed/paperless-gpt", - "version": "v0.20.0", - "date": "2025-05-30T14:39:51Z" - }, { "name": "binwiederhier/ntfy", "version": "v2.12.0", From ad8462bc10a2afd23b3cf1985a8c0947156ff849 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 01:15:37 +0100 Subject: [PATCH 107/139] Update CHANGELOG.md (#5307) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eda99198..9c840a55c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-20 + ## 2025-06-19 ### 🆕 New Scripts From 69d939484080286cdc9a364f75245be90d4b73ee Mon Sep 17 00:00:00 2001 From: Mateusz Haligowski Date: Thu, 19 Jun 2025 22:40:51 -0700 Subject: [PATCH 108/139] (turnkey) Add OpenLDAP (#5305) --- turnkey/turnkey.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/turnkey/turnkey.sh b/turnkey/turnkey.sh index 71cfca81d..93337f02e 100644 --- a/turnkey/turnkey.sh +++ b/turnkey/turnkey.sh @@ -79,6 +79,7 @@ mediaserver Media Server nextcloud Nextcloud observium Observium odoo Odoo +openldap OpenLDAP openvpn OpenVPN owncloud ownCloud phpbb phpBB @@ -201,7 +202,7 @@ echo "TurnKey ${turnkey} password: ${PASS}" >>~/turnkey-${turnkey}.creds # file # Start container msg "Starting LXC Container..." pct start "$CTID" -sleep 5 +sleep 10 # Get container IP set +euo pipefail # Turn off error checking From 6bc17a003188fa75ffacb8ed4176552ea70d0397 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 06:41:16 +0100 Subject: [PATCH 109/139] Update CHANGELOG.md (#5308) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c840a55c..2e9041e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ All LXC instances created using this repository come pre-installed with Midnight ## 2025-06-20 +### 🚀 Updated Scripts + + - #### ✨ New Features + + - (turnkey) Add OpenLDAP as a TurnKey option [@mhaligowski](https://github.com/mhaligowski) ([#5305](https://github.com/community-scripts/ProxmoxVE/pull/5305)) + ## 2025-06-19 ### 🆕 New Scripts From b019991ee67135e8c2d00f020d069abd5c8d6390 Mon Sep 17 00:00:00 2001 From: Niklas <33813894+Niklas04@users.noreply.github.com> Date: Fri, 20 Jun 2025 07:42:12 +0200 Subject: [PATCH 110/139] Update changedetection.sh (#5301) Add Microsoft Edge as Broswer - as needed for changedetection/playwright to work --- ct/changedetection.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/changedetection.sh b/ct/changedetection.sh index 0f1bdeeeb..e9dd4c81f 100644 --- a/ct/changedetection.sh +++ b/ct/changedetection.sh @@ -52,7 +52,7 @@ function update_script() { $STD /opt/browserless/node_modules/playwright-core/cli.js install --with-deps # Update Chrome separately, as it has to be done with the force option. Otherwise the installation of other browsers will not be done if Chrome is already installed. $STD /opt/browserless/node_modules/playwright-core/cli.js install --force chrome - $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit + $STD /opt/browserless/node_modules/playwright-core/cli.js install chromium firefox webkit msedge $STD npm run build --prefix /opt/browserless $STD npm run build:function --prefix /opt/browserless $STD npm prune production --prefix /opt/browserless @@ -74,4 +74,4 @@ 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}:5000${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5000${CL}" From ac203f4e799f6f4f1c489aa321147017d9057372 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 06:42:45 +0100 Subject: [PATCH 111/139] Update CHANGELOG.md (#5309) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e9041e7f..5dec031cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts + - #### 🐞 Bug Fixes + + - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) + - #### ✨ New Features - (turnkey) Add OpenLDAP as a TurnKey option [@mhaligowski](https://github.com/mhaligowski) ([#5305](https://github.com/community-scripts/ProxmoxVE/pull/5305)) From b52cba817f43c75c32988c51f0047aae1b1c6a1c Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 20 Jun 2025 01:47:46 -0400 Subject: [PATCH 112/139] Immich: test fix for 5299 (#5300) --- ct/immich.sh | 3 +++ install/immich-install.sh | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ct/immich.sh b/ct/immich.sh index 7d421ed9a..3622199f2 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -226,6 +226,9 @@ function update_script() { mv "$APP-$RELEASE"/ "$SRC_DIR" mkdir -p "$ML_DIR" cd "$SRC_DIR"/server + if [[ "$RELEASE" == "1.135.1" ]]; then + rm ./src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts + fi $STD npm install -g node-gyp node-pre-gyp $STD npm ci $STD npm run build diff --git a/install/immich-install.sh b/install/immich-install.sh index ccc8dd648..b8cfce932 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -273,11 +273,6 @@ rm -rf "$SOURCE"/build } >~/.immich_library_revisions msg_ok "Custom Photo-processing Library Compiled" -msg_info "Installing ${APPLICATION} (more patience please)" -tmp_file=$(mktemp) -RELEASE=$(curl -fsSL https://api.github.com/repos/immich-app/immich/releases?per_page=1 | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -curl -fsSL "https://github.com/immich-app/immich/archive/refs/tags/v${RELEASE}.zip" -o "$tmp_file" -unzip -q "$tmp_file" INSTALL_DIR="/opt/${APPLICATION}" UPLOAD_DIR="${INSTALL_DIR}/upload" SRC_DIR="${INSTALL_DIR}/source" @@ -285,9 +280,12 @@ APP_DIR="${INSTALL_DIR}/app" ML_DIR="${APP_DIR}/machine-learning" GEO_DIR="${INSTALL_DIR}/geodata" mkdir -p "$INSTALL_DIR" -mv "$APPLICATION-$RELEASE"/ "$SRC_DIR" mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${ML_DIR}","${INSTALL_DIR}"/cache} +fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v1.135.0" "$SRC_DIR" + +msg_info "Installing ${APPLICATION} (more patience please)" + cd "$SRC_DIR"/server $STD npm install -g node-gyp node-pre-gyp $STD npm ci @@ -358,7 +356,6 @@ msg_ok "Installed GeoNames data" mkdir -p /var/log/immich touch /var/log/immich/{web.log,ml.log} -echo "$RELEASE" >/opt/"${APPLICATION}"_version.txt msg_ok "Installed ${APPLICATION}" msg_info "Creating user, env file, scripts & services" From cd45ccfb709bafae510c010c9a048dfd8ee43dd7 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 06:48:20 +0100 Subject: [PATCH 113/139] Update CHANGELOG.md (#5310) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dec031cb..e8bf07fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) - #### ✨ New Features From 43a90bf9e62ccf7498961bb884dee1b999e84bcd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 20 Jun 2025 07:57:32 +0200 Subject: [PATCH 114/139] fix planka Tags (#5311) --- ct/planka.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/planka.sh b/ct/planka.sh index 4a8a35a1b..01f817a3b 100644 --- a/ct/planka.sh +++ b/ct/planka.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://github.com/plankanban/planka APP="PLANKA" -var_tags="${var_tags:-Arr}" +var_tags="${var_tags:-Todo,kanban}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" From 3e47e396334199c1ea5a797e82a3932b595571fb Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 06:57:57 +0100 Subject: [PATCH 115/139] Update CHANGELOG.md (#5312) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8bf07fd3..1c345e50d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts + - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) + - #### 🐞 Bug Fixes - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) From e343c7b5aad81dff1cf2b9b2304e15daf5423b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:06:24 +0200 Subject: [PATCH 116/139] Better DB pass (#5313) --- install/planka-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/planka-install.sh b/install/planka-install.sh index b0cd1309c..03f8b984e 100644 --- a/install/planka-install.sh +++ b/install/planka-install.sh @@ -26,7 +26,7 @@ PG_VERSION="16" setup_postgresql msg_info "Setting up PostgreSQL Database" DB_NAME=planka DB_USER=planka -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +DB_PASS=$(openssl rand -base64 16 | tr -d '/+=') $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" From 913378228965c4571ce6b27205dd2e592efc8ffc Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 07:06:50 +0100 Subject: [PATCH 117/139] Update CHANGELOG.md (#5314) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c345e50d..68c97c6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) From 92f631b6287aeed562f91677f2c4481bfd0e0a75 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 11:28:17 +0200 Subject: [PATCH 118/139] migrate Jupyter Notebook to uv-based installation with update support (#5320) --- ct/jupyternotebook.sh | 57 ++++++++++++++++++++++++------ install/jupyternotebook-install.sh | 21 ++++++----- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/ct/jupyternotebook.sh b/ct/jupyternotebook.sh index 2baf5baa8..2b6aef427 100644 --- a/ct/jupyternotebook.sh +++ b/ct/jupyternotebook.sh @@ -20,16 +20,53 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get install -y upgrade - $STD pip3 install jupyter --upgrade - msg_ok "Updated Successfully" - exit + INSTALL_DIR="/opt/jupyter" + VENV_PYTHON="${INSTALL_DIR}/.venv/bin/python" + VENV_JUPYTER="${INSTALL_DIR}/.venv/bin/jupyter" + SERVICE_FILE="/etc/systemd/system/jupyternotebook.service" + + if [[ ! -x "$VENV_JUPYTER" ]]; then + msg_info "Migrating to uv venv" + PYTHON_VERSION="3.12" setup_uv + mkdir -p "$INSTALL_DIR" + cd "$INSTALL_DIR" + $STD uv venv .venv + $STD "$VENV_PYTHON" -m ensurepip --upgrade + $STD "$VENV_PYTHON" -m pip install --upgrade pip + $STD "$VENV_PYTHON" -m pip install jupyter + msg_ok "Migrated to uv and installed Jupyter" + else + msg_info "Updating Jupyter" + $STD "$VENV_PYTHON" -m pip install --upgrade pip + $STD "$VENV_PYTHON" -m pip install --upgrade jupyter + msg_ok "Jupyter updated" + fi + + if [[ -f "$SERVICE_FILE" && "$(grep ExecStart "$SERVICE_FILE")" != *".venv/bin/jupyter"* ]]; then + msg_info "Updating systemd service to use .venv" + cat <"$SERVICE_FILE" +[Unit] +Description=Jupyter Notebook Server +After=network.target +[Service] +Type=simple +WorkingDirectory=${INSTALL_DIR} +ExecStart=${VENV_JUPYTER} notebook --ip=0.0.0.0 --port=8888 --allow-root +Restart=always +RestartSec=10 +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reexec + systemctl restart jupyternotebook + msg_ok "Service updated and restarted" + fi + + exit } start @@ -39,4 +76,4 @@ 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}:8888${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8888${CL}" diff --git a/install/jupyternotebook-install.sh b/install/jupyternotebook-install.sh index 843921187..4b8229d7c 100644 --- a/install/jupyternotebook-install.sh +++ b/install/jupyternotebook-install.sh @@ -13,15 +13,16 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - python3 \ - python3-pip -msg_ok "Installed Dependencies" +PYTHON_VERSION="3.12" setup_uv -msg_info "Setting up Jupyter Notebook" -$STD pip3 install jupyter -msg_ok "Setup Jupyter Notebook" +msg_info "Installing Jupyter" +mkdir -p /opt/jupyter +cd /opt/jupyter +$STD uv venv /opt/jupyter/.venv +$STD /opt/jupyter/.venv/bin/python -m ensurepip --upgrade +$STD /opt/jupyter/.venv/bin/python -m pip install --upgrade pip +$STD /opt/jupyter/.venv/bin/python -m pip install jupyter +msg_ok "Installed Jupyter" msg_info "Creating Service" cat </etc/systemd/system/jupyternotebook.service @@ -31,7 +32,8 @@ After=network.target [Service] Type=simple -ExecStart=jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root +WorkingDirectory=/opt/jupyter +ExecStart=/opt/jupyter/.venv/bin/jupyter notebook --ip=0.0.0.0 --port=8888 --allow-root Restart=always RestartSec=10 @@ -39,6 +41,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now jupyternotebook +msg_ok "Created Service" motd_ssh customize From bcdeeaafb3828d72fade43ebc19fbcb5e21378cf Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 10:28:43 +0100 Subject: [PATCH 119/139] Update CHANGELOG.md (#5323) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68c97c6e6..f34d7f7cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ All LXC instances created using this repository come pre-installed with Midnight - (turnkey) Add OpenLDAP as a TurnKey option [@mhaligowski](https://github.com/mhaligowski) ([#5305](https://github.com/community-scripts/ProxmoxVE/pull/5305)) + - #### 🔧 Refactor + + - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) + ## 2025-06-19 ### 🆕 New Scripts From f81f7fb1c0edb563ebe353cf78d2d265844ce6d3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 11:29:10 +0200 Subject: [PATCH 120/139] Refactor: migrate AdventureLog update to uv and GitHub release logic (#5318) --- ct/adventurelog.sh | 42 ++++++++++++++++++++------------- install/adventurelog-install.sh | 36 +++++++++++++--------------- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/ct/adventurelog.sh b/ct/adventurelog.sh index 966afb2e4..75c873c66 100644 --- a/ct/adventurelog.sh +++ b/ct/adventurelog.sh @@ -27,43 +27,51 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + RELEASE=$(curl -fsSL https://api.github.com/repos/seanmorley15/AdventureLog/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + if [[ "${RELEASE}" != "$(cat ~/.adventurelog 2>/dev/null)" ]] || [[ ! -f ~/.adventurelog ]]; then msg_info "Stopping Services" systemctl stop adventurelog-backend systemctl stop adventurelog-frontend msg_ok "Services Stopped" - msg_info "Updating ${APP} to ${RELEASE}" - mv /opt/adventurelog/ /opt/adventurelog-backup/ - curl -fsSL -o /opt/v${RELEASE}.zip "https://github.com/seanmorley15/AdventureLog/archive/refs/tags/v${RELEASE}.zip" - $STD unzip /opt/v${RELEASE}.zip -d /opt/ - mv /opt/AdventureLog-${RELEASE} /opt/adventurelog + fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" + PYTHON_VERSION="3.12" setup_uv + + msg_info "Updating ${APP} to v${RELEASE}" + # Backend Migration + cp /opt/adventurelog-backup/backend/server/.env /opt/adventurelog/backend/server/.env + cp -r /opt/adventurelog-backup/backend/server/media /opt/adventurelog/backend/server/media - mv /opt/adventurelog-backup/backend/server/.env /opt/adventurelog/backend/server/.env - mv /opt/adventurelog-backup/backend/server/media /opt/adventurelog/backend/server/media cd /opt/adventurelog/backend/server - $STD pip install --upgrade pip - $STD pip install -r requirements.txt - $STD python3 manage.py collectstatic --noinput - $STD python3 manage.py migrate + if [[ ! -x .venv/bin/python ]]; then + $STD uv venv .venv + $STD .venv/bin/python -m ensurepip --upgrade + fi - mv /opt/adventurelog-backup/frontend/.env /opt/adventurelog/frontend/.env + $STD .venv/bin/python -m pip install --upgrade pip + $STD .venv/bin/python -m pip install -r requirements.txt + $STD .venv/bin/python -m manage collectstatic --noinput + $STD .venv/bin/python -m manage migrate + + # Frontend Migration + cp /opt/adventurelog-backup/frontend/.env /opt/adventurelog/frontend/.env cd /opt/adventurelog/frontend - $STD pnpm install - $STD pnpm run build - echo "${RELEASE}" >/opt/${APP}_version.txt + $STD pnpm i + $STD pnpm build msg_ok "Updated ${APP}" msg_info "Starting Services" + systemctl daemon-reexec systemctl start adventurelog-backend systemctl start adventurelog-frontend - msg_ok "Started Services" + msg_ok "Services Started" msg_info "Cleaning Up" rm -rf /opt/v${RELEASE}.zip rm -rf /opt/adventurelog-backup msg_ok "Cleaned" + msg_ok "Updated Successfully" else msg_ok "No update required. ${APP} is already at ${RELEASE}" diff --git a/install/adventurelog-install.sh b/install/adventurelog-install.sh index cdd7f7f07..bf5dbd5c9 100644 --- a/install/adventurelog-install.sh +++ b/install/adventurelog-install.sh @@ -1,8 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck -# Co-Author: MickLesk (Canbiz) +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/seanmorley15/AdventureLog @@ -18,13 +17,12 @@ msg_info "Installing Dependencies" $STD apt-get install -y \ gdal-bin \ libgdal-dev \ - git \ - python3-venv \ - python3-pip + git msg_ok "Installed Dependencies" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs -PG_VERSION="16" PG_MODULES="postgis" setup_postgresql +PYTHON_VERSION="3.12" setup_uv +NODE_VERSION="22" NODE_MODULE="pnpm@latest" install_node_and_modules +PG_VERSION="16" PG_MODULES="postgis" install_postgresql msg_info "Set up PostgreSQL Database" DB_NAME="adventurelog_db" @@ -46,15 +44,12 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/adventurelog.creds msg_ok "Set up PostgreSQL" +fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog" + msg_info "Installing AdventureLog (Patience)" DJANGO_ADMIN_USER="djangoadmin" DJANGO_ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" LOCAL_IP="$(hostname -I | awk '{print $1}')" -cd /opt -RELEASE=$(curl -fsSL https://api.github.com/repos/seanmorley15/AdventureLog/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -curl -fsSL "https://github.com/seanmorley15/AdventureLog/archive/refs/tags/v${RELEASE}.zip" -o "v${RELEASE}.zip" -$STD unzip v${RELEASE}.zip -mv AdventureLog-${RELEASE} /opt/adventurelog cat </opt/adventurelog/backend/server/.env PGHOST='localhost' PGDATABASE='${DB_NAME}' @@ -79,11 +74,13 @@ DISABLE_REGISTRATION=False EOF cd /opt/adventurelog/backend/server mkdir -p /opt/adventurelog/backend/server/media -$STD pip install --upgrade pip -$STD pip install -r requirements.txt -$STD python3 manage.py collectstatic --noinput -$STD python3 manage.py migrate -$STD python3 manage.py download-countries +$STD uv venv /opt/adventurelog/backend/server/.venv +$STD /opt/adventurelog/backend/server/.venv/bin/python -m ensurepip --upgrade +$STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install --upgrade pip +$STD /opt/adventurelog/backend/server/.venv/bin/python -m pip install -r requirements.txt +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage collectstatic --noinput +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage migrate +$STD /opt/adventurelog/backend/server/.venv/bin/python -m manage download-countries cat </opt/adventurelog/frontend/.env PUBLIC_SERVER_URL=http://$LOCAL_IP:8000 BODY_SIZE_LIMIT=Infinity @@ -96,7 +93,8 @@ echo "${RELEASE}" >"/opt/${APPLICATION}_version.txt" msg_ok "Installed AdventureLog" msg_info "Setting up Django Admin" -$STD python3 /opt/adventurelog/backend/server/manage.py shell < Date: Fri, 20 Jun 2025 10:29:44 +0100 Subject: [PATCH 121/139] Update CHANGELOG.md (#5324) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f34d7f7cb..1bbf29be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🔧 Refactor + - Refactor: migrate AdventureLog update to uv and GitHub release logic [@MickLesk](https://github.com/MickLesk) ([#5318](https://github.com/community-scripts/ProxmoxVE/pull/5318)) - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) ## 2025-06-19 From ea5eca83fea34f31ea9b0b8b11cb4ea38531196f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:13:53 +0200 Subject: [PATCH 122/139] Argus: fix wrong port on website (#5322) --- frontend/public/json/argus.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/argus.json b/frontend/public/json/argus.json index 424522c5f..7a0e8cf48 100644 --- a/frontend/public/json/argus.json +++ b/frontend/public/json/argus.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 3000, + "interface_port": 8080, "documentation": "https://release-argus.io/docs/overview/", "website": "https://release-argus.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/argus.webp", From 4e46fd739d685876355143b4dc07888401c64589 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 11:14:28 +0100 Subject: [PATCH 123/139] Update CHANGELOG.md (#5326) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bbf29be0..4e58e9db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,12 @@ All LXC instances created using this repository come pre-installed with Midnight - Refactor: migrate AdventureLog update to uv and GitHub release logic [@MickLesk](https://github.com/MickLesk) ([#5318](https://github.com/community-scripts/ProxmoxVE/pull/5318)) - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) +### 🌐 Website + + - #### 📝 Script Information + + - Argus: fix wrong port on website [@MickLesk](https://github.com/MickLesk) ([#5322](https://github.com/community-scripts/ProxmoxVE/pull/5322)) + ## 2025-06-19 ### 🆕 New Scripts From 1f7d85ac60bccce2f903f4d6a369346178d0e71b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:28:06 +0200 Subject: [PATCH 124/139] [core]: unify misc/*.func scripts with centralized logic from core.func (#5316) * Core Changes * Formatting * add create_lxc patches * Update install.func * remove dev header --- misc/alpine-install.func | 80 ++--------- misc/api.func | 51 +++---- misc/build.func | 297 ++++++++++----------------------------- misc/config-file.func | 245 ++++++++++++++++---------------- misc/core.func | 29 +++- misc/create_lxc.sh | 268 ++++++++++++++++------------------- misc/install.func | 134 ++++-------------- 7 files changed, 404 insertions(+), 700 deletions(-) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 5053519c4..909b51043 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -1,52 +1,13 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - i=$RETRY_NUM - - # Icons - CM="${TAB}✔️${TAB}${CL}" - CROSS="${TAB}✖️${TAB}${CL}" - INFO="${TAB}💡${TAB}${CL}" - NETWORK="${TAB}📡${TAB}${CL}" - OS="${TAB}🖥️${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} +if ! command -v curl >/dev/null 2>&1; then + apk update && apk add curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { @@ -74,33 +35,15 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne " ${TAB}${YW}${msg}" -} - -# This function displays a success message with a green color. -msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - while [ "$i" -gt 0 ]; do + while [ $i -gt 0 ]; do if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY i=$((i - 1)) done @@ -149,10 +92,9 @@ update_os() { # This function modifies the message of the day (motd) and SSH settings motd_ssh() { - # Set terminal to 256-color mode echo "export TERM='xterm-256color'" >>/root/.bashrc IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - # Get OS information + if [ -f "/etc/os-release" ]; then OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') @@ -213,6 +155,6 @@ EOF msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVE/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update } diff --git a/misc/api.func b/misc/api.func index 67f0b6510..5fd550b77 100644 --- a/misc/api.func +++ b/misc/api.func @@ -4,7 +4,7 @@ post_to_api() { - if ! command -v curl &> /dev/null; then + if ! command -v curl &>/dev/null; then return fi @@ -20,7 +20,8 @@ post_to_api() { local pve_version="not found" pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') - JSON_PAYLOAD=$(cat < /dev/null; then - return - fi + if ! command -v curl &>/dev/null; then + return + fi - if [ "$POST_UPDATE_DONE" = true ]; then - return 0 - fi - local API_URL="http://api.community-scripts.org/upload/updatestatus" - local status="${1:-failed}" - local error="${2:-No error message}" + if [ "$POST_UPDATE_DONE" = true ]; then + return 0 + fi + local API_URL="http://api.community-scripts.org/upload/updatestatus" + local status="${1:-failed}" + local error="${2:-No error message}" - JSON_PAYLOAD=$(cat </dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via wget" +fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeuo pipefail @@ -71,7 +35,6 @@ catch_errors() { # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -81,78 +44,6 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with logging support. -declare -A MSG_INFO_SHOWN -SPINNER_ACTIVE=0 -SPINNER_PID="" -SPINNER_MSG="" - -trap 'stop_spinner' EXIT INT TERM HUP - -start_spinner() { - local msg="$1" - local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - local spin_i=0 - local interval=0.1 - - SPINNER_MSG="$msg" - printf "\r\e[2K" >&2 - - { - while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do - printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done - } & - - SPINNER_PID=$! - disown "$SPINNER_PID" -} - -stop_spinner() { - if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then - kill "$SPINNER_PID" 2>/dev/null - sleep 0.1 - kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - fi - SPINNER_ACTIVE=0 - unset SPINNER_PID -} - -spinner_guard() { - if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then - kill "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - SPINNER_ACTIVE=0 - unset SPINNER_PID - fi -} - -msg_info() { - local msg="$1" - [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return - MSG_INFO_SHOWN["$msg"]=1 - - spinner_guard - SPINNER_ACTIVE=1 - start_spinner "$msg" -} - -msg_ok() { - local msg="$1" - stop_spinner - printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 - unset MSG_INFO_SHOWN["$msg"] -} - -msg_error() { - stop_spinner - local msg="$1" - printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 -} - # Check if the shell is using bash shell_check() { if [[ "$(basename "$SHELL")" != "bash" ]]; then @@ -273,42 +164,6 @@ update_motd_ip() { fi } -# Function to download & save header files -get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_name}" - - mkdir -p "$(dirname "$local_header_path")" - - if [ ! -s "$local_header_path" ]; then - if ! curl -fsSL "$header_url" -o "$local_header_path"; then - echo -e "Failed to download header for ${app_name}. No header will be displayed." - return 1 - fi - fi - - cat "$local_header_path" -} - -# This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node. -header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content - - # Download & save Header-File locally - header_content=$(get_header "$app_name") - if [ $? -ne 0 ]; then - # Fallback: Doesn't show Header - return 0 - fi - - # Show ASCII-Header - term_width=$(tput cols 2>/dev/null || echo 120) - clear - echo "$header_content" -} - # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { if [ -n "${SSH_CLIENT:+x}" ]; then @@ -346,8 +201,8 @@ base_settings() { SSH="no" SSH_AUTHORIZED_KEY="" TAGS="community-script;" - ENABLE_FUSE="no" - ENABLE_TUN="no" + ENABLE_FUSE="${1:-no}" + ENABLE_TUN="${1:-no}" # Override default settings with variables from ct script CT_TYPE=${var_unprivileged:-$CT_TYPE} @@ -516,32 +371,40 @@ advanced_settings() { fi done - while true; do + while true; do if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ ! -z "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - fi - else - PW1="Automatic Login" + # Empty = Autologin + if [[ -z "$PW1" ]]; then PW="" + PW1="Automatic Login" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" break fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi else exit_script fi @@ -565,7 +428,7 @@ advanced_settings() { else HN=$(echo "${CT_NAME,,}" | tr -d ' ') fi - + # Hostname validate (RFC 1123) if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" break @@ -824,11 +687,11 @@ advanced_settings() { echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERB="yes" + VERBOSE="yes" else - VERB="no" + VERBOSE="no" fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" @@ -944,18 +807,18 @@ install_script() { 1) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERB="no" + VERBOSE="no" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; 2) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERB="yes" + VERBOSE="yes" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; @@ -1044,18 +907,9 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a New ${APP} LXC. Proceed?" 10 58); then - clear - exit_script - exit - fi - SPINNER_PID="" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script - fi - - if ! command -v pveversion >/dev/null 2>&1; then + else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ 12 60 3 \ @@ -1065,11 +919,11 @@ start() { case "$CHOICE" in 1) - VERB="no" + VERBOSE="no" set_std_mode ;; 2) - VERB="yes" + VERBOSE="yes" set_std_mode ;; 3) @@ -1078,15 +932,13 @@ start() { exit ;; esac - - SPINNER_PID="" update_script fi } # This function collects user settings and integrates all the collected information. build_container() { - # if [ "$VERB" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi if [ "$CT_TYPE" == "1" ]; then FEATURES="keyctl=1,nesting=1" @@ -1119,7 +971,7 @@ build_container() { export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" - export VERBOSE="$VERB" + export VERBOSE="$VERBOSE" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" @@ -1200,20 +1052,34 @@ lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi - # This starts the container and executes -install.sh + # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" msg_ok "Started LXC Container" + msg_info "Customizing LXC Container" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash >/dev/null" - fi - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? + 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" + + # 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://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? } # This function sets the description of the container. @@ -1262,23 +1128,6 @@ EOF post_update_to_api "done" "none" } -set_std_mode() { - if [ "$VERB" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - if [ "$VERB" = "no" ]; then - "$@" >/dev/null 2>&1 || return 1 - else - "$@" || return 1 - fi -} - api_exit_script() { exit_code=$? # Capture the exit status of the last executed command #200 exit codes indicate error in create_lxc.sh diff --git a/misc/config-file.func b/misc/config-file.func index efaa2de82..401cd73cb 100644 --- a/misc/config-file.func +++ b/misc/config-file.func @@ -53,15 +53,15 @@ config_file() { fi else if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${CT_TYPE-}" ]]; then @@ -75,23 +75,22 @@ config_file() { fi echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" else - if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" fi - else - exit_script + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi + else + exit_script + fi fi - if [[ -n "${PW-}" ]]; then if [[ "$PW" == "none" ]]; then PW="" @@ -109,50 +108,50 @@ config_file() { fi else while true; do - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ -n "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + if [[ -n "$PW1" ]]; then + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 + elif [ ${#PW1} -lt 5 ]; then + whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 else - exit_script + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi fi + else + PW1="Automatic Login" + PW="" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break fi else - PW1="Automatic Login" - PW="" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break + exit_script fi - else - exit_script - fi - done + done fi if [[ -n "${HN-}" ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') + exit_script fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi fi if [[ -n "${DISK_SIZE-}" ]]; then @@ -164,19 +163,19 @@ config_file() { fi else if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + if ! [[ $DISK_SIZE =~ $INTEGER ]]; then + echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" + advanced_settings + fi + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + exit_script fi - else - exit_script - fi fi if [[ -n "${CORE_COUNT-}" ]]; then @@ -188,15 +187,15 @@ config_file() { fi else if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${RAM_SIZE-}" ]]; then @@ -208,15 +207,15 @@ config_file() { fi else if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + fi else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + exit_script fi - else - exit_script - fi fi IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) @@ -227,20 +226,20 @@ config_file() { for iface_filepath in ${IFACE_FILEPATH_LIST}; do iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - ( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi - done < "${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" fi done @@ -296,7 +295,7 @@ config_file() { done fi elif [[ "$NET" == *-* ]]; then - IFS="-" read -r ip_start ip_end <<< "$NET" + IFS="-" read -r ip_start ip_end <<<"$NET" if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0" @@ -309,21 +308,21 @@ config_file() { ip_to_int() { local IFS=. - read -r i1 i2 i3 i4 <<< "$1" - echo $(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { local ip=$1 - echo "$(( (ip >> 24) & 0xFF )).$(( (ip >> 16) & 0xFF )).$(( (ip >> 8) & 0xFF )).$(( ip & 0xFF ))" + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } start_int=$(ip_to_int "$ip1") end_int=$(ip_to_int "$ip2") - for ((ip_int=start_int; ip_int<=end_int; ip_int++)); do + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" + msg_info "Checking IP: $ip" if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then NET="$ip/$cidr" msg_ok "Using free IP Address: ${BGN}$NET${CL}" @@ -363,37 +362,37 @@ config_file() { fi else while true; do - NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) - exit_status=$? - if [ $exit_status -eq 0 ]; then - if [ "$NET" = "dhcp" ]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - break - else - if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) + exit_status=$? + if [ $exit_status -eq 0 ]; then + if [ "$NET" = "dhcp" ]; then echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" break else - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + fi fi + else + exit_script fi - else - exit_script - fi done if [ "$NET" != "dhcp" ]; then - while true; do - GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done + while true; do + GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done else GATE="" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" @@ -406,8 +405,8 @@ config_file() { else if [[ -n "${APT_CACHER_IP-}" ]]; then if [[ ! $APT_CACHER_IP == "none" ]]; then - APT_CACHER="yes" - echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" + APT_CACHER="yes" + echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" else APT_CACHER="" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}" @@ -416,7 +415,7 @@ config_file() { if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then APT_CACHER="${APT_CACHER_IP:+yes}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - if [[ -n $APT_CACHER_IP ]]; then + if [[ -n $APT_CACHER_IP ]]; then APT_CACHER_IP="none" fi else @@ -506,7 +505,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" else - exit_script + exit_script fi fi @@ -533,7 +532,7 @@ config_file() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else - exit_script + exit_script fi fi @@ -582,7 +581,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" else - exit_script + exit_script fi fi @@ -646,7 +645,7 @@ config_file() { exit fi else - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then VERBOSE="yes" else VERBOSE="no" diff --git a/misc/core.func b/misc/core.func index cbabf2f03..fffef59a8 100644 --- a/misc/core.func +++ b/misc/core.func @@ -46,15 +46,26 @@ load_functions() { on_error() { local exit_code="$1" local lineno="$2" - msg_error "Script failed at line $lineno with exit code $exit_code" - # Optionally log to your API or file here + + 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() { - # Always called on script exit, success or failure - cleanup_temp_files || true - msg_info "Script exited" + cleanup_spinner || true + [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited" } on_interrupt() { @@ -429,8 +440,14 @@ msg_info() { local msg="$1" [[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 + stop_spinner - start_spinner "$msg" + + if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then + start_spinner "$msg" + else + printf "\r\e[2K%s %b"${TAB}"" "⏳" "${YW}${msg}${CL}" >&2 + fi } msg_ok() { diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 17ee1e516..632e876f2 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -3,32 +3,20 @@ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # Co-Author: MickLesk -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" # if [ "$VERBOSE" == "yes" ]; then set -x; fi -# This function sets color variables for formatting output in the terminal -# Colors -YW=$(echo "\033[33m") -YWB=$(echo "\033[93m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -GN=$(echo "\033[1;92m") - -# Formatting -CL=$(echo "\033[m") -UL=$(echo "\033[4m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -# Icons -CM="${TAB}✔️${TAB}${CL}" -CROSS="${TAB}✖️${TAB}${CL}" -INFO="${TAB}💡${TAB}${CL}" +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via wget" +fi # This sets error handling options and defines the error_handler function to handle errors set -Eeuo pipefail @@ -36,7 +24,6 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR # This function handles errors function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -46,53 +33,6 @@ function error_handler() { exit 200 } -# This function displays a spinner. -function spinner() { - local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -function msg_warn() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${INFO}${YWB}${msg}${CL}" -} - -# This function displays a success message with a green color. -function msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -function msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # 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') @@ -126,40 +66,44 @@ function select_storage() { } ;; esac - # This Queries all storage locations + # Collect storage options 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)}') - local ITEM="Type: $TYPE Free: $FREE " - local OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - MENU+=("$TAG" "$ITEM" "OFF") - done < <(pvesm status -content $CONTENT | awk 'NR>1') + local MSG_MAX_LENGTH=0 - # Select storage location - if [ $((${#MENU[@]} / 3)) -eq 1 ]; then - printf ${MENU[0]} - else - local STORAGE - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for the ${CONTENT_LABEL,,}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${MENU[@]}" 3>&1 1>&2 2>&3) || { - msg_error "Menu aborted." - exit 202 - } - if [ $? -ne 0 ]; then - echo -e "${CROSS}${RD} Menu aborted by user.${CL}" - exit 0 - fi - done - printf "%s" "$STORAGE" + 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") + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + local OPTION_COUNT=$((${#MENU[@]} / 3)) + + # Auto-select if only one option available + if [[ "$OPTION_COUNT" -eq 1 ]]; then + echo "${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 + + echo "$STORAGE" } # Test if required variables are set [[ "${CTID:-}" ]] || { @@ -177,20 +121,6 @@ function select_storage() { exit 205 } -# Check for network connectivity (IPv4 & IPv6) -#function check_network() { -# local CHECK_URLS=("8.8.8.8" "1.1.1.1" "9.9.9.9" "2606:4700:4700::1111" "2001:4860:4860::8888" "2620:fe::fe") -# -# for url in "${CHECK_URLS[@]}"; do -# if ping -c 1 -W 2 "$url" &>/dev/null; then -# return 0 # Success: At least one connection works -# fi -# done -# -# msg_error "No network connection detected. Check your internet connection." -# exit 101 -#} - # Test if ID is in use if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then echo -e "ID '$CTID' is already in use." @@ -207,6 +137,13 @@ 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." +# Check free space on selected container storage +STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') +REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) +if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 +fi # Check Cluster Quorum if in Cluster if [ -f /etc/pve/corosync.conf ]; then msg_info "Checking Proxmox cluster quorum status" @@ -220,44 +157,48 @@ fi # Update LXC template list msg_info "Updating LXC Template List" -#check_network -pveam update >/dev/null -msg_ok "Updated LXC Template List" + +if ! timeout 10 pveam update >/dev/null 2>&1; then + msg_error "Failed to update LXC template list. Please check your Proxmox host's internet connection and DNS resolution." + exit 201 +fi +$STD msg_ok "LXC Template List Updated" # Get LXC template string -TEMPLATE_SEARCH=${PCT_OSTYPE}-${PCT_OSVERSION:-} +TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) -[ ${#TEMPLATES[@]} -gt 0 ] || { - msg_error "Unable to find a template when searching for '$TEMPLATE_SEARCH'." - exit 207 -} -TEMPLATE="${TEMPLATES[-1]}" -TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE)" -# Without NAS/Mount: TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" -# Check if template exists, if corrupt remove and redownload -if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then - msg_warn "Template $TEMPLATE not found in storage or seems to be corrupted. Redownloading." - [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" - # Download with 3 attempts +if [ ${#TEMPLATES[@]} -eq 0 ]; then + msg_error "No matching LXC template found for '${TEMPLATE_SEARCH}'. Make sure your host can reach the Proxmox template repository." + exit 207 +fi + +TEMPLATE="${TEMPLATES[-1]}" +TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" + +# Check if template exists and is valid +if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then + msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading." + + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do msg_info "Attempt $attempt: Downloading LXC template..." - if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then + if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then msg_ok "Template download successful." break fi if [ $attempt -eq 3 ]; then - msg_error "Three failed attempts. Aborting." + msg_error "Failed after 3 attempts. Please check your Proxmox host’s internet access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" exit 208 fi sleep $((attempt * 5)) done fi -msg_ok "LXC Template is ready to use." +msg_ok "LXC Template '$TEMPLATE' is ready to use." # 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 @@ -266,28 +207,55 @@ grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgi PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) [[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") +# Secure creation of the LXC container with lock and template check +lockfile="/tmp/template.${TEMPLATE}.lock" +exec 9>"$lockfile" +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." + msg_error "Container creation failed. Checking if template is corrupted or incomplete." - if ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then - msg_error "Template appears to be corrupted. Removing and re-downloading." + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_error "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then + msg_error "Template appears to be corrupted – re-downloading." rm -f "$TEMPLATE_PATH" - - if ! timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then - msg_error "Failed to re-download template." - exit 208 - fi - - msg_ok "Re-downloaded LXC Template" - - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed after re-downloading template." - exit 200 - fi else - msg_error "Container creation failed, but template is not corrupted." + msg_error "Template is valid, but container creation still failed." exit 209 fi + + # Retry download + for attempt in {1..3}; do + msg_info "Attempt $attempt: Re-downloading template..." + if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then + msg_ok "Template re-download successful." + break + fi + if [ "$attempt" -eq 3 ]; then + msg_error "Three failed attempts. Aborting." + exit 208 + fi + sleep $((attempt * 5)) + done + + sleep 1 # I/O-Sync-Delay + + msg_ok "Re-downloaded LXC Template" + + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then + msg_error "Container creation failed after re-downloading template." + exit 200 + 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." diff --git a/misc/install.func b/misc/install.func index 3aedd94e6..239a4e0c8 100644 --- a/misc/install.func +++ b/misc/install.func @@ -4,53 +4,13 @@ # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - YWB=$(echo "\033[93m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - - # Icons - CM="${TAB}✔️${TAB}${CL}" - CROSS="${TAB}✖️${TAB}${CL}" - INFO="${TAB}💡${TAB}${CL}" - NETWORK="${TAB}📡${TAB}${CL}" - OS="${TAB}🖥️${TAB}${CL}" - OSVERSION="${TAB}🌟${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" - DEFAULT="${TAB}⚙️${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} - +if ! command -v curl >/dev/null 2>&1; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { set_std_mode # Set STD mode based on VERBOSE @@ -70,7 +30,6 @@ catch_errors() { # This function handles errors error_handler() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -85,62 +44,15 @@ error_handler() { fi } -# This function displays a spinner. -spinner() { - local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen - 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} - echo "$tz" >/etc/timezone - ln -sf /usr/share/zoneinfo/"$tz" /etc/localtime for ((i = RETRY_NUM; i > 0; i--)); do if [ "$(hostname -I)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY done if [ "$(hostname -I)" = "" ]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" @@ -150,7 +62,7 @@ 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_ok "Network Connected: ${BL}$(hostname -I)" + msg_custom "${CM}" "${GN}" "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 @@ -178,7 +90,7 @@ network_check() { # If both IPv4 and IPv6 checks fail, prompt the user if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected,would you like to continue anyway? " prompt + read -r -p "No Internet detected, would you like to continue anyway? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" else @@ -187,8 +99,26 @@ network_check() { fi fi - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; 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:" + DNS_FAILED=false + + for HOST in "${GITHUB_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)" + DNS_FAILED=true + else + GITHUB_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GITHUB_STATUS" + else + msg_ok "$GITHUB_STATUS" + fi set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } @@ -213,11 +143,7 @@ EOF rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - msg_info "Installing core dependencies" - $STD apt-get update - $STD apt-get install -y sudo curl mc gnupg2 source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) - msg_ok "Core dependencies installed" } # This function modifies the message of the day (motd) and SSH settings From 03551e23e433b0abbd3cd294c4575d7be4df1c8b Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:28:33 +0100 Subject: [PATCH 125/139] Update CHANGELOG.md (#5327) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e58e9db3..0e03fb8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts - - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) + - [core]: unify misc/*.func scripts with centralized logic from core.func [@MickLesk](https://github.com/MickLesk) ([#5316](https://github.com/community-scripts/ProxmoxVE/pull/5316)) +- fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - #### 🐞 Bug Fixes From a7ed02160bd060eb0327d0382552a9745c595feb Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:31:30 +0100 Subject: [PATCH 126/139] Update CHANGELOG.md (#5328) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e03fb8af..d721c7b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,9 @@ All LXC instances created using this repository come pre-installed with Midnight ### 🚀 Updated Scripts - - [core]: unify misc/*.func scripts with centralized logic from core.func [@MickLesk](https://github.com/MickLesk) ([#5316](https://github.com/community-scripts/ProxmoxVE/pull/5316)) -- fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - - #### 🐞 Bug Fixes + - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) - changedetection: add msedge as Browser dependency [@Niklas04](https://github.com/Niklas04) ([#5301](https://github.com/community-scripts/ProxmoxVE/pull/5301)) @@ -33,6 +31,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🔧 Refactor + - [core]: unify misc/*.func scripts with centralized logic from core.func [@MickLesk](https://github.com/MickLesk) ([#5316](https://github.com/community-scripts/ProxmoxVE/pull/5316)) - Refactor: migrate AdventureLog update to uv and GitHub release logic [@MickLesk](https://github.com/MickLesk) ([#5318](https://github.com/community-scripts/ProxmoxVE/pull/5318)) - Refactor: migrate Jupyter Notebook to uv-based installation with update support [@MickLesk](https://github.com/MickLesk) ([#5320](https://github.com/community-scripts/ProxmoxVE/pull/5320)) From 637f9a1f441b4f976a7f3c124d3b8e08bc61122e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:32:57 +0200 Subject: [PATCH 127/139] quickfix broken format --- misc/build.func | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 40a3b35b5..d1df1ce3f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -371,7 +371,7 @@ advanced_settings() { fi done - while true; do + while true; do if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then # Empty = Autologin if [[ -z "$PW1" ]]; then @@ -428,7 +428,7 @@ advanced_settings() { else HN=$(echo "${CT_NAME,,}" | tr -d ' ') fi - # Hostname validate (RFC 1123) + # Hostname validate (RFC 1123) if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" break @@ -907,7 +907,8 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then install_script else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ @@ -1052,7 +1053,7 @@ lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi - # This starts the container and executes -install.sh + # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" msg_ok "Started LXC Container" @@ -1079,7 +1080,7 @@ EOF' 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://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? } # This function sets the description of the container. From 12a6055ea3139625b536dbe577121bf5dda64b73 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:39:45 +0200 Subject: [PATCH 128/139] set syslinks for jupyter to prevent issues --- install/jupyternotebook-install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install/jupyternotebook-install.sh b/install/jupyternotebook-install.sh index 4b8229d7c..a9a2eca8f 100644 --- a/install/jupyternotebook-install.sh +++ b/install/jupyternotebook-install.sh @@ -22,6 +22,9 @@ $STD uv venv /opt/jupyter/.venv $STD /opt/jupyter/.venv/bin/python -m ensurepip --upgrade $STD /opt/jupyter/.venv/bin/python -m pip install --upgrade pip $STD /opt/jupyter/.venv/bin/python -m pip install jupyter +ln -s /opt/jupyter/.venv/bin/jupyter /usr/local/bin/jupyter +ln -s /opt/jupyter/.venv/bin/jupyter-lab /usr/local/bin/jupyter-lab +ln -s /opt/jupyter/.venv/bin/jupyter-notebook /usr/local/bin/jupyter-notebook msg_ok "Installed Jupyter" msg_info "Creating Service" From 8dfacb9e7df4d084e06a647f095bff5eb3ffd39b Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:07:54 +0200 Subject: [PATCH 129/139] Update versions.json (#5330) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 74 +++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 38f367e22..4ba409322 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,8 +1,38 @@ [ + { + "name": "zabbix/zabbix", + "version": "7.0.15", + "date": "2025-06-20T10:54:31Z" + }, + { + "name": "syncthing/syncthing", + "version": "2.0.0-rc.19", + "date": "2025-06-02T17:56:25Z" + }, + { + "name": "benzino77/tasmocompiler", + "version": "v12.7.0", + "date": "2025-06-20T08:31:16Z" + }, { "name": "plexguide/Huntarr.io", - "version": "8.1.3", - "date": "2025-06-19T23:45:15Z" + "version": "8.1.5", + "date": "2025-06-20T06:50:27Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2032", + "date": "2025-06-20T05:57:27Z" + }, + { + "name": "arunavo4/gitea-mirror", + "version": "v2.16.3", + "date": "2025-06-20T05:49:06Z" + }, + { + "name": "firefly-iii/firefly-iii", + "version": "v6.2.18", + "date": "2025-06-20T04:45:37Z" }, { "name": "go-gitea/gitea", @@ -24,6 +54,11 @@ "version": "v1.135.1", "date": "2025-06-19T17:55:06Z" }, + { + "name": "keycloak/keycloak", + "version": "26.2.5", + "date": "2025-05-28T06:49:43Z" + }, { "name": "rclone/rclone", "version": "v1.70.1", @@ -59,11 +94,6 @@ "version": "8.2-m01-int2", "date": "2025-06-12T08:52:10Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2029", - "date": "2025-06-19T05:56:19Z" - }, { "name": "prometheus-pve/prometheus-pve-exporter", "version": "v3.5.5", @@ -79,11 +109,6 @@ "version": "v0.35.0", "date": "2025-05-21T18:00:32Z" }, - { - "name": "keycloak/keycloak", - "version": "26.2.5", - "date": "2025-05-28T06:49:43Z" - }, { "name": "pterodactyl/panel", "version": "v1.11.11", @@ -114,11 +139,6 @@ "version": "5.0.0.M3", "date": "2025-06-18T14:18:12Z" }, - { - "name": "zabbix/zabbix", - "version": "7.2.8", - "date": "2025-06-18T13:46:00Z" - }, { "name": "Bubka/2FAuth", "version": "v5.6.0", @@ -204,11 +224,6 @@ "version": "4.5.1", "date": "2025-04-11T09:57:47Z" }, - { - "name": "arunavo4/gitea-mirror", - "version": "v2.16.2", - "date": "2025-06-17T11:59:34Z" - }, { "name": "crowdsecurity/crowdsec", "version": "v1.6.9", @@ -289,11 +304,6 @@ "version": "v1.18.3", "date": "2025-06-16T07:03:46Z" }, - { - "name": "firefly-iii/firefly-iii", - "version": "v6.2.17", - "date": "2025-06-11T12:07:38Z" - }, { "name": "jellyfin/jellyfin", "version": "v10.10.7", @@ -319,11 +329,6 @@ "version": "v3.1.9", "date": "2025-03-01T02:24:33Z" }, - { - "name": "syncthing/syncthing", - "version": "2.0.0-rc.19", - "date": "2025-06-02T17:56:25Z" - }, { "name": "Prowlarr/Prowlarr", "version": "v1.37.0.5076", @@ -989,11 +994,6 @@ "version": "v2.10.0", "date": "2025-04-18T20:46:28Z" }, - { - "name": "benzino77/tasmocompiler", - "version": "v12.6.1", - "date": "2025-04-17T17:35:02Z" - }, { "name": "IceWhaleTech/CasaOS", "version": "v0.4.15", From 074d6fa31b5181af98f1358f01ee67268a838331 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:10:50 +0200 Subject: [PATCH 130/139] quickfix: broken formatting in verbose spinner --- misc/core.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index fffef59a8..1a904f9ab 100644 --- a/misc/core.func +++ b/misc/core.func @@ -195,6 +195,7 @@ icons() { CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" FUSE="${TAB}🗂️${TAB}${CL}" + HOURGLASS="${TAB}⏳${TAB}" } # ------------------------------------------------------------------------------ @@ -446,7 +447,7 @@ msg_info() { if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then start_spinner "$msg" else - printf "\r\e[2K%s %b"${TAB}"" "⏳" "${YW}${msg}${CL}" >&2 + printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2 fi } From 25f6245d313e819ac46022e314eba6a1a1fc2f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 20 Jun 2025 21:47:07 +0200 Subject: [PATCH 131/139] Update huntarr.sh (#5336) --- ct/huntarr.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/huntarr.sh b/ct/huntarr.sh index 0a3023f38..7e290f897 100644 --- a/ct/huntarr.sh +++ b/ct/huntarr.sh @@ -40,10 +40,10 @@ function update_script() { msg_ok "Stopped huntarr service" fetch_and_deploy_gh_release "huntarr" "plexguide/Huntarr.io" - msg_info "Updating $APP to v${RELEASE}" + msg_info "Configuring $APP" cd /opt/huntarr $STD uv pip install -r requirements.txt --python /opt/huntarr/.venv/bin/python - msg_ok "Updated $APP to v${RELEASE}" + msg_ok "Configured $APP" msg_info "Starting $APP" systemctl start huntarr From ce9c1e63a84e7b000b19bb8335ddde51a4e65c77 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 20:47:43 +0100 Subject: [PATCH 132/139] Update CHANGELOG.md (#5338) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d721c7b50..1390eb336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Huntarr: Fix duplicate update status messages [@tremor021](https://github.com/tremor021) ([#5336](https://github.com/community-scripts/ProxmoxVE/pull/5336)) - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) - Immich: Hotfix for #5299 [@vhsdream](https://github.com/vhsdream) ([#5300](https://github.com/community-scripts/ProxmoxVE/pull/5300)) From 74a073e8bd14f5c76ed2aa19c919225767071d53 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 23:22:49 +0200 Subject: [PATCH 133/139] Immich: remove unneeded tmp_file (#5332) * remove temp cleanup from immich * Update immich-install.sh * vchord --- ct/immich.sh | 2 +- install/immich-install.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/immich.sh b/ct/immich.sh index 3622199f2..27e442aef 100644 --- a/ct/immich.sh +++ b/ct/immich.sh @@ -208,7 +208,7 @@ function update_script() { $STD apt install -y ./vchord.deb $STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;" systemctl restart postgresql - if [[ ! -f ~/.vchord-version ]] || [[ ! "$(cat ~/.vchord_version)" > "0.3.0" ]]; then + if [[ ! -f ~/.vchord_version ]] || [[ ! "$(cat ~/.vchord_version)" > "0.3.0" ]]; then $STD sudo -u postgres psql -d immich -c "REINDEX DATABASE;" fi echo "$VCHORD_RELEASE" >~/.vchord_version diff --git a/install/immich-install.sh b/install/immich-install.sh index b8cfce932..aa0490b9b 100644 --- a/install/immich-install.sh +++ b/install/immich-install.sh @@ -447,7 +447,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -f "$tmp_file" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From b35eadfc36e3b72954da38af6b78b705bb88cbf8 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 22:23:24 +0100 Subject: [PATCH 134/139] Update CHANGELOG.md (#5340) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1390eb336..fda438657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All LXC instances created using this repository come pre-installed with Midnight - #### 🐞 Bug Fixes + - Immich: remove unneeded tmp_file [@MickLesk](https://github.com/MickLesk) ([#5332](https://github.com/community-scripts/ProxmoxVE/pull/5332)) - Huntarr: Fix duplicate update status messages [@tremor021](https://github.com/tremor021) ([#5336](https://github.com/community-scripts/ProxmoxVE/pull/5336)) - fix planka Tags [@CrazyWolf13](https://github.com/CrazyWolf13) ([#5311](https://github.com/community-scripts/ProxmoxVE/pull/5311)) - PLANKA: Better DB password generate [@tremor021](https://github.com/tremor021) ([#5313](https://github.com/community-scripts/ProxmoxVE/pull/5313)) From e33cf652a1ea46153a655b44d72218eb7a776d0e Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 02:14:57 +0200 Subject: [PATCH 135/139] Update versions.json (#5344) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 148 ++++++++++++++--------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 4ba409322..817ed102d 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,8 +1,78 @@ [ + { + "name": "fallenbagel/jellyseerr", + "version": "preview-dns-cache-manager", + "date": "2025-06-20T23:03:23Z" + }, + { + "name": "go-gitea/gitea", + "version": "v1.24.2", + "date": "2025-06-20T20:37:55Z" + }, + { + "name": "coder/code-server", + "version": "v4.101.0", + "date": "2025-06-20T20:21:50Z" + }, + { + "name": "immich-app/immich", + "version": "v1.135.3", + "date": "2025-06-20T20:19:20Z" + }, + { + "name": "apache/tika", + "version": "3.2.1-rc1", + "date": "2025-06-20T19:41:10Z" + }, + { + "name": "VictoriaMetrics/VictoriaMetrics", + "version": "v1.24.0-victorialogs", + "date": "2025-06-20T19:31:31Z" + }, + { + "name": "homarr-labs/homarr", + "version": "v1.25.0", + "date": "2025-06-20T19:15:43Z" + }, + { + "name": "msgbyte/tianji", + "version": "v1.22.1", + "date": "2025-06-20T18:12:20Z" + }, + { + "name": "mongodb/mongo", + "version": "r8.1.2-rc0", + "date": "2025-06-20T17:35:38Z" + }, + { + "name": "nzbgetcom/nzbget", + "version": "v25.0", + "date": "2025-05-12T09:12:04Z" + }, + { + "name": "Sonarr/Sonarr", + "version": "v4.0.15.2941", + "date": "2025-06-20T17:20:54Z" + }, + { + "name": "bunkerity/bunkerweb", + "version": "testing", + "date": "2025-06-16T18:10:42Z" + }, + { + "name": "plexguide/Huntarr.io", + "version": "8.1.6", + "date": "2025-06-20T14:50:59Z" + }, + { + "name": "docker/compose", + "version": "v2.37.2", + "date": "2025-06-20T13:25:03Z" + }, { "name": "zabbix/zabbix", - "version": "7.0.15", - "date": "2025-06-20T10:54:31Z" + "version": "7.2.9", + "date": "2025-06-20T10:58:45Z" }, { "name": "syncthing/syncthing", @@ -14,11 +84,6 @@ "version": "v12.7.0", "date": "2025-06-20T08:31:16Z" }, - { - "name": "plexguide/Huntarr.io", - "version": "8.1.5", - "date": "2025-06-20T06:50:27Z" - }, { "name": "Jackett/Jackett", "version": "v0.22.2032", @@ -34,11 +99,6 @@ "version": "v6.2.18", "date": "2025-06-20T04:45:37Z" }, - { - "name": "go-gitea/gitea", - "version": "v1.24.1", - "date": "2025-06-19T19:38:29Z" - }, { "name": "paperless-ngx/paperless-ngx", "version": "v2.17.1", @@ -49,11 +109,6 @@ "version": "v1.4.0", "date": "2025-06-19T18:30:11Z" }, - { - "name": "immich-app/immich", - "version": "v1.135.1", - "date": "2025-06-19T17:55:06Z" - }, { "name": "keycloak/keycloak", "version": "26.2.5", @@ -64,11 +119,6 @@ "version": "v1.70.1", "date": "2025-06-19T13:19:02Z" }, - { - "name": "fallenbagel/jellyseerr", - "version": "preview-dns-cache-manager", - "date": "2025-06-19T12:05:10Z" - }, { "name": "icereed/paperless-gpt", "version": "v0.21.0", @@ -114,11 +164,6 @@ "version": "v1.11.11", "date": "2025-06-18T18:04:50Z" }, - { - "name": "msgbyte/tianji", - "version": "v1.22.0", - "date": "2025-06-18T16:21:38Z" - }, { "name": "ollama/ollama", "version": "v0.9.2", @@ -189,11 +234,6 @@ "version": "jenkins-2.515", "date": "2025-06-17T19:17:56Z" }, - { - "name": "coder/code-server", - "version": "v4.100.3", - "date": "2025-06-03T21:06:41Z" - }, { "name": "HabitRPG/habitica", "version": "v5.36.6", @@ -254,11 +294,6 @@ "version": "2.36.1", "date": "2025-06-16T19:20:54Z" }, - { - "name": "bunkerity/bunkerweb", - "version": "testing", - "date": "2025-06-16T18:10:42Z" - }, { "name": "goauthentik/authentik", "version": "version/2025.6.2", @@ -266,8 +301,8 @@ }, { "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" + "version": "nightly", + "date": "2025-06-16T17:35:17Z" }, { "name": "emqx/emqx", @@ -364,11 +399,6 @@ "version": "v2.25.1", "date": "2025-06-14T23:32:15Z" }, - { - "name": "nzbgetcom/nzbget", - "version": "v25.0", - "date": "2025-05-12T09:12:04Z" - }, { "name": "theonedev/onedev", "version": "v11.11.0", @@ -389,11 +419,6 @@ "version": "2025.6.1", "date": "2025-06-13T20:16:18Z" }, - { - "name": "homarr-labs/homarr", - "version": "v1.24.0", - "date": "2025-06-13T19:15:36Z" - }, { "name": "Luligu/matterbridge", "version": "3.0.6", @@ -424,11 +449,6 @@ "version": "v2.3.0p34", "date": "2025-06-12T12:15:44Z" }, - { - "name": "docker/compose", - "version": "v2.37.1", - "date": "2025-06-12T09:00:21Z" - }, { "name": "zitadel/zitadel", "version": "v3.3.0", @@ -484,21 +504,11 @@ "version": "1.11.2", "date": "2025-06-10T11:07:14Z" }, - { - "name": "VictoriaMetrics/VictoriaMetrics", - "version": "v1.110.11", - "date": "2025-06-10T10:00:25Z" - }, { "name": "glanceapp/glance", "version": "v0.8.4", "date": "2025-06-10T07:57:14Z" }, - { - "name": "Sonarr/Sonarr", - "version": "v4.0.14.2939", - "date": "2025-03-17T19:12:37Z" - }, { "name": "tailscale/tailscale", "version": "v1.84.2", @@ -699,11 +709,6 @@ "version": "cassandra-4.0.18", "date": "2025-05-28T21:45:55Z" }, - { - "name": "mongodb/mongo", - "version": "r6.0.24", - "date": "2025-05-28T21:25:03Z" - }, { "name": "Athou/commafeed", "version": "5.10.0", @@ -769,11 +774,6 @@ "version": "0.5", "date": "2025-05-21T20:19:14Z" }, - { - "name": "apache/tika", - "version": "3.2.0-rc2", - "date": "2025-05-21T20:09:07Z" - }, { "name": "Stirling-Tools/Stirling-PDF", "version": "v0.46.2", From a8deff54d8b31ab3e00ecbf95678b26aa054dc54 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 01:15:29 +0100 Subject: [PATCH 136/139] Update CHANGELOG.md (#5345) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda438657..bd818e127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-21 + ## 2025-06-20 ### 🚀 Updated Scripts From 6cc090b69381481cb21b0576a12b618faa54992d Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 14:07:08 +0200 Subject: [PATCH 137/139] Update versions.json (#5351) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 74 +++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 817ed102d..468f3c1e6 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,34 @@ [ + { + "name": "Luligu/matterbridge", + "version": "3.0.7", + "date": "2025-06-21T09:24:21Z" + }, + { + "name": "theonedev/onedev", + "version": "v11.11.1", + "date": "2025-06-21T09:23:39Z" + }, + { + "name": "pocketbase/pocketbase", + "version": "v0.28.4", + "date": "2025-06-21T08:29:04Z" + }, + { + "name": "dgtlmoon/changedetection.io", + "version": "0.50.4", + "date": "2025-06-21T07:47:02Z" + }, + { + "name": "Jackett/Jackett", + "version": "v0.22.2035", + "date": "2025-06-21T05:52:31Z" + }, + { + "name": "coder/code-server", + "version": "v4.101.1", + "date": "2025-06-21T02:47:08Z" + }, { "name": "fallenbagel/jellyseerr", "version": "preview-dns-cache-manager", @@ -9,11 +39,6 @@ "version": "v1.24.2", "date": "2025-06-20T20:37:55Z" }, - { - "name": "coder/code-server", - "version": "v4.101.0", - "date": "2025-06-20T20:21:50Z" - }, { "name": "immich-app/immich", "version": "v1.135.3", @@ -54,6 +79,11 @@ "version": "v4.0.15.2941", "date": "2025-06-20T17:20:54Z" }, + { + "name": "keycloak/keycloak", + "version": "26.2.5", + "date": "2025-05-28T06:49:43Z" + }, { "name": "bunkerity/bunkerweb", "version": "testing", @@ -84,11 +114,6 @@ "version": "v12.7.0", "date": "2025-06-20T08:31:16Z" }, - { - "name": "Jackett/Jackett", - "version": "v0.22.2032", - "date": "2025-06-20T05:57:27Z" - }, { "name": "arunavo4/gitea-mirror", "version": "v2.16.3", @@ -109,11 +134,6 @@ "version": "v1.4.0", "date": "2025-06-19T18:30:11Z" }, - { - "name": "keycloak/keycloak", - "version": "26.2.5", - "date": "2025-05-28T06:49:43Z" - }, { "name": "rclone/rclone", "version": "v1.70.1", @@ -301,8 +321,8 @@ }, { "name": "runtipi/runtipi", - "version": "nightly", - "date": "2025-06-16T17:35:17Z" + "version": "v4.2.1", + "date": "2025-06-03T20:04:28Z" }, { "name": "emqx/emqx", @@ -399,11 +419,6 @@ "version": "v2.25.1", "date": "2025-06-14T23:32:15Z" }, - { - "name": "theonedev/onedev", - "version": "v11.11.0", - "date": "2025-06-14T13:23:36Z" - }, { "name": "semaphoreui/semaphore", "version": "v2.15.0", @@ -419,11 +434,6 @@ "version": "2025.6.1", "date": "2025-06-13T20:16:18Z" }, - { - "name": "Luligu/matterbridge", - "version": "3.0.6", - "date": "2025-06-13T15:02:37Z" - }, { "name": "wazuh/wazuh", "version": "coverity-w25-4.13.0", @@ -469,11 +479,6 @@ "version": "4.8.11.0", "date": "2025-03-10T06:39:11Z" }, - { - "name": "dgtlmoon/changedetection.io", - "version": "0.50.3", - "date": "2025-06-11T15:19:52Z" - }, { "name": "autobrr/autobrr", "version": "v1.63.1", @@ -514,11 +519,6 @@ "version": "v1.84.2", "date": "2025-06-09T23:43:27Z" }, - { - "name": "pocketbase/pocketbase", - "version": "v0.28.3", - "date": "2025-06-09T18:11:46Z" - }, { "name": "Brandawg93/PeaNUT", "version": "v5.8.0", From 3d99d746856ff4010d75a58a8ad51223eb0119ca Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:16:31 +0200 Subject: [PATCH 138/139] Update versions.json (#5359) Co-authored-by: GitHub Actions[bot] --- frontend/public/json/versions.json | 54 +++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/frontend/public/json/versions.json b/frontend/public/json/versions.json index 468f3c1e6..4a721f2bd 100644 --- a/frontend/public/json/versions.json +++ b/frontend/public/json/versions.json @@ -1,4 +1,29 @@ [ + { + "name": "plexguide/Huntarr.io", + "version": "8.1.7", + "date": "2025-06-21T23:25:45Z" + }, + { + "name": "HabitRPG/habitica", + "version": "v5.37.0", + "date": "2025-06-21T14:05:12Z" + }, + { + "name": "fallenbagel/jellyseerr", + "version": "preview-fix-proxy-auth", + "date": "2025-06-21T13:43:58Z" + }, + { + "name": "rcourtman/Pulse", + "version": "v3.30.0", + "date": "2025-06-17T16:00:01Z" + }, + { + "name": "rogerfar/rdt-client", + "version": "v2.0.114", + "date": "2025-06-21T11:20:21Z" + }, { "name": "Luligu/matterbridge", "version": "3.0.7", @@ -29,11 +54,6 @@ "version": "v4.101.1", "date": "2025-06-21T02:47:08Z" }, - { - "name": "fallenbagel/jellyseerr", - "version": "preview-dns-cache-manager", - "date": "2025-06-20T23:03:23Z" - }, { "name": "go-gitea/gitea", "version": "v1.24.2", @@ -89,11 +109,6 @@ "version": "testing", "date": "2025-06-16T18:10:42Z" }, - { - "name": "plexguide/Huntarr.io", - "version": "8.1.6", - "date": "2025-06-20T14:50:59Z" - }, { "name": "docker/compose", "version": "v2.37.2", @@ -239,11 +254,6 @@ "version": "v6.12.7", "date": "2025-06-18T03:44:24Z" }, - { - "name": "rcourtman/Pulse", - "version": "v3.30.0", - "date": "2025-06-17T16:00:01Z" - }, { "name": "grafana/grafana", "version": "v11.5.6", @@ -254,11 +264,6 @@ "version": "jenkins-2.515", "date": "2025-06-17T19:17:56Z" }, - { - "name": "HabitRPG/habitica", - "version": "v5.36.6", - "date": "2025-06-17T18:12:31Z" - }, { "name": "project-zot/zot", "version": "v2.1.5", @@ -321,8 +326,8 @@ }, { "name": "runtipi/runtipi", - "version": "v4.2.1", - "date": "2025-06-03T20:04:28Z" + "version": "nightly", + "date": "2025-06-16T17:35:17Z" }, { "name": "emqx/emqx", @@ -759,11 +764,6 @@ "version": "RELEASE.2025-05-24T17-08-30Z", "date": "2025-05-24T21:42:19Z" }, - { - "name": "rogerfar/rdt-client", - "version": "v2.0.113", - "date": "2025-05-23T01:47:35Z" - }, { "name": "0xERR0R/blocky", "version": "v0.26.2", From 70a1cfbe286734fd2957c830bc7f261214f9f144 Mon Sep 17 00:00:00 2001 From: "community-scripts-pr-app[bot]" <189241966+community-scripts-pr-app[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:17:03 +0100 Subject: [PATCH 139/139] Update CHANGELOG.md (#5360) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd818e127..aadf3f0dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit All LXC instances created using this repository come pre-installed with Midnight Commander, which is a command-line tool (`mc`) that offers a user-friendly file and directory management interface for the terminal environment. +## 2025-06-22 + ## 2025-06-21 ## 2025-06-20