Compare commits

..

38 Commits

Author SHA1 Message Date
github-actions[bot] d3b02777dc Update CHANGELOG.md 2026-07-20 18:32:53 +00:00
CanbiZ (MickLesk) 6f04a9787f core-refactor: single-reporter telemetry rewrite (#15933)
Overhaul the telemetry system to enforce a single-reporter model where only the HOST sends terminal statuses (success/failed/aborted) to the API. Containers now write local artifact files (.failed flag + .errinfo) that the host picks up after lxc-attach returns, preventing the race condition where a metadata-less container payload would win over the full host payload.

Key changes:
- Add TELEMETRY_CONTEXT=container export in install.func/alpine-install.func to mark container context before error handling starts
- Introduce _is_container_context() heuristic and _container_write_failure() artifact writer in error_handler.func
- Refactor api.func: unified _tm_payload() builder (full metadata on every send), _tm_send() curl wrapper, _tm_enabled() gate, telemetry_collect_sysinfo() cached collector
- Add structured .errinfo capture in silent() (core.func) using byte-offset to extract exactly the failing command's output
- Pull .errinfo from container in build.func after lxc-attach for precise error traces
- Add categorize_error(), telemetry_new_attempt(), detect_arm(), REPO_SLUG tracking
- Signal exits (129/130/143) now report as 'aborted' instead of 'failed'
- Remove post_update_to_api_extended() (superseded by unified _tm_payload)
- Export REPO_SOURCE, REPO_SLUG, TELEMETRY_PLATFORM into container environment
2026-07-20 20:32:36 +02:00
community-scripts-pr-app[bot] b889e8d05e Update CHANGELOG.md (#15935)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 18:23:30 +00:00
MickLesk bd834f690a Fix CLEAN_INSTALL to wipe dotfiles too
CLEAN_INSTALL now removes dotfiles (e.g. .env) along with other files, using `find -mindepth 1 -delete` instead of `rm -rf *`. Update scripts for affine, nametag, and postiz are updated to explicitly back up and restore .env (via create_backup/restore_backup or cp) BEFORE the build step that sources it, rather than relying on the wipe preserving dotfiles.
2026-07-20 20:23:05 +02:00
MickLesk a5a0b90643 hotfix: preserve .env 2026-07-20 19:56:29 +02:00
community-scripts-pr-app[bot] aa8352bfd5 Update CHANGELOG.md (#15932)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 15:57:41 +00:00
CanbiZ (MickLesk) 3b8f57672e AFFiNE: Bump version to v0.27.2 (#15930) 2026-07-20 17:57:16 +02:00
community-scripts-pr-app[bot] 9028029ea0 Update CHANGELOG.md (#15931)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 15:21:26 +00:00
Tobias 0d3ee851d0 fix: vikunja: asset selection (#15929)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 01:20:54 +10:00
community-scripts-pr-app[bot] 6408653564 Update CHANGELOG.md (#15928)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 12:39:57 +00:00
CanbiZ (MickLesk) e925e29966 RomM: use backup helpers in update / clear folder (#15915)
* RomM: use backup helpers in update / clear folder

Replace manual .env backup/restore with create_backup and restore_backup helpers, and add CLEAN_INSTALL=1 flag to fetch_and_deploy_gh_release.

* add screenscraper env

* add rom_patcher

* add ruffle and emulatorJS
2026-07-20 14:39:29 +02:00
MickLesk 8505d28429 Set explicit Elasticsearch paths
Define `path.data` and `path.logs` in the Zammad install script's Elasticsearch config so the service uses the expected data and log directories when running in the single-node local setup.
2026-07-20 12:15:57 +02:00
community-scripts-pr-app[bot] f15e9c320b Update CHANGELOG.md (#15926)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:58:10 +00:00
CanbiZ (MickLesk) 5b4e526329 tools.func: add support for extracting 7z archives (#15919) 2026-07-20 11:57:41 +02:00
community-scripts-pr-app[bot] a61dc9c585 Update CHANGELOG.md (#15925)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:55:26 +00:00
CanbiZ (MickLesk) f8cc69e244 Gotify: Migration to v3 (#15912) 2026-07-20 11:55:01 +02:00
community-scripts-pr-app[bot] 1dba4eb2ee Update CHANGELOG.md (#15924)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:54:29 +00:00
CanbiZ (MickLesk) fa9377cd45 Zammad : bind Elasticsearch to 127.0.0.1 (#15909) 2026-07-20 11:54:04 +02:00
community-scripts-pr-app[bot] b6a8ca1a3a Update CHANGELOG.md (#15923)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:53:51 +00:00
CanbiZ (MickLesk) 2c13cb6a38 Meilisearch : use dumpless Meilisearch upgrades (#15921) 2026-07-20 11:53:27 +02:00
community-scripts-pr-app[bot] beeb497519 Update CHANGELOG.md (#15922)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:46:56 +00:00
CanbiZ (MickLesk) 9ea82fa992 core: Improve GPU detection and mapping logic (#15918)
Refactor GPU detection logic to map DRI nodes to their owning GPUs based on vendor IDs. Update messages for detected Intel and AMD GPUs.
2026-07-20 11:46:33 +02:00
community-scripts-pr-app[bot] d6cd545e98 Update CHANGELOG.md (#15920)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 09:43:57 +00:00
CanbiZ (MickLesk) 31b7024997 Fix Omada package version extraction (#15908)
Update both `ct/omada.sh` and `install/omada-install.sh` to parse the Omada version directly from the `_v..._linux` segment of the Debian filename. This removes the old dependency on a timestamp suffix format and ensures `.omada` gets a valid version when TP-Link package naming varies.
2026-07-20 11:43:34 +02:00
community-scripts-pr-app[bot] a6a608614f Update CHANGELOG.md (#15916)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-20 08:29:28 +00:00
Michel Roegl-Brunner 3042cd5cee fix(wanderer): use PocketBase-relative plugin symlink in unprivileged LXC (#15911)
The /data/plugins symlink fails in default unprivileged containers with Operation not permitted. Link plugins via /opt/wanderer/source/db/data/plugins instead so PluginDir() can discover them without root-level symlinks.

Fixes #15799
2026-07-20 10:29:05 +02:00
community-scripts-pr-app[bot] 026fd25e7a Update CHANGELOG.md (#15904)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 17:02:04 +00:00
TowyTowy e1ce55b547 fix(build.func): expand glob in SSH key "Scan Folder/Glob" so it can find keys (#15873)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Sam Heinz <sam@samheinz.com>
2026-07-19 19:01:41 +02:00
community-scripts-pr-app[bot] d81c9f3ed2 Update CHANGELOG.md (#15900)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 12:54:11 +00:00
CanbiZ (MickLesk) b098501a37 tools.func: centralize deploy tail + trap-based tmpdir cleanup (#15872)
* tools.func: replace rm -rf with find for safer directory cleanup

* tools.func: replace rm -rf with find for safer directory cleanup

* refactor(tools.func): centralize deploy tail + trap-based tmpdir cleanup

Extract the duplicated post-download logic of the fetch_and_deploy_*
helpers into two shared functions and switch per-branch cleanup to a
single RETURN trap.

- _deploy_source_tarball: shared source-tarball tail (6 call sites)
- _deploy_unpacked_archive: shared prebuild-archive tail (3 call sites)
- RETURN trap per function guarantees tmpdir/unpack_tmp cleanup on every
  return path, replacing ~40 manual `rm -rf "$tmpdir"` lines
- CLEAN_INSTALL now lives in the helpers instead of 12 copies

Behavior-preserving except: codeberg prebuild gains .txz support and
uses helper return codes; from_url resets shopt on error paths.

* Add _download_source_tarball with retry/validation

Introduce `_download_source_tarball` helper that validates gzip integrity after download and retries up to 3 times. This guards against truncated-but-valid-HTTP responses from GitHub/GitLab/Codeberg on-the-fly archive generation. Replace ad-hoc curl/curl_download calls in fetch_and_deploy_* functions with the new helper. Also remove redundant tmpdir cleanup before early returns (tmpdir is cleaned up at function exit).

* Fix Manyfold Ruby setup home initialization

Create the `manyfold` service user before calling `setup_ruby` so `/home/manyfold` exists when rbenv profile snippets are written. Also harden `setup_ruby` by creating `$HOME` if missing, preventing profile-write failures for installers that pass a home directory before creating the user.

* Update install/manyfold-install.sh

---------

Co-authored-by: Sam Heinz <sam@samheinz.com>
2026-07-19 22:53:46 +10:00
community-scripts-pr-app[bot] a1058256be Update CHANGELOG.md (#15899)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 12:53:02 +00:00
tdn131 e425d9c02c Update OPNsense from 26.1 to 26.7 (#15895) 2026-07-19 22:52:42 +10:00
community-scripts-pr-app[bot] 0dbea508d3 Update CHANGELOG.md (#15898)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 12:03:22 +00:00
Sam Heinz 787e27a4ac change trek repo to liketrek/TREK (#15893) 2026-07-19 14:02:56 +02:00
community-scripts-pr-app[bot] 8f0083850e Update CHANGELOG.md (#15896)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 11:21:43 +00:00
thieneret 8f9c4eb13b update authentik to 2026.5.5 (#15855) 2026-07-19 13:21:14 +02:00
community-scripts-pr-app[bot] beacf9e43a Update CHANGELOG.md (#15892)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-19 09:17:11 +00:00
Chris b2898debce [FIX] BookOrbit: add missing restore_backup during update (#15881)
- Otherwise the `.env` file is not restored and the sed command fails
2026-07-19 19:16:44 +10:00
31 changed files with 1264 additions and 1712 deletions
+49
View File
@@ -505,12 +505,61 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-07-20
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- RomM: use backup helpers in update / clear folder [@MickLesk](https://github.com/MickLesk) ([#15915](https://github.com/community-scripts/ProxmoxVE/pull/15915))
- fix: vikunja: asset selection [@CrazyWolf13](https://github.com/CrazyWolf13) ([#15929](https://github.com/community-scripts/ProxmoxVE/pull/15929))
- Zammad : bind Elasticsearch to 127.0.0.1 [@MickLesk](https://github.com/MickLesk) ([#15909](https://github.com/community-scripts/ProxmoxVE/pull/15909))
- Omada: fix package version extraction [@MickLesk](https://github.com/MickLesk) ([#15908](https://github.com/community-scripts/ProxmoxVE/pull/15908))
- fix(wanderer): use PocketBase-relative plugin symlink in unprivileged LXC [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#15911](https://github.com/community-scripts/ProxmoxVE/pull/15911))
- #### ✨ New Features
- AFFiNE: Bump version to v0.27.2 [@MickLesk](https://github.com/MickLesk) ([#15930](https://github.com/community-scripts/ProxmoxVE/pull/15930))
- #### 💥 Breaking Changes
- Gotify: Migration to v3 [@MickLesk](https://github.com/MickLesk) ([#15912](https://github.com/community-scripts/ProxmoxVE/pull/15912))
### 💾 Core
- #### ✨ New Features
- core: refactor to single-reporter telemetry and better error_handling [@MickLesk](https://github.com/MickLesk) ([#15933](https://github.com/community-scripts/ProxmoxVE/pull/15933))
- tools.func: add support for extracting 7z archives [@MickLesk](https://github.com/MickLesk) ([#15919](https://github.com/community-scripts/ProxmoxVE/pull/15919))
- Meilisearch : use dumpless Meilisearch upgrades [@MickLesk](https://github.com/MickLesk) ([#15921](https://github.com/community-scripts/ProxmoxVE/pull/15921))
- #### 🔧 Refactor
- core: Improve GPU detection and mapping logic [@MickLesk](https://github.com/MickLesk) ([#15918](https://github.com/community-scripts/ProxmoxVE/pull/15918))
## 2026-07-19
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- change trek repo to liketrek/TREK [@asylumexp](https://github.com/asylumexp) ([#15893](https://github.com/community-scripts/ProxmoxVE/pull/15893))
- update authentik to 2026.5.5 [@thieneret](https://github.com/thieneret) ([#15855](https://github.com/community-scripts/ProxmoxVE/pull/15855))
- [FIX] BookOrbit: add missing `restore_backup` during update [@vhsdream](https://github.com/vhsdream) ([#15881](https://github.com/community-scripts/ProxmoxVE/pull/15881))
- #### ✨ New Features
- tools.func: centralize deploy tail + trap-based tmpdir cleanup [@MickLesk](https://github.com/MickLesk) ([#15872](https://github.com/community-scripts/ProxmoxVE/pull/15872))
- Update OPNsense from 26.1 to 26.7 [@tdn131](https://github.com/tdn131) ([#15895](https://github.com/community-scripts/ProxmoxVE/pull/15895))
### 💾 Core
- Revert "core: add configurable host CA inheritance during bootstrap" [@MickLesk](https://github.com/MickLesk) ([#15886](https://github.com/community-scripts/ProxmoxVE/pull/15886))
- #### 🐞 Bug Fixes
- fix(build.func): expand glob in SSH key "Scan Folder/Glob" so it can find keys [@TowyTowy](https://github.com/TowyTowy) ([#15873](https://github.com/community-scripts/ProxmoxVE/pull/15873))
## 2026-07-18
### 💾 Core
+11 -4
View File
@@ -30,7 +30,7 @@ function update_script() {
exit
fi
RELEASE="v0.27.0"
RELEASE="v0.27.2"
if check_for_gh_release "affine_app" "toeverything/AFFiNE" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
msg_info "Stopping Services"
systemctl stop affine-web affine-worker
@@ -38,10 +38,19 @@ function update_script() {
ensure_dependencies cmake
create_backup /root/.affine/config /root/.affine/storage
create_backup /opt/affine/.env /root/.affine/config /root/.affine/storage
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "${RELEASE}" "/opt/affine"
# Restore BEFORE the build: CLEAN_INSTALL wiped /opt/affine including .env,
# and the build below sources it
restore_backup
if [[ ! -f /opt/affine/.env ]]; then
msg_error "/opt/affine/.env is missing (lost by an earlier update). Recreate it before retrying — see the AFFiNE install script for the expected variables."
exit 1
fi
msg_info "Rebuilding Application (Patience ~25 mins, don't close the console!)"
cd /opt/affine
source /root/.profile
@@ -111,8 +120,6 @@ TURBO
set -a && source /opt/affine/.env && set +a
$STD node ./scripts/self-host-predeploy.js
restore_backup
msg_info "Starting Services"
systemctl start affine-web affine-worker
msg_ok "Started Services"
+4 -3
View File
@@ -38,13 +38,14 @@ function update_script() {
NODE_VERSION="24" setup_nodejs
setup_go
UV_PYTHON_INSTALL_DIR="/usr/local/bin" PYTHON_VERSION="3.14.3" setup_uv
$STD uv cache clean
UV_PYTHON_INSTALL_DIR="/usr/local/bin" PYTHON_VERSION="3.14.6" setup_uv
RUST_PROFILE="minimal" RUST_TOOLCHAIN="stable" setup_rust
setup_yq
AUTHENTIK_VERSION="version/2026.5.3"
AUTHENTIK_VERSION="version/2026.5.5"
# Source: https://github.com/goauthentik/fips/blob/main/Makefile#L26
XMLSEC_VERSION="1.3.11"
XMLSEC_VERSION="1.3.12"
if check_for_gh_release "geoipupdate" "maxmind/geoipupdate"; then
fetch_and_deploy_gh_release "geoipupdate" "maxmind/geoipupdate" "binary"
+1
View File
@@ -53,6 +53,7 @@ function update_script() {
mkdir -p /opt/bookorbit/server/migrations
cp -r /opt/bookorbit/server/src/db/migrations/. /opt/bookorbit/server/migrations/
chmod +x /opt/bookorbit/server/bin/kepubify/*
restore_backup
APP_VER=$(cat ~/.bookorbit)
sed -i "s/^APP_VERSION=.*/APP_VERSION=v$APP_VER/" /opt/bookorbit/.env
msg_ok "Rebuilt Application"
+24
View File
@@ -36,6 +36,30 @@ function update_script() {
fetch_and_deploy_gh_release "gotify" "gotify/server" "prebuild" "latest" "/opt/gotify" "gotify-linux-$(arch_resolve).zip"
chmod +x /opt/gotify/gotify-linux-$(arch_resolve)
if [[ ! -f /opt/gotify/gotify-server.env ]]; then
gotify_old_config=""
for f in /opt/gotify/config.yml /etc/gotify/config.yml; do
[[ -f "$f" ]] && gotify_old_config="$f" && break
done
if [[ -n "$gotify_old_config" ]]; then
msg_info "Migrating ${gotify_old_config} to env format (Gotify 3.x)"
if /opt/gotify/gotify-linux-$(arch_resolve) migrate-config "$gotify_old_config" >/opt/gotify/gotify-server.env 2>/dev/null; then
mv "$gotify_old_config" "${gotify_old_config}.bak"
msg_ok "Migrated config to /opt/gotify/gotify-server.env (backup: ${gotify_old_config}.bak)"
else
rm -f /opt/gotify/gotify-server.env
msg_warn "Config migration failed — left ${gotify_old_config} in place, review manually"
fi
fi
fi
if ! grep -qE '^ExecStart=.* serve' /etc/systemd/system/gotify.service 2>/dev/null; then
msg_info "Migrating service to serve subcommand (Gotify 3.x)"
sed -i -E 's|^(ExecStart=/opt/gotify/.*gotify-linux-[^ ]+)$|\1 serve|' /etc/systemd/system/gotify.service
systemctl daemon-reload
msg_ok "Migrated service to serve subcommand"
fi
msg_info "Starting Service"
systemctl start gotify
msg_ok "Started Service"
+3
View File
@@ -42,6 +42,9 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/opt/nametag"
# Restore .env BEFORE the build: CLEAN_INSTALL wiped it and the build sources it
cp /opt/nametag.env.bak /opt/nametag/.env
msg_info "Rebuilding Application"
cd /opt/nametag
$STD npm ci
+1 -1
View File
@@ -43,7 +43,7 @@ function update_script() {
grep -o 'https://static\.tp-link\.com/upload/software/[^"]*linux_x64[^"]*\.deb' |
head -n1)
OMADA_PKG=$(basename "${OMADA_URL}")
VERSION=$(sed -n 's/.*_v\([0-9.]*\)_.*_\([0-9]\{14\}\)\.deb$/\1-\2/p' <<<"${OMADA_PKG}")
VERSION=$(sed -n 's/.*_v\([0-9.]*\)_linux.*/\1/p' <<<"${OMADA_PKG}")
CURRENT_VERSION=$(cat $HOME/.omada 2>/dev/null || echo "0")
+4 -2
View File
@@ -41,9 +41,12 @@ function update_script() {
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "postiz" "gitroomhq/postiz-app" "tarball"
# Restore BEFORE the build: CLEAN_INSTALL wiped /opt/postiz including .env,
# and the build below sources it
restore_backup
msg_info "Building Application"
cd /opt/postiz
cp /opt/postiz_env.bak /opt/postiz/.env
set -a && source /opt/postiz/.env && set +a
export NODE_OPTIONS="--max-old-space-size=4096"
$STD pnpm install
@@ -57,7 +60,6 @@ function update_script() {
msg_ok "Ran Database Migrations"
mkdir -p /opt/postiz/uploads
restore_backup
msg_info "Starting Services"
systemctl start postiz-backend postiz-frontend postiz-orchestrator
+22 -5
View File
@@ -37,18 +37,29 @@ function update_script() {
systemctl stop romm-backend romm-worker romm-scheduler romm-watcher
msg_ok "Stopped Services"
msg_info "Backing up configuration"
cp /opt/romm/.env /opt/romm/.env.backup
msg_ok "Backed up configuration"
create_backup /opt/romm/.env
BACKUP_DIR=/opt/romm-players.backup create_backup \
/opt/romm/frontend/dist/assets/emulatorjs \
/opt/romm/frontend/dist/assets/ruffle
fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm"
restore_backup
msg_info "Updating ROMM"
cp /opt/romm/.env.backup /opt/romm/.env
cd /opt/romm
$STD uv sync --all-extras
cd /opt/romm/backend
$STD uv run alembic upgrade head
if [[ -f /opt/romm/backend/utils/rom_patcher/package.json ]]; then
cd /opt/romm/backend/utils/rom_patcher
$STD npm install --ignore-scripts --no-audit --no-fund
if [[ -d node_modules/rom-patcher/rom-patcher-js ]]; then
rm -rf rom-patcher-js
cp -r node_modules/rom-patcher/rom-patcher-js ./rom-patcher-js
fi
rm -rf node_modules
fi
cd /opt/romm/frontend
$STD npm install
$STD npm run build
@@ -73,6 +84,12 @@ function update_script() {
msg_ok "Started Services"
msg_ok "Updated successfully"
fi
if check_for_gh_release "EmulatorJS" "EmulatorJS/EmulatorJS" "v4.2.3"; then
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "EmulatorJS" "EmulatorJS/EmulatorJS" "prebuild" "v4.2.3" "/opt/romm/frontend/dist/assets/emulatorjs" "4.2.3.7z"
systemctl restart romm-backend romm-worker romm-scheduler romm-watcher
msg_ok "Updated EmulatorJS successfully"
fi
exit
}
+4 -4
View File
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/mauriceboe/TREK
# Source: https://github.com/liketrek/TREK
APP="TREK"
var_tags="${var_tags:-travel;planning;collaboration}"
@@ -32,7 +32,7 @@ function update_script() {
NODE_VERSION="24" setup_nodejs
if check_for_gh_release "trek" "mauriceboe/TREK"; then
if check_for_gh_release "trek" "liketrek/TREK"; then
MIGRATION=0
grep -qF "ExecStart=/usr/bin/node --import tsx src/index.ts" \
/etc/systemd/system/trek.service && MIGRATION=1
@@ -47,7 +47,7 @@ function update_script() {
/opt/trek/data \
/opt/trek/uploads
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "trek" "mauriceboe/TREK" "tarball"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "trek" "liketrek/TREK" "tarball"
msg_info "Building TREK"
cd /opt/trek
@@ -79,7 +79,7 @@ function update_script() {
cat <<EOF >/etc/systemd/system/trek.service
[Unit]
Description=TREK Travel Planner
Documentation=https://github.com/mauriceboe/TREK
Documentation=https://github.com/liketrek/TREK
After=network-online.target
Wants=network-online.target
+1 -1
View File
@@ -65,7 +65,7 @@ function update_script() {
systemctl stop vikunja
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary"
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
$STD systemctl daemon-reload
msg_info "Starting Service"
+2 -2
View File
@@ -46,8 +46,8 @@ function update_script() {
cd /opt/wanderer/source/web
$STD npm ci
$STD npm run build
mkdir -p /opt/wanderer/data/plugins
[[ -e /data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /data/plugins
mkdir -p /opt/wanderer/data/plugins /opt/wanderer/source/db/data
[[ -e /opt/wanderer/source/db/data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /opt/wanderer/source/db/data/plugins
msg_info "Installing wanderer plugins"
for plugin in hammerhead komoot strava; do
fetch_and_deploy_gh_release "wanderer-plugin-${plugin}" "open-wanderer/wanderer" "prebuild" "${CHECK_UPDATE_RELEASE:-latest}" "/opt/wanderer/data/plugins" "wanderer-plugin-${plugin}.tar.gz" || msg_warn "Failed to install wanderer plugin: ${plugin}"
+1 -1
View File
@@ -31,7 +31,7 @@ PG_DB_NAME="affine" PG_DB_USER="affine" setup_postgresql_db
NODE_VERSION="22" setup_nodejs
setup_rust
fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "v0.27.0" "/opt/affine"
fetch_and_deploy_gh_release "affine_app" "toeverything/AFFiNE" "tarball" "v0.27.2" "/opt/affine"
msg_info "Setting up Directories"
rm -rf /root/.affine
+3 -3
View File
@@ -54,12 +54,12 @@ NODE_VERSION="24" setup_nodejs
setup_yq
setup_go
RUST_PROFILE="minimal" RUST_TOOLCHAIN="stable" setup_rust
UV_PYTHON_INSTALL_DIR="/usr/local/bin" PYTHON_VERSION="3.14.3" setup_uv
UV_PYTHON_INSTALL_DIR="/usr/local/bin" PYTHON_VERSION="3.14.6" setup_uv
PG_VERSION="17" setup_postgresql
PG_DB_NAME="authentik" PG_DB_USER="authentik" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
XMLSEC_VERSION="1.3.11"
AUTHENTIK_VERSION="version/2026.5.3"
XMLSEC_VERSION="1.3.12"
AUTHENTIK_VERSION="version/2026.5.5"
fetch_and_deploy_gh_release "xmlsec" "lsh123/xmlsec" "tarball" "${XMLSEC_VERSION}" "/opt/xmlsec"
fetch_and_deploy_gh_release "authentik" "goauthentik/authentik" "tarball" "${AUTHENTIK_VERSION}" "/opt/authentik"
fetch_and_deploy_gh_release "geoipupdate" "maxmind/geoipupdate" "binary"
+1 -1
View File
@@ -27,7 +27,7 @@ After=network.target
Type=simple
User=root
WorkingDirectory=/opt/gotify
ExecStart=/opt/gotify/./gotify-linux-$(arch_resolve)
ExecStart=/opt/gotify/gotify-linux-$(arch_resolve) serve
Restart=always
RestartSec=3
+2 -1
View File
@@ -31,13 +31,14 @@ NODE_VERSION="24" NODE_MODULE="corepack,yarn" setup_nodejs
fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app"
useradd -m -s /usr/bin/bash manyfold
RUBY_INSTALL_VERSION=$(cat /opt/manyfold/app/.ruby-version)
RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" HOME=/home/manyfold setup_ruby
msg_info "Configuring Manyfold"
YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/app/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/')
RELEASE=$(get_latest_github_release "manyfold3d/manyfold")
useradd -m -s /usr/bin/bash manyfold
cat <<EOF >/opt/manyfold/.env
export APP_VERSION=${RELEASE}
export GUID=1002
+1 -1
View File
@@ -42,7 +42,7 @@ OMADA_PKG=$(basename "${OMADA_URL}")
curl_download "${OMADA_PKG}" "${OMADA_URL}"
$STD dpkg -i "${OMADA_PKG}"
rm -rf "${OMADA_PKG}"
VERSION=$(sed -n 's/.*_v\([0-9.]*\)_.*_\([0-9]\{14\}\)\.deb$/\1-\2/p' <<<"${OMADA_PKG}")
VERSION=$(sed -n 's/.*_v\([0-9.]*\)_linux.*/\1/p' <<<"${OMADA_PKG}")
echo "${VERSION}" >$HOME/.omada
msg_ok "Installed Omada Controller"
+17
View File
@@ -134,6 +134,8 @@ else
fi
fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball"
fetch_and_deploy_gh_release "ruffle" "ruffle-rs/ruffle" "prebuild" "latest" "/opt/romm/frontend/dist/assets/ruffle" "ruffle-*-web-selfhosted.zip"
fetch_and_deploy_gh_release "EmulatorJS" "EmulatorJS/EmulatorJS" "prebuild" "v4.2.3" "/opt/romm/frontend/dist/assets/emulatorjs" "4.2.3.7z"
msg_info "Creating environment file"
sed -i 's/^supervised no/supervised systemd/' /etc/redis/redis.conf
@@ -159,6 +161,9 @@ ROMM_AUTH_SECRET_KEY=$AUTH_SECRET_KEY
DISABLE_DOWNLOAD_ENDPOINT_AUTH=false
DISABLE_CSRF_PROTECTION=false
SCREENSCRAPER_DEV_ID=
SCREENSCRAPER_DEV_PASSWORD=
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true
RESCAN_ON_FILESYSTEM_CHANGE_DELAY=5
@@ -181,6 +186,18 @@ cd /opt/romm/backend
$STD uv run alembic upgrade head
msg_ok "Set up RomM Backend"
if [[ -f /opt/romm/backend/utils/rom_patcher/package.json ]]; then
msg_info "Building ROM Patcher helper"
cd /opt/romm/backend/utils/rom_patcher
$STD npm install --ignore-scripts --no-audit --no-fund
if [[ -d node_modules/rom-patcher/rom-patcher-js ]]; then
rm -rf rom-patcher-js
cp -r node_modules/rom-patcher/rom-patcher-js ./rom-patcher-js
fi
rm -rf node_modules
msg_ok "Built ROM Patcher helper"
fi
msg_info "Setting up RomM Frontend"
cd /opt/romm/frontend
$STD npm install
+3 -3
View File
@@ -3,7 +3,7 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/mauriceboe/TREK
# Source: https://github.com/liketrek/TREK
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
@@ -20,7 +20,7 @@ $STD apt install -y \
msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
fetch_and_deploy_gh_release "trek" "mauriceboe/TREK" "tarball"
fetch_and_deploy_gh_release "trek" "liketrek/TREK" "tarball"
msg_info "Setup TREK"
cd /opt/trek
@@ -78,7 +78,7 @@ msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/trek.service
[Unit]
Description=TREK Travel Planner
Documentation=https://github.com/mauriceboe/TREK
Documentation=https://github.com/liketrek/TREK
After=network-online.target
Wants=network-online.target
+1 -1
View File
@@ -13,7 +13,7 @@ setting_up_container
network_check
update_os
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary"
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
msg_info "Setting up Vikunja"
sed -i 's|^# \(service:\)|\1|' /etc/vikunja/config.yml
+2 -1
View File
@@ -21,8 +21,9 @@ else
fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" "latest" "/opt/wanderer/source/search"
fi
mkdir -p /opt/wanderer/{source,data/pb_data,data/meili_data,data/plugins}
[[ -e /data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /data/plugins
fetch_and_deploy_gh_release "wanderer" "open-wanderer/wanderer" "tarball" "latest" "/opt/wanderer/source"
mkdir -p /opt/wanderer/source/db/data
[[ -e /opt/wanderer/source/db/data/plugins ]] || ln -sfn /opt/wanderer/data/plugins /opt/wanderer/source/db/data/plugins
msg_info "Installing wanderer (patience)"
cd /opt/wanderer/source/db
+5 -2
View File
@@ -31,7 +31,10 @@ $STD apt install -y elasticsearch
sed -i 's/^#\{0,2\} *-Xms[0-9]*g.*/-Xms2g/' /etc/elasticsearch/jvm.options
sed -i 's/^#\{0,2\} *-Xmx[0-9]*g.*/-Xmx2g/' /etc/elasticsearch/jvm.options
cat <<EOF >/etc/elasticsearch/elasticsearch.yml
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
discovery.type: single-node
network.host: 127.0.0.1
xpack.security.enabled: false
bootstrap.memory_lock: false
EOF
@@ -40,7 +43,7 @@ systemctl daemon-reload
systemctl enable -q elasticsearch
systemctl restart -q elasticsearch
for i in $(seq 1 30); do
if curl -s http://localhost:9200 >/dev/null 2>&1; then
if curl -s http://127.0.0.1:9200 >/dev/null 2>&1; then
break
fi
sleep 2
@@ -55,7 +58,7 @@ setup_deb822_repo \
"$(get_os_info version_id)" \
"main"
$STD apt install -y zammad
$STD zammad run rails r "Setting.set('es_url', 'http://localhost:9200')"
$STD zammad run rails r "Setting.set('es_url', 'http://127.0.0.1:9200')"
$STD zammad run rake zammad:searchindex:rebuild
msg_ok "Installed Zammad"
+5 -1
View File
@@ -6,6 +6,10 @@
if ! command -v curl >/dev/null 2>&1; then
apk update && apk add curl >/dev/null 2>&1
fi
# Mark container context BEFORE error handling starts: error_handler/on_exit
# must write local failure artifacts instead of talking to the telemetry API
# (the host is the single telemetry reporter).
export TELEMETRY_CONTEXT="container"
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
load_functions
@@ -41,7 +45,7 @@ post_progress_to_api() {
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
-H "Content-Type: application/json" \
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\",\"platform\":\"${TELEMETRY_PLATFORM:-}\",\"repo_source\":\"${REPO_SOURCE:-}\",\"repo_slug\":\"${REPO_SLUG:-}\"}" &>/dev/null || true
}
# This function enables IPv6 if it's not disabled and sets verbose mode
+549 -829
View File
File diff suppressed because it is too large Load Diff
+75 -166
View File
@@ -1009,7 +1009,6 @@ base_settings() {
APT_CACHER=${var_apt_cacher:-""}
APT_CACHER_IP=${var_apt_cacher_ip:-""}
INHERIT_HOST_CA="${var_inherit_host_ca:-auto}"
# Runtime check: Verify APT cacher is reachable if configured
if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then
@@ -1089,7 +1088,7 @@ load_vars_file() {
# Allowed var_* keys
local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1286,12 +1285,6 @@ load_vars_file() {
continue
fi
;;
var_inherit_host_ca)
if [[ "$var_val" != "yes" && "$var_val" != "no" && "$var_val" != "auto" ]]; then
msg_warn "Invalid host CA inheritance value '$var_val' in $file (must be yes/no/auto), ignoring"
continue
fi
;;
var_container_storage | var_template_storage)
# Validate that the storage exists and is active on the current node
local _storage_status
@@ -1331,7 +1324,7 @@ default_var_settings() {
# Allowed var_* keys (alphabetically sorted)
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
@@ -1414,7 +1407,6 @@ var_ssh=no
# HTTP/HTTPS proxy (optional - for networks requiring a proxy)
# var_http_proxy=http://proxy.local:8080
# var_http_no_proxy=localhost,127.0.0.1,.local
# var_inherit_host_ca=auto
# Features/Tags/verbosity
var_fuse=no
@@ -1515,7 +1507,7 @@ get_app_defaults_path() {
if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
declare -ag VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1665,7 +1657,6 @@ _build_current_app_vars_tmp() {
_apt_cacher_ip="${APT_CACHER_IP:-}"
_http_proxy="${HTTP_PROXY:-${var_http_proxy:-}}"
_http_no_proxy="${HTTP_NO_PROXY:-${var_http_no_proxy:-}}"
_inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-auto}}"
_fuse="${ENABLE_FUSE:-no}"
_tun="${ENABLE_TUN:-no}"
_gpu="${ENABLE_GPU:-no}"
@@ -1719,7 +1710,6 @@ _build_current_app_vars_tmp() {
[ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")"
[ -n "$_http_proxy" ] && echo "var_http_proxy=$(_sanitize_value "$_http_proxy")"
[ -n "$_http_no_proxy" ] && echo "var_http_no_proxy=$(_sanitize_value "$_http_no_proxy")"
[ -n "$_inherit_host_ca" ] && echo "var_inherit_host_ca=$(_sanitize_value "$_inherit_host_ca")"
[ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")"
[ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")"
@@ -1884,7 +1874,7 @@ advanced_settings() {
TAGS="community-script${var_tags:+;${var_tags}}"
fi
local STEP=1
local MAX_STEP=31
local MAX_STEP=30
# Store values for back navigation - inherit from var_* app defaults
local _ct_type="${var_unprivileged:-1}"
@@ -1906,7 +1896,6 @@ advanced_settings() {
local _apt_cacher_ip="${var_apt_cacher_ip:-}"
local _http_proxy="${var_http_proxy:-}"
local _http_no_proxy="${var_http_no_proxy:-}"
local _inherit_host_ca="${var_inherit_host_ca:-auto}"
local _mtu="${var_mtu:-}"
local _sd="${var_searchdomain:-}"
local _ns="${var_ns:-}"
@@ -2736,47 +2725,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 25: Host CA Inheritance
# STEP 25: Container Timezone
# ═══════════════════════════════════════════════════════════════════════════
25)
local host_ca_count=0
local host_ca_dir="/usr/local/share/ca-certificates"
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_ca_count=$((host_ca_count + 1))
done
shopt -u nullglob
if [[ $host_ca_count -eq 0 ]]; then
_inherit_host_ca="auto"
((STEP++))
continue
fi
local host_ca_default_flag=""
[[ "$_inherit_host_ca" == "no" ]] && host_ca_default_flag="--defaultno"
if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
--title "HOST CA INHERITANCE" \
--ok-button "Next" --cancel-button "Back" \
$host_ca_default_flag \
--yesno "\nInherit host CA certificates into this container?\n\nDetected on host: ${host_ca_count} certificate(s) in:\n${host_ca_dir}\n\nRecommended for private PKI / TLS-inspection environments.\n\n(App default: ${var_inherit_host_ca:-auto})" 16 72; then
_inherit_host_ca="yes"
else
if [ $? -eq 1 ]; then
_inherit_host_ca="no"
else
((STEP--))
continue
fi
fi
((STEP++))
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 26: Container Timezone
# ═══════════════════════════════════════════════════════════════════════════
26)
local tz_hint="$_ct_timezone"
[[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)"
@@ -2799,9 +2750,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 27: Container Protection
# STEP 26: Container Protection
# ═══════════════════════════════════════════════════════════════════════════
27)
26)
local protect_default_flag="--defaultno"
[[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag=""
@@ -2823,9 +2774,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 28: Device Node Creation (mknod)
# STEP 27: Device Node Creation (mknod)
# ═══════════════════════════════════════════════════════════════════════════
28)
27)
local mknod_default_flag="--defaultno"
[[ "$_enable_mknod" == "1" ]] && mknod_default_flag=""
@@ -2847,9 +2798,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 29: Mount Filesystems
# STEP 28: Mount Filesystems
# ═══════════════════════════════════════════════════════════════════════════
29)
28)
local mount_hint=""
[[ -n "$_mount_fs" ]] && mount_hint="$_mount_fs" || mount_hint="(none)"
@@ -2870,9 +2821,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 30: Optional host-side post-install hook (path on the Proxmox HOST)
# STEP 29: Optional host-side post-install hook (path on the Proxmox HOST)
# ═══════════════════════════════════════════════════════════════════════════
30)
29)
local _hook_prompt="Optional: absolute path to a *.sh file ON THE PROXMOX HOST.
It runs as root on the HOST (NOT in the LXC) after the container
@@ -2922,9 +2873,9 @@ Leave empty to skip."
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 31: Verbose Mode & Confirmation
# STEP 30: Verbose Mode & Confirmation
# ═══════════════════════════════════════════════════════════════════════════
31)
30)
local verbose_default_flag="--defaultno"
[[ "$_verbose" == "yes" ]] && verbose_default_flag=""
@@ -2953,7 +2904,6 @@ Leave empty to skip."
local apt_display="${_apt_cacher:-no}"
[[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip"
local http_proxy_display="${_http_proxy:-(none)}"
local inherit_ca_display="${_inherit_host_ca:-auto}"
local post_install_display="${_post_install:-(none)}"
local post_install_warn=""
@@ -2984,7 +2934,6 @@ Advanced:
Timezone: $tz_display
APT Cacher: $apt_display
HTTP Proxy: $http_proxy_display
Inherit Host CAs: $inherit_ca_display
Verbose: $_verbose
Post-Install Script: ${post_install_display}${post_install_warn}"
@@ -3030,7 +2979,6 @@ Advanced:
APT_CACHER_IP="$_apt_cacher_ip"
HTTP_PROXY="$_http_proxy"
HTTP_NO_PROXY="$_http_no_proxy"
INHERIT_HOST_CA="$_inherit_host_ca"
VERBOSE="$_verbose"
var_post_install="$_post_install"
@@ -3049,7 +2997,6 @@ Advanced:
var_sdn_vnet="$_sdn_vnet"
var_http_proxy="$_http_proxy"
var_http_no_proxy="$_http_no_proxy"
var_inherit_host_ca="$_inherit_host_ca"
# Format optional values
[[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU=""
@@ -3653,9 +3600,9 @@ configure_ssh_settings() {
glob_path=$(whiptail --backtitle "$backtitle" \
--inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)
if [[ -n "$glob_path" ]]; then
shopt -s nullglob
read -r -a _scan_files <<<"$glob_path"
shopt -u nullglob
[[ -d "$glob_path" ]] && glob_path="${glob_path%/}/*"
local -a _scan_files
mapfile -t _scan_files < <(compgen -G "$glob_path")
if [[ "${#_scan_files[@]}" -gt 0 ]]; then
ssh_build_choices_from_files "${_scan_files[@]}"
if [[ "$COUNT" -gt 0 ]]; then
@@ -3998,81 +3945,6 @@ EOF
msg_ok "Applied HTTP proxy in container"
}
# ------------------------------------------------------------------------------
# _apply_host_ca_certs_in_container()
#
# - Copies administrator-provided CA certificates from the Proxmox host into the
# container before base package bootstrap
# - Source: /usr/local/share/ca-certificates/*.crt (Debian convention)
# - Refreshes the container trust store when update-ca-certificates is available
# - No-op when no host certificates are present; failures are non-fatal
# ------------------------------------------------------------------------------
_apply_host_ca_certs_in_container() {
local host_ca_dir="/usr/local/share/ca-certificates"
[[ -z "${CTID:-}" ]] && return 0
local inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-auto}}"
local -a host_certs=()
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_certs+=("$cert")
done
shopt -u nullglob
[[ ${#host_certs[@]} -eq 0 ]] && return 0
case "${inherit_host_ca,,}" in
no | false | 0 | off)
msg_warn "Skipping host CA inheritance by configuration (${#host_certs[@]} host certificate(s) available)"
return 0
;;
esac
msg_info "Inheriting host CA certificates into container"
local found=${#host_certs[@]}
local copied=0
local skipped=0
local cert_name
pct exec "$CTID" -- mkdir -p /usr/local/share/ca-certificates >/dev/null 2>&1 || {
msg_warn "Failed to create CA certificate directory in container"
return 0
}
for cert in "${host_certs[@]}"; do
cert_name="$(basename "$cert")"
if [[ ! -r "$cert" || "$cert_name" != *.crt ]]; then
msg_warn "Skipping invalid or unreadable host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
continue
fi
if pct push "$CTID" "$cert" "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1; then
pct exec "$CTID" -- chmod 644 "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1 || true
copied=$((copied + 1))
else
msg_warn "Failed to push host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
fi
done
if [[ $copied -eq 0 ]]; then
msg_warn "No host CA certificates were copied (${found} found, ${skipped} skipped)"
return 0
fi
local refresh_shell="bash"
[[ "$var_os" == "alpine" ]] && refresh_shell="ash"
if pct exec "$CTID" -- "$refresh_shell" -c 'command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates' >/dev/null 2>&1; then
msg_ok "Inherited ${copied} host CA certificate(s) and updated trust store (${skipped} skipped)"
else
msg_warn "Copied ${copied} host CA certificate(s), but trust store update failed or update-ca-certificates is unavailable (${skipped} skipped)"
fi
}
# ------------------------------------------------------------------------------
# build_container()
#
@@ -4200,6 +4072,11 @@ build_container() {
export RANDOM_UUID="$RANDOM_UUID"
export EXECUTION_ID="$EXECUTION_ID"
export SESSION_ID="$SESSION_ID"
# Repo attribution + platform for container-side progress pings (the
# container inherits the host's detection instead of re-detecting)
export REPO_SOURCE="${REPO_SOURCE:-}"
export REPO_SLUG="${REPO_SLUG:-}"
export TELEMETRY_PLATFORM="pve"
export CACHER="$APT_CACHER"
export CACHER_IP="$APT_CACHER_IP"
if [[ -n "${HTTP_PROXY:-}" ]]; then
@@ -4361,27 +4238,31 @@ $PCT_OPTIONS_STRING"
return 0
fi
# Check for Intel GPU - look for Intel vendor ID [8086]
if grep -q "\[8086:" <<<"$pci_vga_info"; then
msg_custom "🎮" "${BL}" "Detected Intel GPU"
if [[ -d /dev/dri ]]; then
for d in /dev/dri/renderD* /dev/dri/card*; do
[[ -e "$d" ]] && INTEL_DEVICES+=("$d")
done
fi
# Map each DRI render/card node to its owning GPU via the PCI vendor ID
# exposed in sysfs (/sys/class/drm/<node>/device/vendor). This is required
# on multi-GPU hosts: globbing every /dev/dri node onto the first detected
# vendor would pass through *both* GPUs and skip the selection prompt.
if [[ -d /dev/dri ]]; then
for d in /dev/dri/renderD* /dev/dri/card*; do
[[ -e "$d" ]] || continue
local node vendor
node=$(basename "$d")
vendor=$(cat "/sys/class/drm/${node}/device/vendor" 2>/dev/null || true)
case "$vendor" in
0x8086) INTEL_DEVICES+=("$d") ;; # Intel
0x1002 | 0x1022) AMD_DEVICES+=("$d") ;; # AMD/ATI
0x10de) ;; # NVIDIA - handled via /dev/nvidia* below
*) msg_debug "Unmapped GPU vendor '${vendor:-unknown}' for $d" ;;
esac
done
fi
# Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD)
if grep -qE "\[1002:|\[1022:" <<<"$pci_vga_info"; then
msg_custom "🎮" "${RD}" "Detected AMD GPU"
if [[ -d /dev/dri ]]; then
# Only add if not already claimed by Intel
if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then
for d in /dev/dri/renderD* /dev/dri/card* /dev/kfd; do
[[ -e "$d" ]] && AMD_DEVICES+=("$d")
done
fi
fi
[[ ${#INTEL_DEVICES[@]} -gt 0 ]] && msg_custom "🎮" "${BL}" "Detected Intel GPU"
[[ ${#AMD_DEVICES[@]} -gt 0 ]] && msg_custom "🎮" "${RD}" "Detected AMD GPU"
# AMD compute (ROCm) additionally needs /dev/kfd alongside the render nodes
if [[ ${#AMD_DEVICES[@]} -gt 0 && -e /dev/kfd ]]; then
AMD_DEVICES+=("/dev/kfd")
fi
# Check for NVIDIA GPU - look for NVIDIA vendor ID [10de]
@@ -4693,7 +4574,6 @@ EOF
local install_exit_code=0
_apply_http_proxy_in_container
_apply_host_ca_certs_in_container
# Continue with standard container setup
if [ "$var_os" == "alpine" ]; then
@@ -5020,6 +4900,16 @@ EOF
# Point INSTALL_LOG to combined log so get_full_log() finds it
INSTALL_LOG="$combined_log"
fi
# Pull the structured error capture (.errinfo) from the container.
# It contains EXACTLY the output of the command that failed (written by
# silent()/error_handler inside the container) and is the primary source
# for the telemetry error trace - instead of a generic log tail.
local host_errinfo="/tmp/.errinfo-${SESSION_ID}"
if timeout 8 pct pull "$CTID" "/root/.install-${SESSION_ID}.log.errinfo" "$host_errinfo" 2>/dev/null && [[ -s "$host_errinfo" ]]; then
TELEMETRY_ERRINFO="$host_errinfo"
export TELEMETRY_ERRINFO
fi
fi
# Defense-in-depth: Ensure error handling stays disabled during recovery.
@@ -5295,6 +5185,8 @@ EOF
echo -e " Verbose: ${GN}enabled${CL}"
echo ""
msg_info "Restarting installation..."
# New telemetry execution for the retry (previous one keeps its "failed")
declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt
# Re-run build_container
build_container
return $?
@@ -5334,6 +5226,10 @@ EOF
echo ""
msg_info "Re-running installation script..."
# New telemetry execution for the in-place retry
declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt
declare -f post_to_api &>/dev/null && post_to_api 2>/dev/null || true
# Re-run install script in existing container (don't destroy/recreate)
set +Eeuo pipefail
trap - ERR
@@ -5397,6 +5293,7 @@ EOF
echo -e " Verbose: ${GN}enabled${CL}"
echo ""
msg_info "Restarting installation..."
declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt
build_container
return $?
fi
@@ -5426,6 +5323,7 @@ EOF
echo -e " Verbose: ${GN}enabled${CL}"
echo ""
msg_info "Restarting installation..."
declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt
build_container
return $?
fi
@@ -5450,6 +5348,7 @@ EOF
echo -e " Verbose: ${GN}enabled${CL}"
echo ""
msg_info "Restarting installation..."
declare -f telemetry_new_attempt &>/dev/null && telemetry_new_attempt
build_container
return $?
fi
@@ -7124,6 +7023,16 @@ ensure_log_on_host() {
rm -f "$temp_log"
fi
fi
# Also pull the structured error capture (.errinfo) so the telemetry error
# trace shows the failing command's exact output (signal-exit paths reach
# this via on_exit before/instead of the recovery flow)
if [[ -z "${TELEMETRY_ERRINFO:-}" || ! -s "${TELEMETRY_ERRINFO:-}" ]]; then
local host_errinfo="/tmp/.errinfo-${SESSION_ID}"
if timeout 8 pct pull "$CTID" "/root/.install-${SESSION_ID}.log.errinfo" "$host_errinfo" 2>/dev/null && [[ -s "$host_errinfo" ]]; then
TELEMETRY_ERRINFO="$host_errinfo"
export TELEMETRY_ERRINFO
fi
fi
if [[ -s "$combined_log" ]]; then
INSTALL_LOG="$combined_log"
fi
+37
View File
@@ -546,6 +546,12 @@ silent() {
set +Eeuo pipefail
trap - ERR
# Byte offset BEFORE the command runs - everything the log grows by is
# exactly this command's output (used for the .errinfo telemetry capture)
local start_bytes=0
[[ -f "$logfile" ]] && start_bytes=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
[[ ! "$start_bytes" =~ ^[0-9]+$ ]] && start_bytes=0
"$@" >>"$logfile" 2>&1
local rc=$?
@@ -567,12 +573,43 @@ silent() {
export _SILENT_FAILED_LINE="$caller_line"
export _SILENT_FAILED_LOG="$logfile"
# ── Structured error capture (.errinfo) for telemetry ──
# Extract exactly THIS command's output (from the recorded byte offset),
# strip ANSI/progress noise, keep the last 60 lines. The host builds the
# telemetry error trace from this file (pulled from the container on
# failure). Self-contained - containers don't source api.func.
{
local flat_cmd
flat_cmd=$(printf '%s' "$cmd" | tr '\n' ' ' | head -c 300)
echo "EXIT_CODE=${rc}"
echo "LINE=${caller_line}"
echo "COMMAND=${flat_cmd}"
echo "--- OUTPUT ---"
if [[ -s "$logfile" ]]; then
local segment
segment=$(tail -c +"$((start_bytes + 1))" "$logfile" 2>/dev/null |
sed 's/\r$//' |
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' |
grep -avE '^(Get:|Hit:|Ign:|Fetched |Reading package lists|Reading state information|Building dependency tree|Selecting previously|Preparing to unpack|Unpacking |Processing triggers for|\(Reading database|[0-9]+%[[:space:]]*\[)' |
grep -avE '^[[:space:]]*$' |
tail -n 60)
# If the noise filter swallowed everything, fall back to the raw tail
if [[ -z "$segment" ]]; then
segment=$(tail -c +"$((start_bytes + 1))" "$logfile" 2>/dev/null |
sed 's/\r$//' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' | tail -n 60)
fi
printf '%s' "$segment" | head -c 10240
fi
} >"${logfile}.errinfo" 2>/dev/null || true
return "$rc"
fi
# Clear stale flags on success (prevents false positives if a previous
# $STD cmd || true failed and a later non-silent command triggers error_handler)
unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE _SILENT_FAILED_LOG 2>/dev/null || true
# Also drop a stale .errinfo from a previously tolerated failure ($STD cmd || true)
rm -f "${logfile}.errinfo" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
+182 -222
View File
@@ -7,12 +7,17 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# ------------------------------------------------------------------------------
#
# Provides comprehensive error handling and signal management for all scripts.
# Includes:
# - Exit code explanations (shell, package managers, databases, custom codes)
# - Error handler with detailed logging
# - Signal handlers (EXIT, INT, TERM)
# - Initialization function for trap setup
# Provides error handling and signal management for all scripts.
#
# TELEMETRY CONTRACT:
# - HOST context: this file reports terminal statuses via post_update_to_api
# (full metadata + focused error trace).
# - CONTAINER context: this file NEVER talks to the telemetry API. It writes
# two local artifacts that the host picks up after lxc-attach returns:
# /root/.install-<SESSION_ID>.failed → exit code (flag file)
# /root/.install-<SESSION_ID>.log.errinfo → structured error capture
# This guarantees the server only ever sees ONE terminal event per
# execution - the host's complete one.
#
# Usage:
# source <(curl -fsSL .../error_handler.func)
@@ -21,15 +26,14 @@
# ------------------------------------------------------------------------------
# ==============================================================================
# SECTION 1: EXIT CODE EXPLANATIONS
# SECTION 1: EXIT CODE EXPLANATIONS (fallback)
# ==============================================================================
# ------------------------------------------------------------------------------
# explain_exit_code()
#
# - Canonical version is defined in api.func (sourced before this file)
# - This section only provides a fallback if api.func was not loaded
# - See api.func SECTION 1 for the authoritative exit code mappings
# - Canonical version lives in api.func (sourced before this file on the host)
# - This fallback covers the container context where api.func is not sourced
# ------------------------------------------------------------------------------
if ! declare -f explain_exit_code &>/dev/null; then
explain_exit_code() {
@@ -94,8 +98,6 @@ if ! declare -f explain_exit_code &>/dev/null; then
100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;
# --- Script Validation & Setup (103-123) ---
103) echo "Validation: Shell is not Bash" ;;
104) echo "Validation: Not running as root (or invoked via sudo)" ;;
105) echo "Validation: Proxmox VE version not supported" ;;
@@ -177,9 +179,8 @@ if ! declare -f explain_exit_code &>/dev/null; then
223) echo "Proxmox: Template not available after download" ;;
224) echo "Proxmox: PBS storage is for backups only" ;;
225) echo "Proxmox: No template available for OS/Version" ;;
226) echo "Proxmox: VM disk import or post-creation setup failed" ;;
231) echo "Proxmox: LXC stack upgrade failed" ;;
# --- Tools & Addon Scripts (232-238) ---
232) echo "Tools: Wrong execution environment (run on PVE host, not inside LXC)" ;;
233) echo "Tools: Application not installed (update prerequisite missing)" ;;
234) echo "Tools: No LXC containers found or available" ;;
@@ -187,7 +188,6 @@ if ! declare -f explain_exit_code &>/dev/null; then
236) echo "Tools: Required hardware not detected" ;;
237) echo "Tools: Dependency package installation failed" ;;
238) echo "Tools: OS or distribution not supported for this addon" ;;
239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;;
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
245) echo "Node.js: Invalid command-line option" ;;
@@ -195,14 +195,11 @@ if ! declare -f explain_exit_code &>/dev/null; then
247) echo "Node.js: Fatal internal error" ;;
248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
249) echo "npm/pnpm/yarn: Unknown fatal error" ;;
# --- Application Install/Update Errors (250-254) ---
250) echo "App: Download failed or version not determined" ;;
251) echo "App: File extraction failed (corrupt or incomplete archive)" ;;
252) echo "App: Required file or resource not found" ;;
253) echo "App: Data migration required — update aborted" ;;
254) echo "App: User declined prompt or input timed out" ;;
255) echo "DPKG: Fatal internal error" ;;
*) echo "Unknown error" ;;
esac
@@ -210,24 +207,98 @@ if ! declare -f explain_exit_code &>/dev/null; then
fi
# ==============================================================================
# SECTION 2: ERROR HANDLERS
# SECTION 2: CONTEXT DETECTION & CONTAINER ARTIFACTS
# ==============================================================================
# ------------------------------------------------------------------------------
# _is_container_context()
#
# - Returns 0 (true) when running INSIDE the LXC container being installed
# - TELEMETRY_CONTEXT can override the heuristic ("host" / "container");
# install.func sets TELEMETRY_CONTEXT=container during bootstrap
# ------------------------------------------------------------------------------
_is_container_context() {
case "${TELEMETRY_CONTEXT:-}" in
container) return 0 ;;
host) return 1 ;;
esac
# Proxmox/Incus tooling exists only on the host
command -v pveversion &>/dev/null && return 1
command -v pct &>/dev/null && return 1
command -v incus &>/dev/null && return 1
# systemd-detect-virt reports lxc inside containers
if command -v systemd-detect-virt &>/dev/null; then
case "$(systemd-detect-virt -c 2>/dev/null)" in
lxc | lxc-libvirt | openvz) return 0 ;;
esac
fi
# PCT_OSTYPE is exported into the install environment by the host
[[ -n "${PCT_OSTYPE:-}" ]] && return 0
return 1
}
# ------------------------------------------------------------------------------
# _container_write_failure()
#
# - Writes the failure artifacts inside the container for the host to pick up:
# * flag file with the exit code
# * .errinfo capture (if silent() has not already written a better one)
# * copy of the install log
# - This REPLACES any direct telemetry send from the container
# - Arguments: $1 = exit_code, $2 = command (optional), $3 = line (optional)
# ------------------------------------------------------------------------------
_container_write_failure() {
local exit_code="${1:-1}"
local command="${2:-}"
local line="${3:-}"
local sid="${SESSION_ID:-error}"
# Flag file with exit code (host reads this after lxc-attach returns)
echo "$exit_code" >"/root/.install-${sid}.failed" 2>/dev/null || true
# Keep the install log where the host expects it
if [[ -n "${INSTALL_LOG:-}" && -f "${INSTALL_LOG}" && "${INSTALL_LOG}" != "/root/.install-${sid}.log" ]]; then
cp "${INSTALL_LOG}" "/root/.install-${sid}.log" 2>/dev/null || true
fi
# Structured error capture (skip if silent() already wrote the exact
# output segment of the failing command - that one is always better).
# Self-contained: api.func is NOT sourced inside containers.
local errinfo="${INSTALL_LOG:-/root/.install-${sid}.log}.errinfo"
if [[ ! -s "$errinfo" ]]; then
local flat_cmd
flat_cmd=$(printf '%s' "${command:-unknown}" | tr '\n' ' ' | head -c 300)
{
echo "EXIT_CODE=${exit_code}"
echo "LINE=${line:-0}"
echo "COMMAND=${flat_cmd}"
echo "--- OUTPUT ---"
if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then
tail -n 80 "${INSTALL_LOG}" 2>/dev/null |
sed 's/\r$//' |
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' |
grep -avE '^(Get:|Hit:|Ign:|Fetched |Reading package lists|Reading state information|Building dependency tree|Selecting previously|Preparing to unpack|Unpacking |Processing triggers for|\(Reading database|[0-9]+%[[:space:]]*\[)' |
grep -avE '^[[:space:]]*$' |
tail -n 60 | head -c 10240
fi
} >"$errinfo" 2>/dev/null || true
fi
}
# ==============================================================================
# SECTION 3: ERROR HANDLER
# ==============================================================================
# ------------------------------------------------------------------------------
# error_handler()
#
# - Main error handler triggered by ERR trap
# - Arguments: exit_code, command, line_number
# - Behavior:
# * Returns silently if exit_code is 0 (success)
# * Sources explain_exit_code() for detailed error description
# * Displays error message with:
# - Line number where error occurred
# - Exit code with explanation
# - Command that failed
# * Shows last 20 lines of SILENT_LOGFILE if available
# * Copies log to container /root for later inspection
# * Exits with original exit code
# - Displays error message with line number, exit code, explanation, command
# - Shows last 20 lines of the active log
# - Emits actionable hints for common failure patterns (OOM, APT, network...)
# - HOST: reports "failed" to telemetry (full payload via api.func)
# - CONTAINER: writes failure artifacts, sends nothing
# - Exits with original exit code
# ------------------------------------------------------------------------------
error_handler() {
local exit_code=${1:-$?}
@@ -237,12 +308,10 @@ error_handler() {
command="${command//\$STD/}"
# If error originated from silent(), use its captured metadata
# This provides the actual command and line number instead of "silent ..."
if [[ -n "${_SILENT_FAILED_RC:-}" ]]; then
exit_code="$_SILENT_FAILED_RC"
command="$_SILENT_FAILED_CMD"
line_number="$_SILENT_FAILED_LINE"
# Clear flags to prevent stale data on subsequent errors
unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE
fi
@@ -250,8 +319,12 @@ error_handler() {
return 0
fi
# Stop spinner and restore cursor FIRST — before any output
# This prevents spinner text overlapping with error messages
# Export the failure location so telemetry can include the "where"
FAILED_COMMAND="$command"
FAILED_LINE="$line_number"
export FAILED_COMMAND FAILED_LINE
# Stop spinner and restore cursor FIRST - before any output
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
@@ -259,18 +332,18 @@ error_handler() {
local explanation
explanation="$(explain_exit_code "$exit_code")"
# ALWAYS report failure to API immediately - don't wait for container checks
# This ensures we capture failures that occur before/after container exists
if declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
else
# Container context: post_update_to_api not available (api.func not sourced)
# Send status directly via curl so container failures are never lost
_send_abort_telemetry "$exit_code" 2>/dev/null || true
if [[ "$explanation" == curl:* && ! "$command" =~ (^|[[:space:]])([^[:space:]]*/)?curl([[:space:]]|$) ]]; then
explanation="Command failed with exit status ${exit_code}"
fi
# Use msg_error if available, fallback to echo
# ── Telemetry / failure artifacts ──
if _is_container_context; then
_container_write_failure "$exit_code" "$command" "$line_number"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
fi
# ── Display ──
if declare -f msg_error >/dev/null 2>&1; then
msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}"
else
@@ -288,8 +361,7 @@ error_handler() {
} >>"$DEBUG_LOGFILE"
fi
# Get active log file (BUILD_LOG or INSTALL_LOG)
# Prefer silent()'s logfile when available (contains the actual command output)
# Get active log file (prefer silent()'s logfile when available)
local active_log=""
if [[ -n "${_SILENT_FAILED_LOG:-}" && -s "${_SILENT_FAILED_LOG}" ]]; then
active_log="$_SILENT_FAILED_LOG"
@@ -300,21 +372,17 @@ error_handler() {
active_log="$SILENT_LOGFILE"
fi
# If active_log points to a container-internal path that doesn't exist on host,
# fall back to BUILD_LOG (host-side log)
if [[ -n "$active_log" && ! -s "$active_log" && -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then
active_log="$BUILD_LOG"
fi
# Show last log lines if available
if [[ -n "$active_log" && -s "$active_log" ]]; then
echo -e "\n${TAB}--- Last 20 lines of log ---"
tail -n 20 "$active_log"
echo -e "${TAB}-----------------------------------\n"
fi
# Detect probable Node.js heap OOM and print actionable guidance.
# This avoids generic SIGABRT/SIGKILL confusion for frontend build failures.
# ── Node.js heap OOM detection with actionable guidance ──
local node_oom_detected="false"
local node_build_context="false"
if [[ "$command" =~ (npm|pnpm|yarn|node|vite|turbo) ]]; then
@@ -331,7 +399,6 @@ error_handler() {
if [[ "$node_oom_detected" == "true" ]] || { [[ "$node_build_context" == "true" ]] && [[ "$exit_code" =~ ^(134|137)$ ]]; }; then
local heap_hint_mb=""
# If explicitly configured, prefer the current value for troubleshooting output.
if [[ -n "${NODE_OPTIONS:-}" ]] && [[ "${NODE_OPTIONS}" =~ max-old-space-size=([0-9]+) ]]; then
heap_hint_mb="${BASH_REMATCH[1]}"
elif [[ -n "${var_ram:-}" ]] && [[ "${var_ram}" =~ ^[0-9]+$ ]]; then
@@ -358,14 +425,13 @@ error_handler() {
fi
fi
# ── Log-pattern analysis: detect common failure causes and emit actionable hints ──
# ── Log-pattern analysis: actionable hints for common failure causes ──
if [[ -n "$active_log" && -s "$active_log" ]]; then
local _log_tail
_log_tail=$(tail -n 60 "$active_log" 2>/dev/null || true)
# 1. APT/dpkg dependency conflict
if echo "$_log_tail" | grep -qE "Depends:|depends on.*but.*not installed|broken packages|unmet dep|dependency problems"; then
# Check for PostgreSQL-specific version mismatch (most actionable)
local _pg_conflict
_pg_conflict=$(echo "$_log_tail" | grep -oE 'postgresql-[0-9]+ but.*installed' | head -1 || true)
if [[ -n "$_pg_conflict" ]]; then
@@ -386,7 +452,7 @@ error_handler() {
msg_warn "Hint: A repository GPG key may be missing, expired, or the keyring file is not yet present (/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc etc.)."
msg_warn "Hint: Install the 'postgresql-common' package first, or re-add the repository with its correct signing key."
fi
# 3. Network / DNS failure during apt-get or curl
# 3. Network / DNS failure
elif echo "$_log_tail" | grep -qE "Could not resolve|Failed to fetch|Unable to connect|Name or service not known|Network is unreachable|curl.*resolve"; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "Network or DNS failure detected."
@@ -407,17 +473,12 @@ error_handler() {
fi
fi
# Detect context: Container (INSTALL_LOG set + inside container /root) vs Host
if [[ -n "${INSTALL_LOG:-}" && -f "${INSTALL_LOG:-}" && -d /root ]]; then
# CONTAINER CONTEXT: Copy log and create flag file for host
local container_log="/root/.install-${SESSION_ID:-error}.log"
cp "${INSTALL_LOG}" "$container_log" 2>/dev/null || true
# Create error flag file with exit code for host detection
echo "$exit_code" >"/root/.install-${SESSION_ID:-error}.failed" 2>/dev/null || true
# Log path is shown by host as combined log - no need to show container path
# ── Context-specific cleanup ──
if _is_container_context; then
# Container: artifacts already written above; nothing more to do
:
else
# HOST CONTEXT: Show local log path and offer container cleanup
# HOST: show log path and offer container cleanup
if [[ -n "$active_log" && -s "$active_log" ]]; then
if declare -f msg_custom >/dev/null 2>&1; then
msg_custom "📋" "${YW}" "Full log: ${active_log}"
@@ -435,7 +496,6 @@ error_handler() {
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
fi
# Read user response
local response=""
if read -t 60 -r response; then
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
@@ -476,12 +536,6 @@ error_handler() {
echo -e "${GN}${CL} Container ${CTID} removed"
fi
fi
# Force one final status update attempt after cleanup
# This ensures status is updated even if the first attempt failed (e.g., HTTP 400)
if declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" "force"
fi
fi
fi
@@ -489,106 +543,33 @@ error_handler() {
}
# ==============================================================================
# SECTION 3: TELEMETRY & CLEANUP HELPERS FOR SIGNAL HANDLERS
# SECTION 4: TELEMETRY & CLEANUP HELPERS FOR SIGNAL HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# _send_abort_telemetry()
# _send_abort_telemetry() (compatibility name)
#
# - Sends failure/abort status to telemetry API
# - Works in BOTH host context (post_update_to_api available) and
# container context (only curl available, api.func not sourced)
# - Container context is critical: without this, container-side failures
# and signal exits are never reported, leaving records stuck in
# "installing" or "configuring" forever
# - HOST: reports via post_update_to_api (signals map to "aborted" there)
# - CONTAINER: writes failure artifacts instead of sending anything
# - Arguments: $1 = exit_code
# ------------------------------------------------------------------------------
_send_abort_telemetry() {
local exit_code="${1:-1}"
# Try full API function first (host context - api.func sourced)
if _is_container_context; then
_container_write_failure "$exit_code"
return 0
fi
if declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
return
fi
# Fallback: direct curl (container context - api.func NOT sourced)
# This is the ONLY way containers can report failures to telemetry
command -v curl &>/dev/null || return 0
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
[[ -z "${RANDOM_UUID:-}" ]] && return 0
# Collect last 200 log lines for error diagnosis (best-effort)
# Container context has no get_full_log(), so we gather as much as possible
local error_text=""
local logfile=""
if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then
logfile="${INSTALL_LOG}"
elif [[ -n "${SILENT_LOGFILE:-}" && -s "${SILENT_LOGFILE}" ]]; then
logfile="${SILENT_LOGFILE}"
fi
if [[ -n "$logfile" ]]; then
error_text=$(tail -n 200 "$logfile" 2>/dev/null | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\\/\\\\/g; s/"/\\"/g; s/\r//g' | tr '\n' '|' | sed 's/|$//' | head -c 16384 | tr -d '\000-\010\013\014\016-\037\177') || true
fi
# Prepend exit code explanation header (like build_error_string does on host)
local explanation=""
if declare -f explain_exit_code &>/dev/null; then
explanation=$(explain_exit_code "$exit_code" 2>/dev/null) || true
fi
if [[ -n "$explanation" && -n "$error_text" ]]; then
error_text="exit_code=${exit_code} | ${explanation}|---|${error_text}"
elif [[ -n "$explanation" && -z "$error_text" ]]; then
error_text="exit_code=${exit_code} | ${explanation}"
fi
# Calculate duration if start time is available
local duration=""
if [[ -n "${DIAGNOSTICS_START_TIME:-}" ]]; then
duration=$(($(date +%s) - DIAGNOSTICS_START_TIME))
fi
# Categorize error if function is available (may not be in minimal container context)
local error_category=""
if declare -f categorize_error &>/dev/null; then
error_category=$(categorize_error "$exit_code" 2>/dev/null) || true
fi
# Build JSON payload with error context
local payload
payload="{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"${TELEMETRY_TYPE:-lxc}\",\"nsapp\":\"${NSAPP:-${app:-unknown}}\",\"status\":\"failed\",\"exit_code\":${exit_code}"
[[ -n "$error_text" ]] && payload="${payload},\"error\":\"${error_text}\""
[[ -n "$error_category" ]] && payload="${payload},\"error_category\":\"${error_category}\""
[[ -n "$duration" ]] && payload="${payload},\"duration\":${duration}"
payload="${payload}}"
local api_url="${TELEMETRY_URL:-https://telemetry.community-scripts.org/telemetry}"
# 2 attempts (retry once on failure) — original had no retry
local attempt
for attempt in 1 2; do
if curl -fsS -m 5 -X POST "$api_url" \
-H "Content-Type: application/json" \
-d "$payload" &>/dev/null; then
return 0
fi
[[ $attempt -eq 1 ]] && sleep 1
done
return 0
}
# ------------------------------------------------------------------------------
# _stop_container_if_installing()
#
# - Stops the LXC container if we're in the install phase
# - Stops the LXC container if we're in the install phase (host only)
# - Prevents orphaned container processes when the host exits due to a signal
# (SSH disconnect, Ctrl+C, SIGTERM) — without this, the container keeps
# running and may send "configuring" status AFTER the host already sent
# "failed", leaving records permanently stuck in "configuring"
# - Only acts when:
# * CONTAINER_INSTALLING flag is set (during lxc-attach in build_container)
# * CTID is set (container was created)
# * pct command is available (we're on the Proxmox host, not inside a container)
# - Does NOT destroy the container — just stops it for potential debugging
# ------------------------------------------------------------------------------
_stop_container_if_installing() {
[[ "${CONTAINER_INSTALLING:-}" == "true" ]] || return 0
@@ -598,51 +579,47 @@ _stop_container_if_installing() {
}
# ==============================================================================
# SECTION 4: SIGNAL HANDLERS
# SECTION 5: SIGNAL HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# on_exit()
#
# - EXIT trap handler — runs on EVERY script termination
# - Catches orphaned "installing"/"configuring" records:
# * If post_to_api sent "installing" but post_update_to_api never ran
# * Reports final status to prevent records stuck forever
# - Best-effort log collection for failed installs
# - Stops orphaned container processes on failure
# - Cleans up lock files
# - CONTAINER: ensures failure artifacts exist on non-zero exit (this also
# covers silent()'s direct `exit $rc`, which bypasses the ERR trap)
# - HOST: catches executions that never sent a final status:
# * non-zero exit → "failed" (signal codes map to "aborted")
# * zero exit with an "installing" record but no final → "aborted"
# (e.g. user cancelled a whiptail dialog, script exited cleanly)
# ------------------------------------------------------------------------------
on_exit() {
local exit_code=$?
# Report orphaned telemetry records
# Two scenarios handled:
# 1. POST_TO_API_DONE=true but POST_UPDATE_DONE=false: Record was created but
# never got a final status update → send abort/done now.
# 2. POST_TO_API_DONE=false but DIAGNOSTICS=yes: Initial post failed (server
# unreachable/timeout), but the server has fallback create-on-update logic,
# so a status update can still create the record. Worth one last try.
if [[ "${POST_UPDATE_DONE:-}" != "true" ]]; then
if [[ "${POST_TO_API_DONE:-}" == "true" || "${DIAGNOSTICS:-no}" == "yes" ]]; then
if _is_container_context; then
if [[ $exit_code -ne 0 ]]; then
_container_write_failure "$exit_code" "${FAILED_COMMAND:-}" "${FAILED_LINE:-}"
fi
else
if [[ "${POST_UPDATE_DONE:-}" != "true" ]] && declare -f post_update_to_api >/dev/null 2>&1; then
if [[ $exit_code -ne 0 ]]; then
_send_abort_telemetry "$exit_code"
elif [[ "${INSTALL_COMPLETE:-}" == "true" ]] && declare -f post_update_to_api >/dev/null 2>&1; then
# Only report success if the install was explicitly marked complete.
# Without this guard, early bailouts (e.g. user cancelled) with exit 0
# would be falsely reported as successful installations.
post_update_to_api "done" "0" 2>/dev/null || true
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
elif [[ "${POST_TO_API_DONE:-}" == "true" ]]; then
# Clean exit but no success was ever reported: the user backed out
# somewhere. Report as aborted so the record doesn't stay "installing".
post_update_to_api "aborted" "0" 2>/dev/null || true
fi
fi
fi
# Best-effort log collection on failure (non-critical, telemetry already sent)
if [[ $exit_code -ne 0 ]] && declare -f ensure_log_on_host >/dev/null 2>&1; then
ensure_log_on_host 2>/dev/null || true
fi
# Best-effort log collection on failure
if [[ $exit_code -ne 0 ]] && declare -f ensure_log_on_host >/dev/null 2>&1; then
ensure_log_on_host 2>/dev/null || true
fi
# Stop orphaned container if we're in the install phase and exiting with error
if [[ $exit_code -ne 0 ]]; then
_stop_container_if_installing
# Stop orphaned container if we're in the install phase
if [[ $exit_code -ne 0 ]]; then
_stop_container_if_installing
fi
fi
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
@@ -650,21 +627,19 @@ on_exit() {
}
# ------------------------------------------------------------------------------
# on_interrupt()
#
# - SIGINT (Ctrl+C) trap handler
# - Reports status FIRST (time-critical: container may be dying)
# - Stops orphaned container to prevent "configuring" ghost records
# - Exits with code 130 (128 + SIGINT=2)
# on_interrupt() - SIGINT (Ctrl+C)
# ------------------------------------------------------------------------------
on_interrupt() {
# Stop spinner and restore cursor before any output
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
printf "\e[?25h" 2>/dev/null || true
_send_abort_telemetry "130"
if _is_container_context; then
_container_write_failure "130"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "130" 2>/dev/null || true
fi
_stop_container_if_installing
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Interrupted by user (SIGINT)" 2>/dev/null || true
@@ -675,21 +650,19 @@ on_interrupt() {
}
# ------------------------------------------------------------------------------
# on_terminate()
#
# - SIGTERM trap handler
# - Reports status FIRST (time-critical: process being killed)
# - Stops orphaned container to prevent "configuring" ghost records
# - Exits with code 143 (128 + SIGTERM=15)
# on_terminate() - SIGTERM
# ------------------------------------------------------------------------------
on_terminate() {
# Stop spinner and restore cursor before any output
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
printf "\e[?25h" 2>/dev/null || true
_send_abort_telemetry "143"
if _is_container_context; then
_container_write_failure "143"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "143" 2>/dev/null || true
fi
_stop_container_if_installing
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Terminated by signal (SIGTERM)" 2>/dev/null || true
@@ -700,45 +673,32 @@ on_terminate() {
}
# ------------------------------------------------------------------------------
# on_hangup()
#
# - SIGHUP trap handler (SSH disconnect, terminal closed)
# - CRITICAL: This was previously MISSING from catch_errors(), causing
# container processes to become orphans on SSH disconnect — the #1 cause
# of records stuck in "installing" and "configuring" states
# - Reports status via direct curl (terminal is already closed, no output)
# - Stops orphaned container to prevent ghost records
# - Exits with code 129 (128 + SIGHUP=1)
# on_hangup() - SIGHUP (SSH disconnect, terminal closed)
# ------------------------------------------------------------------------------
on_hangup() {
# Stop spinner (no cursor restore needed — terminal is already gone)
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
_send_abort_telemetry "129"
if _is_container_context; then
_container_write_failure "129"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "129" 2>/dev/null || true
fi
_stop_container_if_installing
exit 129
}
# ==============================================================================
# SECTION 5: INITIALIZATION
# SECTION 6: INITIALIZATION
# ==============================================================================
# ------------------------------------------------------------------------------
# catch_errors()
#
# - Initializes error handling and signal traps
# - Enables strict error handling:
# * set -Ee: Exit on error, inherit ERR trap in functions
# * set -o pipefail: Pipeline fails if any command fails
# * set -u: (optional) Exit on undefined variable (if STRICT_UNSET=1)
# - Sets up traps:
# * ERR → error_handler (script errors)
# * EXIT → on_exit (any termination — cleanup + orphan detection)
# * INT → on_interrupt (Ctrl+C)
# * TERM → on_terminate (kill / systemd stop)
# * HUP → on_hangup (SSH disconnect / terminal closed)
# - set -Ee -o pipefail (+ set -u when STRICT_UNSET=1)
# - Traps: ERR → error_handler, EXIT → on_exit, INT/TERM/HUP → signal handlers
# - Call this function early in every script
# ------------------------------------------------------------------------------
catch_errors() {
+9 -1
View File
@@ -32,6 +32,11 @@ if ! command -v curl >/dev/null 2>&1; then
apt update >/dev/null 2>&1
apt install -y curl >/dev/null 2>&1
fi
# Mark container context BEFORE error handling starts: error_handler/on_exit
# must write local failure artifacts instead of talking to the telemetry API
# (the host is the single telemetry reporter).
export TELEMETRY_CONTEXT="container"
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
load_functions
@@ -65,9 +70,12 @@ post_progress_to_api() {
local progress_status="${1:-configuring}"
# Progress pings are the ONLY telemetry a container sends (terminal statuses
# are reported by the host). Include execution_id + platform + repo
# attribution (exported by the host) so the server can correlate and filter.
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
-H "Content-Type: application/json" \
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\",\"platform\":\"${TELEMETRY_PLATFORM:-}\",\"repo_source\":\"${REPO_SOURCE:-}\",\"repo_slug\":\"${REPO_SLUG:-}\"}" &>/dev/null || true
}
# ==============================================================================
+239 -453
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -573,8 +573,10 @@ cleanup() {
if [[ $exit_code -ne 0 ]]; then
post_update_to_api "failed" "$exit_code"
else
# Exited cleanly but description()/success was never called — shouldn't happen
post_update_to_api "failed" "1"
# Exited cleanly but description()/success was never called: the user
# backed out of a dialog. Report as aborted - NOT "failed 1" (which
# produced meaningless 'General error' records with no error text).
post_update_to_api "aborted" "0"
fi
fi
fi
+2 -2
View File
@@ -24,7 +24,7 @@ RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
METHOD=""
NSAPP="opnsense-vm"
var_os="opnsense"
var_version="26.1"
var_version="26.7"
#
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
@@ -814,7 +814,7 @@ if [ -n "$WAN_BRG" ]; then
msg_ok "WAN interface added"
sleep 5 # Brief pause after adding network interface
fi
send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r 26.1"
send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r 26.7"
msg_ok "OPNsense VM is being installed, do not close the terminal, or the installation will fail."
#We need to wait for the OPNsense build proccess to finish, this takes a few minutes
sleep 1000