Compare commits

..

16 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
7bb14b4d22 Update build.func 2026-02-24 14:03:09 +01:00
CanbiZ (MickLesk)
75c0b1bfc9 fix(error-handler): prevent silent() from re-enabling error handling during recovery
Root cause: silent() (core.func) unconditionally calls set -Eeuo pipefail
and trap 'error_handler' ERR after every command. When build_container()
intentionally disables error handling for its recovery section, any
intermediate call through silent()/ re-enables it. This causes the
grep/sed pipeline for missing_cmd extraction to trigger error_handler
(grep returns exit code 1 on no match + pipefail = fatal).

Fixes:
1. silent(): Save errexit state before disabling, only restore if it was
   active. Callers that intentionally disabled error handling (e.g.
   build_container recovery) are no longer silently re-enabled.

2. build.func: Add || true to missing_cmd grep pipeline as defense-in-depth
   against pipeline failure propagation.

3. build.func: Add explicit set +Eeuo pipefail / trap - ERR after
   post_update_to_api() call, before error classification grep/sed section.

4. build.func: Remove stale global combined_log variable from variables()
   that used a different path format (/tmp/install-SESSION-combined.log)
   than the actual local variable (/tmp/NSAPP-CTID-SESSION.log). The global
   was never written to and caused confusion when error_handler displayed it.
2026-02-24 13:51:27 +01:00
community-scripts-pr-app[bot]
73fd1dd11b chore: update github-versions.json (#12278)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 12:15:56 +00:00
community-scripts-pr-app[bot]
efcdd65c67 Update CHANGELOG.md (#12274)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 11:10:36 +00:00
CanbiZ (MickLesk)
8b15af7499 ci: add weekly Node.js version drift check workflow (#12267)
Scans all install scripts using setup_nodejs and compares our
NODE_VERSION with upstream Dockerfile and package.json values.

Features:
- Detects FROM node:XX, nodesource/setup_XX, FROM alpine:X.Y
- Resolves Alpine package registry for nodejs version when
  upstream uses alpine base images
- Caches Alpine version lookups to minimize requests
- Creates individual GitHub issues per script with investigation
  checklist when drift is detected
- Rate-limited to avoid GitHub API throttling
- Runs weekly on Monday at 06:00 UTC + manual dispatch
2026-02-24 12:10:11 +01:00
community-scripts-pr-app[bot]
13daffdeed Update .app files (#12271)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-24 12:01:46 +01:00
community-scripts-pr-app[bot]
fd82f5b496 Update CHANGELOG.md (#12273)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:54:29 +00:00
CanbiZ (MickLesk)
83f03d617e Refactor n8n (#12264) 2026-02-24 11:54:02 +01:00
community-scripts-pr-app[bot]
4c5d5b2030 Update CHANGELOG.md (#12272)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:38:42 +00:00
CanbiZ (MickLesk)
4e1ade4c28 bump various scripts from Node 22 to 24 (#12265)
* bump(node): Node 22 to 24 for Tier 1 projects

Bump Node.js from 22 to 24 in install and ct update scripts for projects
where upstream already uses Node 24 in their Dockerfile/package.json:

- Cross-seed (engines >=24, Dockerfile node:24-alpine)
- Matterbridge (engines >=24.0.0, Dockerfile node:24-trixie-slim)
- Manyfold (volta.node 24.13.0)
- Pangolin (Dockerfile node:24-alpine)
- ROMM (engines 24, nvm install 24)
- SnowShare (Dockerfile node:24-alpine)
- Verdaccio (engines >=18, Dockerfile node:24-alpine)

Also adds missing NODE_VERSION setup_nodejs calls to ct update scripts
for cross-seed, pangolin, romm, snowshare, and manyfold.

* formatting

* fix: pin NODE_VERSION for nodecast-tv and wealthfolio

nodecast-tv: NODE_VERSION=20 (upstream Dockerfile uses nodesource/setup_20.x)
wealthfolio: NODE_VERSION=20 (upstream Dockerfile uses node:20-alpine)

* ci: add weekly Node.js version drift check workflow

Scans all install scripts using setup_nodejs, fetches upstream
package.json (engines.node) and Dockerfile (FROM node:XX),
compares with our NODE_VERSION and opens/updates a GitHub issue
on mismatch. Runs weekly on Monday at 06:00 UTC.

* remove wf - seperate branch
2026-02-24 11:38:15 +01:00
community-scripts-pr-app[bot]
e5073d1a4f Update CHANGELOG.md (#12270)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:35:32 +00:00
community-scripts-pr-app[bot]
01e956bdf8 Update CHANGELOG.md (#12269)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:35:01 +00:00
community-scripts-pr-app[bot]
0cc049edb6 Update date in json (#12268)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:34:55 +00:00
push-app-to-main[bot]
ffa1a26b5e Arcane (#12263)
* Add arcane (addon)

* Update tools/addon/arcane.sh

Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com>

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com>
2026-02-24 11:34:33 +01:00
community-scripts-pr-app[bot]
59ca9c26c9 Update CHANGELOG.md (#12266)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-24 10:08:03 +00:00
CanbiZ (MickLesk)
56de2d1e39 feat(tools): add get_latest_gh_tag helper function (#12261)
Adds a reusable function to fetch the latest tag from a GitHub repo.
Useful for projects that only use tags, not full releases (e.g.
mongodb/mongo-tools).

Features:
- Optional prefix filter (e.g. '100.' or 'v')
- Optional prefix stripping for clean version output
- Skips pre-release tags (rc, alpha, beta, dev, test)
- Sorts by version (sort -V) to find the latest
- Respects GITHUB_TOKEN for rate limiting
2026-02-24 11:07:40 +01:00
26 changed files with 483 additions and 98 deletions

View File

@@ -415,15 +415,31 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- Refactor n8n [@MickLesk](https://github.com/MickLesk) ([#12264](https://github.com/community-scripts/ProxmoxVE/pull/12264))
- Firefly: PHP bump [@tremor021](https://github.com/tremor021) ([#12247](https://github.com/community-scripts/ProxmoxVE/pull/12247)) - Firefly: PHP bump [@tremor021](https://github.com/tremor021) ([#12247](https://github.com/community-scripts/ProxmoxVE/pull/12247))
- #### ✨ New Features - #### ✨ New Features
- make searxng updateable [@shtefko](https://github.com/shtefko) ([#12207](https://github.com/community-scripts/ProxmoxVE/pull/12207)) - make searxng updateable [@shtefko](https://github.com/shtefko) ([#12207](https://github.com/community-scripts/ProxmoxVE/pull/12207))
- #### 🔧 Refactor
- bump various scripts from Node 22 to 24 [@MickLesk](https://github.com/MickLesk) ([#12265](https://github.com/community-scripts/ProxmoxVE/pull/12265))
### 💾 Core
- #### ✨ New Features
- tools.func: add get_latest_gh_tag helper function [@MickLesk](https://github.com/MickLesk) ([#12261](https://github.com/community-scripts/ProxmoxVE/pull/12261))
### 🧰 Tools
- Arcane ([#12263](https://github.com/community-scripts/ProxmoxVE/pull/12263))
### 📂 Github ### 📂 Github
- add: workflow to close stale PRs [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12243](https://github.com/community-scripts/ProxmoxVE/pull/12243)) - github: add weekly Node.js version drift check workflow [@MickLesk](https://github.com/MickLesk) ([#12267](https://github.com/community-scripts/ProxmoxVE/pull/12267))
- add: workflow to close stale PRs [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12243](https://github.com/community-scripts/ProxmoxVE/pull/12243))
## 2026-02-23 ## 2026-02-23

View File

@@ -24,6 +24,8 @@ function update_script() {
check_container_storage check_container_storage
check_container_resources check_container_resources
NODE_VERSION="24" setup_nodejs
if command -v cross-seed &>/dev/null; then if command -v cross-seed &>/dev/null; then
current_version=$(cross-seed --version) current_version=$(cross-seed --version)
latest_version=$(npm show cross-seed version) latest_version=$(npm show cross-seed version)

View File

@@ -28,6 +28,8 @@ function update_script() {
exit exit
fi fi
NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then if check_for_gh_release "manyfold" "manyfold3d/manyfold"; then
msg_info "Stopping Services" msg_info "Stopping Services"
systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1 systemctl stop manyfold.target manyfold-rails.1 manyfold-default_worker.1 manyfold-performance_worker.1

View File

@@ -29,7 +29,7 @@ function update_script() {
fi fi
$STD apt update $STD apt update
$STD apt upgrade -y $STD apt upgrade -y
NODE_VERSION="22" NODE_MODULE="matterbridge" setup_nodejs NODE_VERSION="24" NODE_MODULE="matterbridge" setup_nodejs
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
exit exit
} }

View File

@@ -27,7 +27,11 @@ function update_script() {
msg_error "No ${APP} Installation Found!" msg_error "No ${APP} Installation Found!"
exit exit
fi fi
ensure_dependencies graphicsmagick
ensure_dependencies graphicsmagick
NODE_VERSION="24" setup_nodejs
msg_info "Updating n8n"
if [ ! -f /opt/n8n.env ]; then if [ ! -f /opt/n8n.env ]; then
sed -i 's|^Environment="N8N_SECURE_COOKIE=false"$|EnvironmentFile=/opt/n8n.env|' /etc/systemd/system/n8n.service sed -i 's|^Environment="N8N_SECURE_COOKIE=false"$|EnvironmentFile=/opt/n8n.env|' /etc/systemd/system/n8n.service
mkdir -p /opt mkdir -p /opt
@@ -37,14 +41,12 @@ N8N_PORT=5678
N8N_PROTOCOL=http N8N_PROTOCOL=http
N8N_HOST=$LOCAL_IP N8N_HOST=$LOCAL_IP
EOF EOF
systemctl daemon-reload
fi fi
NODE_VERSION="22" setup_nodejs $STD npm update -g n8n
msg_info "Updating ${APP} LXC"
rm -rf /usr/lib/node_modules/.n8n-* /usr/lib/node_modules/n8n
$STD npm install -g n8n --force
systemctl restart n8n systemctl restart n8n
msg_ok "Updated n8n"
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
exit exit
} }

View File

@@ -29,6 +29,8 @@ function update_script() {
exit exit
fi fi
NODE_VERSION="24" setup_nodejs
if check_for_gh_release "pangolin" "fosrl/pangolin"; then if check_for_gh_release "pangolin" "fosrl/pangolin"; then
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop pangolin systemctl stop pangolin

View File

@@ -29,6 +29,8 @@ function update_script() {
exit exit
fi fi
NODE_VERSION="24" setup_nodejs
if check_for_gh_release "romm" "rommapp/romm"; then if check_for_gh_release "romm" "rommapp/romm"; then
msg_info "Stopping Services" msg_info "Stopping Services"
systemctl stop romm-backend romm-worker romm-scheduler romm-watcher systemctl stop romm-backend romm-worker romm-scheduler romm-watcher

View File

@@ -28,6 +28,8 @@ function update_script() {
exit exit
fi fi
NODE_VERSION="24" setup_nodejs
if check_for_gh_release "snowshare" "TuroYT/snowshare"; then if check_for_gh_release "snowshare" "TuroYT/snowshare"; then
msg_info "Stopping Service" msg_info "Stopping Service"
systemctl stop snowshare systemctl stop snowshare

View File

@@ -33,7 +33,7 @@ function update_script() {
$STD apt upgrade -y $STD apt upgrade -y
msg_ok "Updated LXC Container" msg_ok "Updated LXC Container"
NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs NODE_VERSION="24" NODE_MODULE="verdaccio" setup_nodejs
systemctl restart verdaccio systemctl restart verdaccio
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
exit exit

View File

@@ -0,0 +1,40 @@
{
"name": "Arcane",
"slug": "arcane",
"categories": [
3
],
"date_created": "2026-02-24",
"type": "addon",
"updateable": true,
"privileged": false,
"interface_port": 3552,
"documentation": "https://getarcane.app/docs",
"website": "https://getarcane.app/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/arcane.webp",
"config_path": "/opt/arcane/.env",
"description": "Arcane is designed to be an easy and modern Docker management platform, built with everybody in mind. The goal of Arcane is to be built for and by the community to make sure nobody feels left out or behind with their specific features or processes. ",
"install_methods": [
{
"type": "default",
"script": "tools/addon/arcane.sh",
"resources": {
"cpu": null,
"ram": null,
"hdd": null,
"os": null,
"version": null
}
}
],
"default_credentials": {
"username": "arcane",
"password": "arcane-admin"
},
"notes": [
{
"text": "This is an addon script intended to be used on top of an existing Docker container.",
"type": "info"
}
]
}

View File

@@ -1,5 +1,5 @@
{ {
"generated": "2026-02-24T06:23:39Z", "generated": "2026-02-24T12:15:44Z",
"versions": [ "versions": [
{ {
"slug": "2fauth", "slug": "2fauth",
@@ -606,9 +606,9 @@
{ {
"slug": "invoiceninja", "slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja", "repo": "invoiceninja/invoiceninja",
"version": "v5.12.65", "version": "v5.12.66",
"pinned": false, "pinned": false,
"date": "2026-02-21T01:03:52Z" "date": "2026-02-24T09:12:50Z"
}, },
{ {
"slug": "jackett", "slug": "jackett",
@@ -1679,7 +1679,7 @@
"repo": "meilisearch/meilisearch", "repo": "meilisearch/meilisearch",
"version": "v1.36.0", "version": "v1.36.0",
"pinned": false, "pinned": false,
"date": "2026-02-23T08:13:32Z" "date": ""
}, },
{ {
"slug": "warracker", "slug": "warracker",

View File

@@ -13,7 +13,7 @@ setting_up_container
network_check network_check
update_os update_os
NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs
msg_info "Setup Cross-Seed" msg_info "Setup Cross-Seed"
$STD npm install cross-seed@latest -g $STD npm install cross-seed@latest -g

View File

@@ -26,7 +26,7 @@ msg_ok "Installed Dependencies"
setup_imagemagick setup_imagemagick
PG_VERSION="16" setup_postgresql PG_VERSION="16" setup_postgresql
PG_DB_NAME="manyfold" PG_DB_USER="manyfold" setup_postgresql_db PG_DB_NAME="manyfold" PG_DB_USER="manyfold" setup_postgresql_db
NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs
fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app" fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold/app"

View File

@@ -15,7 +15,7 @@ update_os
msg_info "Install Matterbridge" msg_info "Install Matterbridge"
mkdir -p /root/Matterbridge mkdir -p /root/Matterbridge
NODE_VERSION="22" NODE_MODULE="matterbridge" setup_nodejs NODE_VERSION="24" NODE_MODULE="matterbridge" setup_nodejs
msg_ok "Installed Matterbridge" msg_ok "Installed Matterbridge"
msg_info "Creating Service" msg_info "Creating Service"

View File

@@ -15,22 +15,19 @@ update_os
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
$STD apt install -y \ $STD apt install -y \
ca-certificates \
build-essential \ build-essential \
python3 \ python3 \
python3-setuptools \ python3-setuptools \
graphicsmagick graphicsmagick
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs
msg_info "Installing n8n (Patience)" msg_info "Installing n8n (Patience)"
$STD npm install --global patch-package $STD npm install -g n8n
$STD npm install --global n8n
msg_ok "Installed n8n" msg_ok "Installed n8n"
msg_info "Creating Service" msg_info "Creating Service"
mkdir -p /opt
cat <<EOF >/opt/n8n.env cat <<EOF >/opt/n8n.env
N8N_SECURE_COOKIE=false N8N_SECURE_COOKIE=false
N8N_PORT=5678 N8N_PORT=5678

View File

@@ -14,7 +14,7 @@ network_check
update_os update_os
fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv" fetch_and_deploy_gh_release "nodecast-tv" "technomancer702/nodecast-tv"
setup_nodejs NODE_VERSION="20" setup_nodejs
msg_info "Installing Dependencies" msg_info "Installing Dependencies"
$STD apt install -y ffmpeg $STD apt install -y ffmpeg

View File

@@ -19,7 +19,7 @@ $STD apt install -y \
iptables iptables
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball"
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64" fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64"
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_amd64.tar.gz" fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_amd64.tar.gz"

View File

@@ -42,7 +42,7 @@ $STD apt install -y \
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
PYTHON_VERSION="3.13" setup_uv PYTHON_VERSION="3.13" setup_uv
NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs
setup_mariadb setup_mariadb
MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db

View File

@@ -13,7 +13,7 @@ setting_up_container
network_check network_check
update_os update_os
NODE_VERSION="22" setup_nodejs NODE_VERSION="24" setup_nodejs
PG_VERSION="17" setup_postgresql PG_VERSION="17" setup_postgresql
PG_DB_USER="snowshare" PG_DB_NAME="snowshare" setup_postgresql_db PG_DB_USER="snowshare" PG_DB_NAME="snowshare" setup_postgresql_db
fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" "tarball" fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" "tarball"

View File

@@ -17,7 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y build-essential $STD apt install -y build-essential
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs NODE_VERSION="24" NODE_MODULE="verdaccio" setup_nodejs
msg_info "Configuring Verdaccio" msg_info "Configuring Verdaccio"
mkdir -p /opt/verdaccio/config mkdir -p /opt/verdaccio/config

View File

@@ -23,7 +23,7 @@ $STD apt install -y \
msg_ok "Installed Dependencies" msg_ok "Installed Dependencies"
setup_rust setup_rust
NODE_MODULE="pnpm" setup_nodejs NODE_VERSION="20" NODE_MODULE="pnpm" setup_nodejs
fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball" fetch_and_deploy_gh_release "wealthfolio" "afadil/wealthfolio" "tarball"
msg_info "Building Frontend (patience)" msg_info "Building Frontend (patience)"

View File

@@ -48,7 +48,8 @@ variables() {
EXECUTION_ID="${RANDOM_UUID}" # Unique execution ID for telemetry record identification (unique-indexed in PocketBase) EXECUTION_ID="${RANDOM_UUID}" # Unique execution ID for telemetry record identification (unique-indexed in PocketBase)
SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files
BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log
combined_log="/tmp/install-${SESSION_ID}-combined.log" # Combined log (build + install) for failed installations # NOTE: combined_log is constructed locally in build_container() and ensure_log_on_host()
# as "/tmp/${NSAPP}-${CTID}-${SESSION_ID}.log" (requires CTID, not available here)
CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"
# Parse dev_mode early # Parse dev_mode early
@@ -1850,7 +1851,7 @@ advanced_settings() {
# ═══════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════
# STEP 2: Root Password # STEP 2: Root Password
# ══════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
2) 2)
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \ if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
--title "ROOT PASSWORD" \ --title "ROOT PASSWORD" \
@@ -4174,6 +4175,13 @@ EOF'
# Report failure to telemetry API (now with log available on host) # Report failure to telemetry API (now with log available on host)
post_update_to_api "failed" "$install_exit_code" post_update_to_api "failed" "$install_exit_code"
# Defense-in-depth: Ensure error handling stays disabled during recovery.
# Some functions (e.g. silent/$STD) unconditionally re-enable set -Eeuo pipefail
# and trap 'error_handler' ERR. If any code path above called such a function,
# the grep/sed pipelines below would trigger error_handler on non-match (exit 1).
set +Eeuo pipefail
trap - ERR
# Show combined log location # Show combined log location
if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then
msg_custom "📋" "${YW}" "Installation log: ${combined_log}" msg_custom "📋" "${YW}" "Installation log: ${combined_log}"
@@ -4287,7 +4295,7 @@ EOF'
if [[ "$is_cmd_not_found" == true ]]; then if [[ "$is_cmd_not_found" == true ]]; then
local missing_cmd="" local missing_cmd=""
if [[ -f "$combined_log" ]]; then if [[ -f "$combined_log" ]]; then
missing_cmd=$(grep -oiE '[a-zA-Z0-9_.-]+: command not found' "$combined_log" | tail -1 | sed 's/: command not found//') missing_cmd=$(grep -oiE '[a-zA-Z0-9_.-]+: command not found' "$combined_log" 2>/dev/null | tail -1 | sed 's/: command not found//') || true
fi fi
if [[ -n "$missing_cmd" ]]; then if [[ -n "$missing_cmd" ]]; then
echo -e "${TAB}${INFO} Missing command: ${GN}${missing_cmd}${CL}" echo -e "${TAB}${INFO} Missing command: ${GN}${missing_cmd}${CL}"

View File

@@ -490,6 +490,8 @@ log_section() {
# - Executes command with output redirected to active log file # - Executes command with output redirected to active log file
# - On error: displays last 20 lines of log and exits with original exit code # - On error: displays last 20 lines of log and exits with original exit code
# - Temporarily disables error trap to capture exit code correctly # - Temporarily disables error trap to capture exit code correctly
# - Saves and restores previous error handling state (so callers that
# intentionally disabled error handling aren't silently re-enabled)
# - Sources explain_exit_code() for detailed error messages # - Sources explain_exit_code() for detailed error messages
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
silent() { silent() {
@@ -507,14 +509,23 @@ silent() {
return 0 return 0
fi fi
# Save current error handling state before disabling
# This prevents re-enabling error handling when the caller intentionally
# disabled it (e.g. build_container recovery section)
local _restore_errexit=false
[[ "$-" == *e* ]] && _restore_errexit=true
set +Eeuo pipefail set +Eeuo pipefail
trap - ERR trap - ERR
"$@" >>"$logfile" 2>&1 "$@" >>"$logfile" 2>&1
local rc=$? local rc=$?
# Restore error handling ONLY if it was active before this call
if $_restore_errexit; then
set -Eeuo pipefail set -Eeuo pipefail
trap 'error_handler' ERR trap 'error_handler' ERR
fi
if [[ $rc -ne 0 ]]; then if [[ $rc -ne 0 ]]; then
# Source explain_exit_code if needed # Source explain_exit_code if needed

View File

@@ -1525,6 +1525,82 @@ verify_gpg_fingerprint() {
return 1 return 1
} }
# ------------------------------------------------------------------------------
# Get latest GitHub tag for a repository.
#
# Description:
# - Queries the GitHub API for tags (not releases)
# - Useful for repos that only create tags, not full releases
# - Supports optional prefix filter and version-only extraction
# - Returns the latest tag name (printed to stdout)
#
# Usage:
# MONGO_VERSION=$(get_latest_gh_tag "mongodb/mongo-tools")
# LATEST=$(get_latest_gh_tag "owner/repo" "v") # only tags starting with "v"
# LATEST=$(get_latest_gh_tag "owner/repo" "" "true") # strip leading "v"
#
# Arguments:
# $1 - GitHub repo (owner/repo)
# $2 - Tag prefix filter (optional, e.g. "v" or "100.")
# $3 - Strip prefix from result (optional, "true" to strip $2 prefix)
#
# Returns:
# 0 on success (tag printed to stdout), 1 on failure
#
# Notes:
# - Skips tags containing "rc", "alpha", "beta", "dev", "test"
# - Sorts by version number (sort -V) to find the latest
# - Respects GITHUB_TOKEN for rate limiting
# ------------------------------------------------------------------------------
get_latest_gh_tag() {
local repo="$1"
local prefix="${2:-}"
local strip_prefix="${3:-false}"
local header_args=()
[[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN")
local http_code=""
http_code=$(curl -sSL --max-time 20 -w "%{http_code}" -o /tmp/gh_tags.json \
-H 'Accept: application/vnd.github+json' \
-H 'X-GitHub-Api-Version: 2022-11-28' \
"${header_args[@]}" \
"https://api.github.com/repos/${repo}/tags?per_page=100" 2>/dev/null) || true
if [[ "$http_code" == "403" ]]; then
msg_warn "GitHub API rate limit exceeded while fetching tags for ${repo}"
rm -f /tmp/gh_tags.json
return 1
fi
if [[ "$http_code" != "200" ]] || [[ ! -s /tmp/gh_tags.json ]]; then
rm -f /tmp/gh_tags.json
return 1
fi
local tags_json
tags_json=$(</tmp/gh_tags.json)
rm -f /tmp/gh_tags.json
# Extract tag names, filter by prefix, exclude pre-release patterns, sort by version
local latest=""
latest=$(echo "$tags_json" | grep -oP '"name":\s*"\K[^"]+' |
{ [[ -n "$prefix" ]] && grep "^${prefix}" || cat; } |
grep -viE '(rc|alpha|beta|dev|test|preview|snapshot)' |
sort -V | tail -n1)
if [[ -z "$latest" ]]; then
return 1
fi
if [[ "$strip_prefix" == "true" && -n "$prefix" ]]; then
latest="${latest#"$prefix"}"
fi
echo "$latest"
return 0
}
# ============================================================================== # ==============================================================================
# INSTALL FUNCTIONS # INSTALL FUNCTIONS
# ============================================================================== # ==============================================================================

219
tools/addon/arcane.sh Normal file
View File

@@ -0,0 +1,219 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: summoningpixels
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/getarcaneapp/arcane
if ! command -v curl &>/dev/null; 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)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
# Enable error handling
set -Eeuo pipefail
trap 'error_handler' ERR
# ==============================================================================
# CONFIGURATION
# ==============================================================================
APP="Arcane"
APP_TYPE="addon"
INSTALL_PATH="/opt/arcane"
COMPOSE_FILE="${INSTALL_PATH}/compose.yaml"
ENV_FILE="${INSTALL_PATH}/.env"
DEFAULT_PORT=3552
# Initialize all core functions (colors, formatting, icons, STD mode)
load_functions
# ==============================================================================
# HEADER
# ==============================================================================
function header_info {
clear
cat <<"EOF"
___ ____ _________ _ ________
/ | / __ \/ ____/ | / | / / ____/
/ /| | / /_/ / / / /| | / |/ / __/
/ ___ |/ _, _/ /___/ ___ |/ /| / /___
/_/ |_/_/ |_|\____/_/ |_/_/ |_/_____/
EOF
}
# ==============================================================================
# UNINSTALL
# ==============================================================================
function uninstall() {
msg_info "Uninstalling ${APP}"
if [[ -f "$COMPOSE_FILE" ]]; then
msg_info "Stopping and removing Docker containers"
cd "$INSTALL_PATH"
$STD docker compose down --volumes --remove-orphans
msg_ok "Stopped and removed Docker containers"
fi
rm -rf "$INSTALL_PATH"
rm -f "/usr/local/bin/update_arcane"
msg_ok "${APP} has been uninstalled"
}
# ==============================================================================
# UPDATE
# ==============================================================================
function update() {
msg_info "Pulling latest ${APP} image"
cd "$INSTALL_PATH"
$STD docker compose pull
msg_ok "Pulled latest image"
msg_info "Restarting ${APP}"
$STD docker compose up -d --remove-orphans
msg_ok "Restarted ${APP}"
msg_ok "Updated successfully"
exit
}
# ==============================================================================
# CHECK DOCKER
# ==============================================================================
function check_docker() {
if ! command -v docker &>/dev/null; then
msg_error "Docker is not installed. This script requires an existing Docker LXC. Exiting."
exit 1
fi
if ! docker compose version &>/dev/null; then
msg_error "Docker Compose plugin is not available. Please install it before running this script. Exiting."
exit 1
fi
msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available"
}
# ==============================================================================
# INSTALL
# ==============================================================================
function install() {
check_docker
msg_info "Creating install directory"
mkdir -p "$INSTALL_PATH"
msg_ok "Created ${INSTALL_PATH}"
# Generate secrets and config values
local ENCRYPTION_KEY JWT_SECRET PROJ_DIR
ENCRYPTION_KEY=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32)
JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c32)
PROJ_DIR="/etc/arcane/projects"
msg_info "Creating stacks directory"
mkdir -p "$PROJ_DIR"
msg_ok "Created ${PROJ_DIR}"
msg_info "Downloading Docker Compose file"
curl -fsSL "https://raw.githubusercontent.com/getarcaneapp/arcane/refs/heads/main/docker/examples/compose.basic.yaml" -o "$COMPOSE_FILE"
msg_ok "Downloaded Docker Compose file"
msg_info "Downloading .env file"
curl -fsSL "https://raw.githubusercontent.com/getarcaneapp/arcane/refs/heads/main/.env.example" -o "$ENV_FILE"
chmod 600 "$ENV_FILE"
msg_ok "Downloaded .env file"
msg_info "Configuring compose and env files"
sed -i '/^[[:space:]]*#/!s|/host/path/to/projects|'"$PROJ_DIR"'|g' "$COMPOSE_FILE"
sed -i '/^[[:space:]]*#/!s|ENCRYPTION_KEY=.*|ENCRYPTION_KEY='"$ENCRYPTION_KEY"'|g' "$COMPOSE_FILE"
sed -i '/^[[:space:]]*#/!s|JWT_SECRET=.*|JWT_SECRET='"$JWT_SECRET"'|g' "$COMPOSE_FILE"
sed -i '/^[[:space:]]*#/!s|APP_URL=.*|APP_URL=http://localhost:'"$DEFAULT_PORT"'|g' "$ENV_FILE"
sed -i '/^[[:space:]]*#/!s|ENCRYPTION_KEY=.*|#&|g' "$ENV_FILE"
sed -i '/^[[:space:]]*#/!s|JWT_SECRET=.*|#&|g' "$ENV_FILE"
msg_ok "Configured compose and env files"
msg_info "Starting ${APP}"
cd "$INSTALL_PATH"
$STD docker compose up -d
msg_ok "Started ${APP}"
# Create update script
msg_info "Creating update script"
cat <<'UPDATEEOF' >/usr/local/bin/update_arcane
#!/usr/bin/env bash
# Arcane Update Script
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/arcane.sh)"
UPDATEEOF
chmod +x /usr/local/bin/update_arcane
msg_ok "Created update script (/usr/local/bin/update_arcane)"
echo ""
msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
echo ""
echo -e "Arcane Credentials"
echo -e "=================="
echo -e "User: arcane"
echo -e "Password: arcane-admin"
echo ""
msg_warn "On first access, you'll be prompted to change your password."
}
# ==============================================================================
# MAIN
# ==============================================================================
# Handle type=update (called from update script)
if [[ "${type:-}" == "update" ]]; then
header_info
if [[ -f "$COMPOSE_FILE" ]]; then
update
else
msg_error "${APP} is not installed. Nothing to update."
exit 1
fi
exit 0
fi
header_info
get_lxc_ip
# Check if already installed
if [[ -f "$COMPOSE_FILE" ]]; then
msg_warn "${APP} is already installed."
echo ""
echo -n "${TAB}Uninstall ${APP}? (y/N): "
read -r uninstall_prompt
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
uninstall
exit 0
fi
echo -n "${TAB}Update ${APP}? (y/N): "
read -r update_prompt
if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then
update
exit 0
fi
msg_warn "No action selected. Exiting."
exit 0
fi
# Fresh installation
msg_warn "${APP} is not installed."
echo ""
echo -e "${TAB}${INFO} This will install:"
echo -e "${TAB} - Arcane (via Docker Compose)"
echo ""
echo -n "${TAB}Install ${APP}? (y/N): "
read -r install_prompt
if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
install
else
msg_warn "Installation cancelled. Exiting."
exit 0
fi

6
tools/headers/arcane Normal file
View File

@@ -0,0 +1,6 @@
___
/ | ______________ _____ ___
/ /| | / ___/ ___/ __ `/ __ \/ _ \
/ ___ |/ / / /__/ /_/ / / / / __/
/_/ |_/_/ \___/\__,_/_/ /_/\___/