mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-16 18:23:27 +01:00
Compare commits
1 Commits
feature/sm
...
hf_zabbix_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50731fc941 |
3
.github/workflows/autolabeler.yml
generated
vendored
3
.github/workflows/autolabeler.yml
generated
vendored
@@ -100,8 +100,7 @@ jobs:
|
||||
// If it's an update script PR with json changes and a content label, skip adding website/json
|
||||
// The PR should be categorized as update script with the content label
|
||||
if (!(hasUpdateScript && hasJson && hasContentLabel)) {
|
||||
labelsToAdd.add("website");
|
||||
if (hasJson) labelsToAdd.add("json");
|
||||
labelsToAdd.add(hasJson ? "json" : "website");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -406,47 +406,12 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
## 2026-02-16
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- RomM ([#11987](https://github.com/community-scripts/ProxmoxVE/pull/11987))
|
||||
- LinkDing ([#11976](https://github.com/community-scripts/ProxmoxVE/pull/11976))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Tududi: Fix sed command for DB_FILE configuration [@tremor021](https://github.com/tremor021) ([#11988](https://github.com/community-scripts/ProxmoxVE/pull/11988))
|
||||
- slskd: fix exit position [@MickLesk](https://github.com/MickLesk) ([#11963](https://github.com/community-scripts/ProxmoxVE/pull/11963))
|
||||
- cryptpad: restore config earlier and run onlyoffice upgrade [@MickLesk](https://github.com/MickLesk) ([#11964](https://github.com/community-scripts/ProxmoxVE/pull/11964))
|
||||
- jellyseerr/overseerr: Migrate update script to Seerr; prompt rerun [@MickLesk](https://github.com/MickLesk) ([#11965](https://github.com/community-scripts/ProxmoxVE/pull/11965))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- core/vm's: ensure script state is sent on script exit [@MickLesk](https://github.com/MickLesk) ([#11991](https://github.com/community-scripts/ProxmoxVE/pull/11991))
|
||||
- Vaultwarden: export VW_VERSION as version number [@MickLesk](https://github.com/MickLesk) ([#11966](https://github.com/community-scripts/ProxmoxVE/pull/11966))
|
||||
- Zabbix: Improve zabbix-agent service detection [@MickLesk](https://github.com/MickLesk) ([#11968](https://github.com/community-scripts/ProxmoxVE/pull/11968))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- tools.func: ensure /usr/local/bin PATH persists for pct enter sessions [@MickLesk](https://github.com/MickLesk) ([#11970](https://github.com/community-scripts/ProxmoxVE/pull/11970))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- core: remove duplicate error handler from alpine-install.func [@MickLesk](https://github.com/MickLesk) ([#11971](https://github.com/community-scripts/ProxmoxVE/pull/11971))
|
||||
|
||||
### 📂 Github
|
||||
|
||||
- github: add "website" label if "json" changed [@MickLesk](https://github.com/MickLesk) ([#11975](https://github.com/community-scripts/ProxmoxVE/pull/11975))
|
||||
|
||||
### 🌐 Website
|
||||
|
||||
- #### 📝 Script Information
|
||||
|
||||
- Update Wishlist LXC webpage to include reverse proxy info [@summoningpixels](https://github.com/summoningpixels) ([#11973](https://github.com/community-scripts/ProxmoxVE/pull/11973))
|
||||
- Update OpenCloud LXC webpage to include services ports [@summoningpixels](https://github.com/summoningpixels) ([#11969](https://github.com/community-scripts/ProxmoxVE/pull/11969))
|
||||
|
||||
## 2026-02-15
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
@@ -27,7 +27,7 @@ function update_script() {
|
||||
exit
|
||||
fi
|
||||
|
||||
RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1')
|
||||
set +o pipefail && RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | head -1) && set -o pipefail
|
||||
|
||||
if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then
|
||||
msg_info "Updating ${APP} LXC"
|
||||
|
||||
@@ -39,20 +39,17 @@ function update_script() {
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cryptpad" "cryptpad/cryptpad" "tarball"
|
||||
|
||||
msg_info "Restoring configuration"
|
||||
mv /opt/config.js /opt/cryptpad/config/
|
||||
msg_ok "Configuration restored"
|
||||
|
||||
msg_info "Updating CryptaPad"
|
||||
cd /opt/cryptpad
|
||||
$STD npm ci
|
||||
$STD npm run install:components
|
||||
if [ -f "/opt/cryptpad/install-onlyoffice.sh" ]; then
|
||||
$STD bash /opt/cryptpad/install-onlyoffice.sh --accept-license
|
||||
fi
|
||||
$STD npm run build
|
||||
msg_ok "Updated CryptaPad"
|
||||
|
||||
msg_info "Restoring configuration"
|
||||
mv /opt/config.js /opt/cryptpad/config/
|
||||
msg_ok "Configuration restored"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start cryptpad
|
||||
msg_ok "Started Service"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
___ __ ___
|
||||
/ (_)___ / /______/ (_)___ ____ _
|
||||
/ / / __ \/ //_/ __ / / __ \/ __ `/
|
||||
/ / / / / / ,< / /_/ / / / / / /_/ /
|
||||
/_/_/_/ /_/_/|_|\__,_/_/_/ /_/\__, /
|
||||
/____/
|
||||
@@ -1,6 +0,0 @@
|
||||
____ __ ___
|
||||
/ __ \____ ____ ___ / |/ /
|
||||
/ /_/ / __ \/ __ `__ \/ /|_/ /
|
||||
/ _, _/ /_/ / / / / / / / / /
|
||||
/_/ |_|\____/_/ /_/ /_/_/ /_/
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (MickLesk)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://linkding.link/
|
||||
|
||||
APP="linkding"
|
||||
var_tags="${var_tags:-bookmarks;management}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/linkding ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "linkding" "sissbruecker/linkding"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop nginx linkding linkding-tasks
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp -r /opt/linkding/data /opt/linkding_data_backup
|
||||
cp /opt/linkding/.env /opt/linkding_env_backup
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding"
|
||||
|
||||
msg_info "Restoring Data"
|
||||
cp -r /opt/linkding_data_backup/. /opt/linkding/data
|
||||
cp /opt/linkding_env_backup /opt/linkding/.env
|
||||
rm -rf /opt/linkding_data_backup /opt/linkding_env_backup
|
||||
ln -sf /usr/lib/x86_64-linux-gnu/mod_icu.so /opt/linkding/libicu.so
|
||||
msg_ok "Restored Data"
|
||||
|
||||
msg_info "Updating LinkDing"
|
||||
cd /opt/linkding
|
||||
rm -f bookmarks/settings/dev.py
|
||||
touch bookmarks/settings/custom.py
|
||||
$STD npm ci
|
||||
$STD npm run build
|
||||
$STD uv sync --no-dev --frozen
|
||||
$STD uv pip install gunicorn
|
||||
set -a && source /opt/linkding/.env && set +a
|
||||
$STD /opt/linkding/.venv/bin/python manage.py migrate
|
||||
$STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input
|
||||
msg_ok "Updated LinkDing"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start nginx linkding linkding-tasks
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9090${CL}"
|
||||
74
ct/romm.sh
74
ct/romm.sh
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://romm.app
|
||||
|
||||
APP="RomM"
|
||||
var_tags="${var_tags:-emulation}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
var_disk="${var_disk:-20}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/romm ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "romm" "rommapp/romm"; then
|
||||
msg_info "Stopping Services"
|
||||
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"
|
||||
|
||||
fetch_and_deploy_gh_release "romm" "rommapp/romm" "tarball" "latest" "/opt/romm"
|
||||
|
||||
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
|
||||
cd /opt/romm/frontend
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
# Merge static assets into dist folder
|
||||
cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/
|
||||
mkdir -p /opt/romm/frontend/dist/assets/romm
|
||||
ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources
|
||||
ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets
|
||||
msg_ok "Updated ROMM"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start romm-backend romm-worker romm-scheduler romm-watcher
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated successfully"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"
|
||||
@@ -83,7 +83,6 @@ function update_script() {
|
||||
msg_ok "Started Soularr Timer"
|
||||
msg_ok "Updated Soularr successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
|
||||
@@ -45,8 +45,6 @@ function update_script() {
|
||||
|
||||
msg_info "Updating VaultWarden to $VAULT (Patience)"
|
||||
cd /tmp/vaultwarden-src
|
||||
VW_VERSION="$VAULT"
|
||||
export VW_VERSION
|
||||
$STD cargo build --features "sqlite,mysql,postgresql" --release
|
||||
if [[ -f /usr/bin/vaultwarden ]]; then
|
||||
cp target/release/vaultwarden /usr/bin/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated": "2026-02-16T12:14:16Z",
|
||||
"generated": "2026-02-16T06:25:10Z",
|
||||
"versions": [
|
||||
{
|
||||
"slug": "2fauth",
|
||||
@@ -557,9 +557,9 @@
|
||||
{
|
||||
"slug": "immich-public-proxy",
|
||||
"repo": "alangrainger/immich-public-proxy",
|
||||
"version": "v1.15.2",
|
||||
"version": "v1.15.1",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T08:54:59Z"
|
||||
"date": "2026-01-26T08:04:27Z"
|
||||
},
|
||||
{
|
||||
"slug": "inspircd",
|
||||
@@ -585,9 +585,9 @@
|
||||
{
|
||||
"slug": "jackett",
|
||||
"repo": "Jackett/Jackett",
|
||||
"version": "v0.24.1127",
|
||||
"version": "v0.24.1124",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T08:43:41Z"
|
||||
"date": "2026-02-15T05:54:22Z"
|
||||
},
|
||||
{
|
||||
"slug": "jellystat",
|
||||
@@ -729,13 +729,6 @@
|
||||
"pinned": false,
|
||||
"date": "2025-11-16T22:40:18Z"
|
||||
},
|
||||
{
|
||||
"slug": "linkding",
|
||||
"repo": "sissbruecker/linkding",
|
||||
"version": "v1.45.0",
|
||||
"pinned": false,
|
||||
"date": "2026-01-06T20:31:04Z"
|
||||
},
|
||||
{
|
||||
"slug": "linkstack",
|
||||
"repo": "linkstackorg/linkstack",
|
||||
@@ -970,9 +963,9 @@
|
||||
{
|
||||
"slug": "ots",
|
||||
"repo": "Luzifer/ots",
|
||||
"version": "v1.21.1",
|
||||
"version": "v1.21.0",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T12:12:23Z"
|
||||
"date": "2026-01-19T23:21:29Z"
|
||||
},
|
||||
{
|
||||
"slug": "outline",
|
||||
@@ -1019,9 +1012,9 @@
|
||||
{
|
||||
"slug": "paperless-gpt",
|
||||
"repo": "icereed/paperless-gpt",
|
||||
"version": "v0.25.0",
|
||||
"version": "v0.24.0",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T08:31:48Z"
|
||||
"date": "2026-01-14T21:28:09Z"
|
||||
},
|
||||
{
|
||||
"slug": "paperless-ngx",
|
||||
@@ -1275,13 +1268,6 @@
|
||||
"pinned": false,
|
||||
"date": "2025-03-28T13:00:23Z"
|
||||
},
|
||||
{
|
||||
"slug": "romm",
|
||||
"repo": "RetroAchievements/RALibretro",
|
||||
"version": "1.8.2",
|
||||
"pinned": false,
|
||||
"date": "2026-01-23T17:03:31Z"
|
||||
},
|
||||
{
|
||||
"slug": "rustdeskserver",
|
||||
"repo": "rustdesk/rustdesk-server",
|
||||
@@ -1334,9 +1320,9 @@
|
||||
{
|
||||
"slug": "semaphore",
|
||||
"repo": "semaphoreui/semaphore",
|
||||
"version": "v2.17.2",
|
||||
"version": "v2.17.0",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T10:27:40Z"
|
||||
"date": "2026-02-13T21:08:30Z"
|
||||
},
|
||||
{
|
||||
"slug": "shelfmark",
|
||||
@@ -1740,9 +1726,9 @@
|
||||
{
|
||||
"slug": "zitadel",
|
||||
"repo": "zitadel/zitadel",
|
||||
"version": "v4.11.0",
|
||||
"version": "v4.10.1",
|
||||
"pinned": false,
|
||||
"date": "2026-02-16T09:48:38Z"
|
||||
"date": "2026-01-30T06:52:53Z"
|
||||
},
|
||||
{
|
||||
"slug": "zoraxy",
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "linkding",
|
||||
"slug": "linkding",
|
||||
"categories": [
|
||||
12
|
||||
],
|
||||
"date_created": "2026-02-16",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 9090,
|
||||
"documentation": "https://linkding.link/",
|
||||
"website": "https://linkding.link/",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/linkding.webp",
|
||||
"config_path": "/opt/linkding/.env",
|
||||
"description": "linkding is a self-hosted bookmark manager that is designed to be minimal, fast, and easy to set up. It features a clean UI, tag-based organization, bulk editing, Markdown notes, read it later functionality, sharing, REST API, and browser extensions for Firefox and Chrome.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/linkding.sh",
|
||||
"resources": {
|
||||
"cpu": 2,
|
||||
"ram": 1024,
|
||||
"hdd": 4,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": "admin",
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "Admin credentials are stored in /opt/linkding/.env",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "Valid TLS certificates and fully-qualified domain names behind a reverse proxy (Caddy) for 3 services - OpenCloud (port: 9200), Collabora (port: 9980), and WOPI (port: 9300) are **REQUIRED**",
|
||||
"text": "Valid TLS certificates and fully-qualified domain names behind a reverse proxy (Caddy) for 3 services - OpenCloud, Collabora, and WOPI are **REQUIRED**",
|
||||
"type": "warning"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "RomM",
|
||||
"slug": "romm",
|
||||
"categories": [
|
||||
24
|
||||
],
|
||||
"date_created": "2026-02-16",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 80,
|
||||
"documentation": "https://docs.romm.app/latest/",
|
||||
"website": "https://romm.app/",
|
||||
"config_path": "/opt/romm/.env",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/romm.webp",
|
||||
"description": "RomM (ROM Manager) allows you to scan, enrich, browse and play your game collection with a clean and responsive interface. Support for multiple platforms, various naming schemes, and custom tags.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/romm.sh",
|
||||
"resources": {
|
||||
"cpu": 2,
|
||||
"ram": 4096,
|
||||
"hdd": 20,
|
||||
"os": "debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": []
|
||||
}
|
||||
@@ -1,40 +1,35 @@
|
||||
{
|
||||
"name": "Wishlist",
|
||||
"slug": "wishlist",
|
||||
"categories": [
|
||||
12
|
||||
],
|
||||
"date_created": "2026-02-04",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 3280,
|
||||
"documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started",
|
||||
"website": "https://github.com/cmintey/wishlist",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp",
|
||||
"config_path": "/opt/wishlist/.env",
|
||||
"description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/wishlist.sh",
|
||||
"resources": {
|
||||
"cpu": 2,
|
||||
"ram": 2048,
|
||||
"hdd": 5,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "When using a reverse proxy with this script, please edit the`ORIGIN` value in `/opt/wishlist/.env` to point to your new URL, otherwise creating an admin account or logging in will not work.",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
"name": "Wishlist",
|
||||
"slug": "wishlist",
|
||||
"categories": [
|
||||
12
|
||||
],
|
||||
"date_created": "2026-02-04",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 3280,
|
||||
"documentation": "https://github.com/cmintey/wishlist/blob/main/README.md#getting-started",
|
||||
"config_path": "/opt/wishlist/.env",
|
||||
"website": "https://github.com/cmintey/wishlist",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/cmintey-wishlist.webp",
|
||||
"description": "Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/wishlist.sh",
|
||||
"resources": {
|
||||
"cpu": 2,
|
||||
"ram": 2048,
|
||||
"hdd": 5,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": []
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ $STD apk add --no-cache \
|
||||
libc6-compat
|
||||
msg_ok "Installed dependencies"
|
||||
|
||||
RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | awk 'NR==1')
|
||||
RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9.]*[0-9]\).*/\1/p' | head -1)
|
||||
msg_info "Installing Teamspeak Server v${RELEASE}"
|
||||
mkdir -p /opt/teamspeak-server
|
||||
cd /opt/teamspeak-server
|
||||
|
||||
@@ -26,13 +26,13 @@ msg_info "Setup CryptPad"
|
||||
cd /opt/cryptpad
|
||||
$STD npm ci
|
||||
$STD npm run install:components
|
||||
if [[ "$onlyoffice" =~ ^[Yy]$ ]]; then
|
||||
$STD bash -c "./install-onlyoffice.sh --accept-license"
|
||||
fi
|
||||
$STD npm run build
|
||||
cp config/config.example.js config/config.js
|
||||
sed -i "51s/localhost/${LOCAL_IP}/g" /opt/cryptpad/config/config.js
|
||||
sed -i "80s#//httpAddress: 'localhost'#httpAddress: '0.0.0.0'#g" /opt/cryptpad/config/config.js
|
||||
$STD npm run build
|
||||
if [[ "$onlyoffice" =~ ^[Yy]$ ]]; then
|
||||
$STD bash -c "./install-onlyoffice.sh --accept-license"
|
||||
fi
|
||||
msg_ok "Setup CryptPad"
|
||||
|
||||
msg_info "Creating Service"
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (MickLesk)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://linkding.link/
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
python3-dev \
|
||||
nginx \
|
||||
libpq-dev \
|
||||
libicu-dev \
|
||||
libsqlite3-dev \
|
||||
libffi-dev
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="22" setup_nodejs
|
||||
setup_uv
|
||||
fetch_and_deploy_gh_release "linkding" "sissbruecker/linkding"
|
||||
|
||||
msg_info "Building Frontend"
|
||||
cd /opt/linkding
|
||||
$STD npm ci
|
||||
$STD npm run build
|
||||
ln -sf /usr/lib/x86_64-linux-gnu/mod_icu.so /opt/linkding/libicu.so
|
||||
msg_ok "Built Frontend"
|
||||
|
||||
msg_info "Setting up LinkDing"
|
||||
rm -f bookmarks/settings/dev.py
|
||||
touch bookmarks/settings/custom.py
|
||||
$STD uv sync --no-dev --frozen
|
||||
$STD uv pip install gunicorn
|
||||
mkdir -p data/{favicons,previews,assets}
|
||||
ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)
|
||||
cat <<EOF >/opt/linkding/.env
|
||||
LD_SUPERUSER_NAME=admin
|
||||
LD_SUPERUSER_PASSWORD=${ADMIN_PASS}
|
||||
LD_CSRF_TRUSTED_ORIGINS=http://${LOCAL_IP}:9090
|
||||
EOF
|
||||
set -a && source /opt/linkding/.env && set +a
|
||||
$STD /opt/linkding/.venv/bin/python manage.py generate_secret_key
|
||||
$STD /opt/linkding/.venv/bin/python manage.py migrate
|
||||
$STD /opt/linkding/.venv/bin/python manage.py enable_wal
|
||||
$STD /opt/linkding/.venv/bin/python manage.py create_initial_superuser
|
||||
$STD /opt/linkding/.venv/bin/python manage.py collectstatic --no-input
|
||||
msg_ok "Set up LinkDing"
|
||||
|
||||
msg_info "Creating Services"
|
||||
cat <<EOF >/etc/systemd/system/linkding.service
|
||||
[Unit]
|
||||
Description=linkding Bookmark Manager
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/opt/linkding
|
||||
EnvironmentFile=/opt/linkding/.env
|
||||
ExecStart=/opt/linkding/.venv/bin/gunicorn \
|
||||
--bind 127.0.0.1:8000 \
|
||||
--workers 3 \
|
||||
--threads 2 \
|
||||
--timeout 120 \
|
||||
bookmarks.wsgi:application
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<EOF >/etc/systemd/system/linkding-tasks.service
|
||||
[Unit]
|
||||
Description=linkding Background Tasks
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/opt/linkding
|
||||
EnvironmentFile=/opt/linkding/.env
|
||||
ExecStart=/opt/linkding/.venv/bin/python manage.py run_huey
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<'EOF' >/etc/nginx/sites-available/linkding
|
||||
server {
|
||||
listen 9090;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size 20M;
|
||||
|
||||
location /static/ {
|
||||
alias /opt/linkding/static/;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
$STD rm -f /etc/nginx/sites-enabled/default
|
||||
$STD ln -sf /etc/nginx/sites-available/linkding /etc/nginx/sites-enabled/linkding
|
||||
systemctl enable -q --now nginx linkding linkding-tasks
|
||||
systemctl restart nginx
|
||||
msg_ok "Created Services"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,344 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ) | DevelopmentCats | AlphaLawless
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://romm.app
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
acl \
|
||||
git \
|
||||
build-essential \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
libmagic-dev \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
libmariadb3 \
|
||||
libmariadb-dev \
|
||||
libpq-dev \
|
||||
libbz2-dev \
|
||||
libreadline-dev \
|
||||
libsqlite3-dev \
|
||||
zlib1g-dev \
|
||||
liblzma-dev \
|
||||
libncurses5-dev \
|
||||
libncursesw5-dev \
|
||||
redis-server \
|
||||
redis-tools \
|
||||
p7zip-full \
|
||||
tzdata \
|
||||
nginx
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PYTHON_VERSION="3.13" setup_uv
|
||||
NODE_VERSION="22" setup_nodejs
|
||||
setup_mariadb
|
||||
MARIADB_DB_NAME="romm" MARIADB_DB_USER="romm" setup_mariadb_db
|
||||
|
||||
msg_info "Creating directories"
|
||||
mkdir -p /opt/romm \
|
||||
/var/lib/romm/config \
|
||||
/var/lib/romm/resources \
|
||||
/var/lib/romm/assets/{saves,states,screenshots} \
|
||||
/var/lib/romm/library/roms \
|
||||
/var/lib/romm/library/bios
|
||||
msg_ok "Created directories"
|
||||
|
||||
msg_info "Creating configuration file"
|
||||
cat <<'EOF' >/var/lib/romm/config/config.yml
|
||||
# RomM Configuration File
|
||||
# Documentation: https://docs.romm.app/latest/Getting-Started/Configuration-File/
|
||||
# Only uncomment the lines you want to use/modify
|
||||
|
||||
# exclude:
|
||||
# platforms:
|
||||
# - excluded_folder_a
|
||||
# roms:
|
||||
# single_file:
|
||||
# extensions:
|
||||
# - xml
|
||||
# - txt
|
||||
# names:
|
||||
# - '._*'
|
||||
# - '*.nfo'
|
||||
# multi_file:
|
||||
# names:
|
||||
# - downloaded_media
|
||||
# - media
|
||||
|
||||
# system:
|
||||
# platforms:
|
||||
# gc: ngc
|
||||
# ps1: psx
|
||||
|
||||
# The folder name where your roms are located (relative to library path)
|
||||
# filesystem:
|
||||
# roms_folder: 'roms'
|
||||
|
||||
# scan:
|
||||
# priority:
|
||||
# metadata:
|
||||
# - "igdb"
|
||||
# - "moby"
|
||||
# - "ss"
|
||||
# - "ra"
|
||||
# artwork:
|
||||
# - "igdb"
|
||||
# - "moby"
|
||||
# - "ss"
|
||||
# region:
|
||||
# - "us"
|
||||
# - "eu"
|
||||
# - "jp"
|
||||
# language:
|
||||
# - "en"
|
||||
# media:
|
||||
# - box2d
|
||||
# - box3d
|
||||
# - screenshot
|
||||
# - manual
|
||||
|
||||
# emulatorjs:
|
||||
# debug: false
|
||||
# cache_limit: null
|
||||
EOF
|
||||
chmod 644 /var/lib/romm/config/config.yml
|
||||
msg_ok "Created configuration file"
|
||||
|
||||
fetch_and_deploy_gh_release "RAHasher" "RetroAchievements/RALibretro" "prebuild" "latest" "/opt/RALibretro" "RAHasher-x64-Linux-*.zip"
|
||||
cp /opt/RALibretro/RAHasher /usr/bin/RAHasher
|
||||
chmod +x /usr/bin/RAHasher
|
||||
|
||||
fetch_and_deploy_gh_release "romm" "rommapp/romm"
|
||||
|
||||
msg_info "Creating environment file"
|
||||
sed -i 's/^supervised no/supervised systemd/' /etc/redis/redis.conf
|
||||
systemctl restart redis-server
|
||||
systemctl enable -q --now redis-server
|
||||
AUTH_SECRET_KEY=$(openssl rand -hex 32)
|
||||
|
||||
cat <<EOF >/opt/romm/.env
|
||||
ROMM_BASE_PATH=/var/lib/romm
|
||||
ROMM_CONFIG_PATH=/var/lib/romm/config/config.yml
|
||||
WEB_CONCURRENCY=4
|
||||
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_NAME=$MARIADB_DB_NAME
|
||||
DB_USER=$MARIADB_DB_USER
|
||||
DB_PASSWD=$MARIADB_DB_PASS
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
|
||||
ROMM_AUTH_SECRET_KEY=$AUTH_SECRET_KEY
|
||||
DISABLE_DOWNLOAD_ENDPOINT_AUTH=false
|
||||
DISABLE_CSRF_PROTECTION=false
|
||||
|
||||
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true
|
||||
RESCAN_ON_FILESYSTEM_CHANGE_DELAY=5
|
||||
|
||||
ENABLE_SCHEDULED_RESCAN=true
|
||||
SCHEDULED_RESCAN_CRON=0 3 * * *
|
||||
ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB=true
|
||||
SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON=0 4 * * *
|
||||
|
||||
LOGLEVEL=INFO
|
||||
EOF
|
||||
|
||||
chmod 600 /opt/romm/.env
|
||||
msg_ok "Created environment file"
|
||||
|
||||
msg_info "Setting up RomM Backend"
|
||||
cd /opt/romm
|
||||
export UV_CONCURRENT_DOWNLOADS=1
|
||||
$STD uv sync --all-extras
|
||||
cd /opt/romm/backend
|
||||
$STD uv run alembic upgrade head
|
||||
msg_ok "Set up RomM Backend"
|
||||
|
||||
msg_info "Setting up RomM Frontend"
|
||||
cd /opt/romm/frontend
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
|
||||
cp -rf /opt/romm/frontend/assets/* /opt/romm/frontend/dist/assets/
|
||||
|
||||
mkdir -p /opt/romm/frontend/dist/assets/romm
|
||||
ln -sfn /var/lib/romm/resources /opt/romm/frontend/dist/assets/romm/resources
|
||||
ln -sfn /var/lib/romm/assets /opt/romm/frontend/dist/assets/romm/assets
|
||||
msg_ok "Set up RomM Frontend"
|
||||
|
||||
msg_info "Configuring Nginx"
|
||||
cat <<'EOF' >/etc/nginx/sites-available/romm
|
||||
upstream romm_backend {
|
||||
server 127.0.0.1:5000;
|
||||
}
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /opt/romm/frontend/dist;
|
||||
client_max_body_size 0;
|
||||
|
||||
# Frontend SPA
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Static assets
|
||||
location /assets {
|
||||
alias /opt/romm/frontend/dist/assets;
|
||||
try_files $uri $uri/ =404;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# EmulatorJS player - requires COOP/COEP headers for SharedArrayBuffer
|
||||
location ~ ^/rom/.*/ejs$ {
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp";
|
||||
add_header Cross-Origin-Opener-Policy "same-origin";
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api {
|
||||
proxy_pass http://romm_backend;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# WebSocket and Netplay
|
||||
location ~ ^/(ws|netplay) {
|
||||
proxy_pass http://romm_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# OpenAPI docs
|
||||
location = /openapi.json {
|
||||
proxy_pass http://romm_backend;
|
||||
}
|
||||
|
||||
# Internal library file serving
|
||||
location /library/ {
|
||||
internal;
|
||||
alias /var/lib/romm/library/;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
ln -sf /etc/nginx/sites-available/romm /etc/nginx/sites-enabled/romm
|
||||
systemctl restart nginx
|
||||
systemctl enable -q --now nginx
|
||||
msg_ok "Configured Nginx"
|
||||
|
||||
msg_info "Creating Services"
|
||||
cat <<EOF >/etc/systemd/system/romm-backend.service
|
||||
[Unit]
|
||||
Description=RomM Backend
|
||||
After=network.target mariadb.service redis-server.service
|
||||
Requires=mariadb.service redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/romm/backend
|
||||
EnvironmentFile=/opt/romm/.env
|
||||
Environment="PYTHONPATH=/opt/romm"
|
||||
ExecStart=/opt/romm/.venv/bin/python main.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat <<EOF >/etc/systemd/system/romm-worker.service
|
||||
[Unit]
|
||||
Description=RomM RQ Worker
|
||||
After=network.target mariadb.service redis-server.service romm-backend.service
|
||||
Requires=mariadb.service redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/romm/backend
|
||||
EnvironmentFile=/opt/romm/.env
|
||||
Environment="PYTHONPATH=/opt/romm/backend"
|
||||
ExecStart=/opt/romm/.venv/bin/rq worker --path /opt/romm/backend --url redis://127.0.0.1:6379/0 high default low
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat <<EOF >/etc/systemd/system/romm-scheduler.service
|
||||
[Unit]
|
||||
Description=RomM RQ Scheduler
|
||||
After=network.target mariadb.service redis-server.service romm-backend.service
|
||||
Requires=mariadb.service redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/romm/backend
|
||||
EnvironmentFile=/opt/romm/.env
|
||||
Environment="PYTHONPATH=/opt/romm/backend"
|
||||
Environment="RQ_REDIS_HOST=127.0.0.1"
|
||||
Environment="RQ_REDIS_PORT=6379"
|
||||
ExecStart=/opt/romm/.venv/bin/rqscheduler --path /opt/romm/backend
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat <<EOF >/etc/systemd/system/romm-watcher.service
|
||||
[Unit]
|
||||
Description=RomM Filesystem Watcher
|
||||
After=network.target romm-backend.service
|
||||
Requires=romm-backend.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/romm/backend
|
||||
EnvironmentFile=/opt/romm/.env
|
||||
Environment="PYTHONPATH=/opt/romm/backend"
|
||||
ExecStart=/opt/romm/.venv/bin/watchfiles --target-type command '/opt/romm/.venv/bin/python watcher.py' /var/lib/romm/library
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl enable -q --now romm-backend romm-worker romm-scheduler romm-watcher
|
||||
msg_ok "Created Services"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -38,8 +38,8 @@ SECRET="$(openssl rand -hex 64)"
|
||||
sed -e '/^NODE_ENV=/s/=.*$/=production/' \
|
||||
-e 's/^TUDUDI_USER/# TUDUDI_USER/g' \
|
||||
-e "/_SECRET=/s/=.*$/=${SECRET}/" \
|
||||
-e '/^# DB_FILE=/s/^# //' \
|
||||
-e "s|^DB_FILE=.*|DB_FILE=${DB_LOCATION}/production.sqlite3|" \
|
||||
-e "/^# DB_FILE/s/^# //; \
|
||||
\|DB_FILE|s|/path.*$|${DB_LOCATION}/production.sqlite3|" \
|
||||
-e "/^# TUDUDI_ALLOWED/s/^# //; \
|
||||
\|_ORIGINS=|s|=.*$|=<your tududi IP or FDQN>|" \
|
||||
-e "/^# TUDUDI_UPLOAD/s/^# //; \
|
||||
|
||||
@@ -29,8 +29,6 @@ fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "l
|
||||
|
||||
msg_info "Building Vaultwarden (Patience)"
|
||||
cd /tmp/vaultwarden-src
|
||||
VW_VERSION=$(get_latest_github_release "dani-garcia/vaultwarden")
|
||||
export VW_VERSION
|
||||
$STD cargo build --features "sqlite,mysql,postgresql" --release
|
||||
msg_ok "Built Vaultwarden"
|
||||
|
||||
|
||||
@@ -34,6 +34,42 @@ EOF
|
||||
fi
|
||||
}
|
||||
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR
|
||||
trap on_exit EXIT
|
||||
trap on_interrupt INT
|
||||
trap on_terminate TERM
|
||||
|
||||
error_handler() {
|
||||
local exit_code="$1"
|
||||
local line_number="$2"
|
||||
local command="$3"
|
||||
|
||||
if [[ "$exit_code" -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "\e[?25h"
|
||||
echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
on_exit() {
|
||||
local exit_code="$?"
|
||||
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
on_interrupt() {
|
||||
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
|
||||
exit 130
|
||||
}
|
||||
|
||||
on_terminate() {
|
||||
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
|
||||
exit 143
|
||||
}
|
||||
|
||||
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
|
||||
setting_up_container() {
|
||||
msg_info "Setting up Container OS"
|
||||
|
||||
@@ -34,19 +34,11 @@ net_resolves() {
|
||||
}
|
||||
|
||||
ensure_usr_local_bin_persist() {
|
||||
# Login shells: /etc/profile.d/
|
||||
local PROFILE_FILE="/etc/profile.d/10-localbin.sh"
|
||||
if [ ! -f "$PROFILE_FILE" ]; then
|
||||
echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' >"$PROFILE_FILE"
|
||||
chmod +x "$PROFILE_FILE"
|
||||
fi
|
||||
|
||||
# Non-login shells (pct enter): /root/.profile and /root/.bashrc
|
||||
for rc_file in /root/.profile /root/.bashrc; do
|
||||
if [ -f "$rc_file" ] && ! grep -q '/usr/local/bin' "$rc_file"; then
|
||||
echo 'export PATH="/usr/local/bin:$PATH"' >>"$rc_file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
download_with_progress() {
|
||||
|
||||
104
misc/api.func
104
misc/api.func
@@ -25,8 +25,40 @@
|
||||
# - Only anonymous statistics (no personal data)
|
||||
# - User can opt-out via DIAGNOSTICS=no
|
||||
# - Random UUID for session tracking only
|
||||
10) echo "Docker / privileged mode required (unsupported environment)" ;;
|
||||
# - Data retention: 30 days
|
||||
#
|
||||
# ==============================================================================
|
||||
|
||||
# ==============================================================================
|
||||
# Telemetry Configuration
|
||||
# ==============================================================================
|
||||
TELEMETRY_URL="https://telemetry.community-scripts.org/telemetry"
|
||||
|
||||
# Timeout for telemetry requests (seconds)
|
||||
TELEMETRY_TIMEOUT=5
|
||||
|
||||
# ==============================================================================
|
||||
# SECTION 0: REPOSITORY SOURCE DETECTION
|
||||
# ==============================================================================
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# detect_repo_source()
|
||||
#
|
||||
# - Dynamically detects which GitHub/Gitea repo the scripts were loaded from
|
||||
# - Inspects /proc/$$/cmdline and $0 to find the source URL
|
||||
# - Maps detected repo to one of three canonical values:
|
||||
# * "ProxmoxVE" — official community-scripts/ProxmoxVE (production)
|
||||
# * "ProxmoxVED" — official community-scripts/ProxmoxVED (development)
|
||||
# * "external" — any fork or unknown source
|
||||
# - Fallback: "ProxmoxVED" (CI sed transforms ProxmoxVED → ProxmoxVE on promotion)
|
||||
# - Sets and exports REPO_SOURCE global variable
|
||||
# - Skips detection if REPO_SOURCE is already set (e.g., by environment)
|
||||
# ------------------------------------------------------------------------------
|
||||
detect_repo_source() {
|
||||
# Allow explicit override via environment
|
||||
[[ -n "${REPO_SOURCE:-}" ]] && return 0
|
||||
|
||||
local content="" owner_repo=""
|
||||
|
||||
# Method 1: Read from /proc/$$/cmdline
|
||||
# When invoked via: bash -c "$(curl -fsSL https://.../ct/app.sh)"
|
||||
@@ -85,17 +117,16 @@ detect_repo_source
|
||||
# - Canonical source of truth for ALL exit code mappings
|
||||
# - Used by both api.func (telemetry) and error_handler.func (error display)
|
||||
# - Supports:
|
||||
# * Generic/Shell errors (1-3, 10, 124-132, 134, 137, 139, 141, 143-146)
|
||||
# * curl/wget errors (4-8, 16, 18, 22-28, 30, 32-36, 39, 44-48, 51-52, 55-57, 59, 61, 63, 75, 78-79, 92, 95)
|
||||
# * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143)
|
||||
# * curl/wget errors (6, 7, 22, 28, 35)
|
||||
# * Package manager errors (APT, DPKG: 100-102, 255)
|
||||
# * BSD sysexits (64-78)
|
||||
# * Systemd/Service errors (150-154)
|
||||
# * Python/pip/uv errors (160-162)
|
||||
# * PostgreSQL errors (170-173)
|
||||
# * MySQL/MariaDB errors (180-183)
|
||||
# * MongoDB errors (190-193)
|
||||
# * Proxmox custom codes (200-231)
|
||||
# * Node.js/npm errors (239, 243, 245-249)
|
||||
# * Node.js/npm errors (243, 245-249)
|
||||
# - Returns description string for given exit code
|
||||
# ------------------------------------------------------------------------------
|
||||
explain_exit_code() {
|
||||
@@ -104,87 +135,30 @@ explain_exit_code() {
|
||||
# --- Generic / Shell ---
|
||||
1) echo "General error / Operation not permitted" ;;
|
||||
2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
|
||||
3) echo "General syntax or argument error" ;;
|
||||
10) echo "Docker / privileged mode required (unsupported environment)" ;;
|
||||
|
||||
# --- curl / wget errors (commonly seen in downloads) ---
|
||||
4) echo "curl: Feature not supported or protocol error" ;;
|
||||
5) echo "curl: Could not resolve proxy" ;;
|
||||
6) echo "curl: DNS resolution failed (could not resolve host)" ;;
|
||||
7) echo "curl: Failed to connect (network unreachable / host down)" ;;
|
||||
8) echo "curl: Server reply error (FTP/SFTP or apk untrusted key)" ;;
|
||||
16) echo "curl: HTTP/2 framing layer error" ;;
|
||||
18) echo "curl: Partial file (transfer not completed)" ;;
|
||||
22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
|
||||
23) echo "curl: Write error (disk full or permissions)" ;;
|
||||
24) echo "curl: Write to local file failed" ;;
|
||||
25) echo "curl: Upload failed" ;;
|
||||
26) echo "curl: Read error on local file (I/O)" ;;
|
||||
27) echo "curl: Out of memory (memory allocation failed)" ;;
|
||||
28) echo "curl: Operation timeout (network slow or server not responding)" ;;
|
||||
30) echo "curl: FTP port command failed" ;;
|
||||
32) echo "curl: FTP SIZE command failed" ;;
|
||||
33) echo "curl: HTTP range error" ;;
|
||||
34) echo "curl: HTTP post error" ;;
|
||||
35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
|
||||
36) echo "curl: FTP bad download resume" ;;
|
||||
39) echo "curl: LDAP search failed" ;;
|
||||
44) echo "curl: Internal error (bad function call order)" ;;
|
||||
45) echo "curl: Interface error (failed to bind to specified interface)" ;;
|
||||
46) echo "curl: Bad password entered" ;;
|
||||
47) echo "curl: Too many redirects" ;;
|
||||
48) echo "curl: Unknown command line option specified" ;;
|
||||
51) echo "curl: SSL peer certificate or SSH host key verification failed" ;;
|
||||
52) echo "curl: Empty reply from server (got nothing)" ;;
|
||||
55) echo "curl: Failed sending network data" ;;
|
||||
56) echo "curl: Receive error (connection reset by peer)" ;;
|
||||
57) echo "curl: Unrecoverable poll/select error (system I/O failure)" ;;
|
||||
59) echo "curl: Couldn't use specified SSL cipher" ;;
|
||||
61) echo "curl: Bad/unrecognized transfer encoding" ;;
|
||||
63) echo "curl: Maximum file size exceeded" ;;
|
||||
75) echo "Temporary failure (retry later)" ;;
|
||||
78) echo "curl: Remote file not found (404 on FTP/file)" ;;
|
||||
79) echo "curl: SSH session error (key exchange/auth failed)" ;;
|
||||
92) echo "curl: HTTP/2 stream error (protocol violation)" ;;
|
||||
95) echo "curl: HTTP/3 layer error" ;;
|
||||
|
||||
# --- Package manager / APT / DPKG ---
|
||||
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)" ;;
|
||||
|
||||
# --- BSD sysexits.h (64-78) ---
|
||||
64) echo "Usage error (wrong arguments)" ;;
|
||||
65) echo "Data format error (bad input data)" ;;
|
||||
66) echo "Input file not found (cannot open input)" ;;
|
||||
67) echo "User not found (addressee unknown)" ;;
|
||||
68) echo "Host not found (hostname unknown)" ;;
|
||||
69) echo "Service unavailable" ;;
|
||||
70) echo "Internal software error" ;;
|
||||
71) echo "System error (OS-level failure)" ;;
|
||||
72) echo "Critical OS file missing" ;;
|
||||
73) echo "Cannot create output file" ;;
|
||||
74) echo "I/O error" ;;
|
||||
76) echo "Remote protocol error" ;;
|
||||
77) echo "Permission denied" ;;
|
||||
|
||||
# --- Common shell/system errors ---
|
||||
124) echo "Command timed out (timeout command)" ;;
|
||||
125) echo "Command failed to start (Docker daemon or execution error)" ;;
|
||||
126) echo "Command invoked cannot execute (permission problem?)" ;;
|
||||
127) echo "Command not found" ;;
|
||||
128) echo "Invalid argument to exit" ;;
|
||||
129) echo "Killed by SIGHUP (terminal closed / hangup)" ;;
|
||||
130) echo "Aborted by user (SIGINT)" ;;
|
||||
131) echo "Killed by SIGQUIT (core dumped)" ;;
|
||||
132) echo "Killed by SIGILL (illegal CPU instruction)" ;;
|
||||
134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;;
|
||||
137) echo "Killed (SIGKILL / Out of memory?)" ;;
|
||||
139) echo "Segmentation fault (core dumped)" ;;
|
||||
141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;;
|
||||
143) echo "Terminated (SIGTERM)" ;;
|
||||
144) echo "Killed by signal 16 (SIGUSR1 / SIGSTKFLT)" ;;
|
||||
146) echo "Killed by signal 18 (SIGTSTP)" ;;
|
||||
|
||||
# --- Systemd / Service errors (150-154) ---
|
||||
150) echo "Systemd: Service failed to start" ;;
|
||||
@@ -192,6 +166,7 @@ explain_exit_code() {
|
||||
152) echo "Permission denied (EACCES)" ;;
|
||||
153) echo "Build/compile failed (make/gcc/cmake)" ;;
|
||||
154) echo "Node.js: Native addon build failed (node-gyp)" ;;
|
||||
|
||||
# --- Python / pip / uv (160-162) ---
|
||||
160) echo "Python: Virtualenv / uv environment missing or broken" ;;
|
||||
161) echo "Python: Dependency resolution failed" ;;
|
||||
@@ -242,8 +217,7 @@ explain_exit_code() {
|
||||
225) echo "Proxmox: No template available for OS/Version" ;;
|
||||
231) echo "Proxmox: LXC stack upgrade failed" ;;
|
||||
|
||||
# --- Node.js / npm / pnpm / yarn (239-249) ---
|
||||
239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;;
|
||||
# --- Node.js / npm / pnpm / yarn (243-249) ---
|
||||
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
|
||||
245) echo "Node.js: Invalid command-line option" ;;
|
||||
246) echo "Node.js: Internal JavaScript Parse Error" ;;
|
||||
@@ -650,8 +624,6 @@ EOF
|
||||
curl -fsS -m "${TELEMETRY_TIMEOUT}" -X POST "${TELEMETRY_URL}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_PAYLOAD" &>/dev/null || true
|
||||
|
||||
POST_TO_API_DONE=true
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
318
misc/build.func
318
misc/build.func
@@ -297,7 +297,7 @@ validate_container_id() {
|
||||
# Falls back gracefully if pvesh unavailable or returns empty
|
||||
if command -v pvesh &>/dev/null; then
|
||||
local cluster_ids
|
||||
cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null |
|
||||
cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null |
|
||||
grep -oP '"vmid":\s*\K[0-9]+' 2>/dev/null || true)
|
||||
if [[ -n "$cluster_ids" ]] && echo "$cluster_ids" | grep -qw "$ctid"; then
|
||||
return 1
|
||||
@@ -4038,13 +4038,6 @@ EOF'
|
||||
|
||||
msg_ok "Customized LXC Container"
|
||||
|
||||
# Optional DNS override for retry scenarios (inside LXC, never on host)
|
||||
if [[ "${DNS_RETRY_OVERRIDE:-false}" == "true" ]]; then
|
||||
msg_info "Applying DNS retry override in LXC (8.8.8.8, 1.1.1.1)"
|
||||
pct exec "$CTID" -- bash -c "printf 'nameserver 8.8.8.8\nnameserver 1.1.1.1\n' >/etc/resolv.conf" >/dev/null 2>&1 || true
|
||||
msg_ok "DNS override applied in LXC"
|
||||
fi
|
||||
|
||||
# Install SSH keys
|
||||
install_ssh_keys_into_ct
|
||||
|
||||
@@ -4157,302 +4150,32 @@ EOF'
|
||||
|
||||
# Prompt user for cleanup with 60s timeout
|
||||
echo ""
|
||||
|
||||
# Detect error type for smart recovery options
|
||||
local is_oom=false
|
||||
local is_network_issue=false
|
||||
local is_apt_issue=false
|
||||
local is_cmd_not_found=false
|
||||
local error_explanation=""
|
||||
if declare -f explain_exit_code >/dev/null 2>&1; then
|
||||
error_explanation="$(explain_exit_code "$install_exit_code")"
|
||||
fi
|
||||
|
||||
# OOM detection: exit codes 134 (SIGABRT/heap), 137 (SIGKILL/OOM), 243 (Node.js heap)
|
||||
if [[ $install_exit_code -eq 134 || $install_exit_code -eq 137 || $install_exit_code -eq 243 ]]; then
|
||||
is_oom=true
|
||||
fi
|
||||
|
||||
# APT/DPKG detection: exit codes 100-102 (APT), 255 (DPKG with log evidence)
|
||||
case "$install_exit_code" in
|
||||
100 | 101 | 102) is_apt_issue=true ;;
|
||||
255)
|
||||
if [[ -f "$combined_log" ]] && grep -qiE 'dpkg|apt-get|apt\.conf|broken packages|unmet dependencies|E: Sub-process|E: Failed' "$combined_log"; then
|
||||
is_apt_issue=true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Command not found detection
|
||||
if [[ $install_exit_code -eq 127 ]]; then
|
||||
is_cmd_not_found=true
|
||||
fi
|
||||
|
||||
# Network-related detection (curl/apt/git fetch failures and transient network issues)
|
||||
case "$install_exit_code" in
|
||||
6 | 7 | 22 | 28 | 35 | 52 | 56 | 57 | 75 | 78) is_network_issue=true ;;
|
||||
100)
|
||||
# APT can fail due to network (Failed to fetch)
|
||||
if [[ -f "$combined_log" ]] && grep -qiE 'Failed to fetch|Could not resolve|Connection failed|Network is unreachable|Temporary failure resolving' "$combined_log"; then
|
||||
is_network_issue=true
|
||||
fi
|
||||
;;
|
||||
128)
|
||||
if [[ -f "$combined_log" ]] && grep -qiE 'RPC failed|early EOF|fetch-pack|HTTP/2 stream|Could not resolve host|Temporary failure resolving|Failed to fetch|Connection reset|Network is unreachable' "$combined_log"; then
|
||||
is_network_issue=true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Exit 1 subclassification: analyze logs to identify actual root cause
|
||||
# Many exit 1 errors are actually APT, OOM, network, or command-not-found issues
|
||||
if [[ $install_exit_code -eq 1 && -f "$combined_log" ]]; then
|
||||
if grep -qiE 'E: Unable to|E: Package|E: Failed to fetch|dpkg.*error|broken packages|unmet dependencies|dpkg --configure -a' "$combined_log"; then
|
||||
is_apt_issue=true
|
||||
fi
|
||||
if grep -qiE 'Cannot allocate memory|Out of memory|oom-killer|Killed process|JavaScript heap' "$combined_log"; then
|
||||
is_oom=true
|
||||
fi
|
||||
if grep -qiE 'Could not resolve|DNS|Connection refused|Network is unreachable|No route to host|Temporary failure resolving|Failed to fetch' "$combined_log"; then
|
||||
is_network_issue=true
|
||||
fi
|
||||
if grep -qiE ': command not found|No such file or directory.*/s?bin/' "$combined_log"; then
|
||||
is_cmd_not_found=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show error explanation if available
|
||||
if [[ -n "$error_explanation" ]]; then
|
||||
echo -e "${TAB}${RD}Error: ${error_explanation}${CL}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Show specific hints for known error types
|
||||
if [[ $install_exit_code -eq 10 ]]; then
|
||||
echo -e "${TAB}${INFO} This error usually means the container needs ${GN}privileged${CL} mode or Docker/nesting support."
|
||||
echo -e "${TAB}${INFO} Recreate with: Advanced Install → Container Type: ${GN}Privileged${CL}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ $install_exit_code -eq 125 || $install_exit_code -eq 126 ]]; then
|
||||
echo -e "${TAB}${INFO} The command exists but cannot be executed. This may be a ${GN}permission${CL} issue."
|
||||
echo -e "${TAB}${INFO} If using Docker, ensure the container is ${GN}privileged${CL} or has correct permissions."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ "$is_cmd_not_found" == true ]]; then
|
||||
local missing_cmd=""
|
||||
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//')
|
||||
fi
|
||||
if [[ -n "$missing_cmd" ]]; then
|
||||
echo -e "${TAB}${INFO} Missing command: ${GN}${missing_cmd}${CL}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Build recovery menu based on error type
|
||||
echo -e "${YW}What would you like to do?${CL}"
|
||||
echo ""
|
||||
echo -e " ${GN}1)${CL} Remove container and exit"
|
||||
echo -e " ${GN}2)${CL} Keep container for debugging"
|
||||
echo -e " ${GN}3)${CL} Retry with verbose mode (full rebuild)"
|
||||
|
||||
local next_option=4
|
||||
local APT_OPTION="" OOM_OPTION="" DNS_OPTION=""
|
||||
|
||||
if [[ "$is_apt_issue" == true ]]; then
|
||||
echo -e " ${GN}${next_option})${CL} Repair APT/DPKG state and re-run install (in-place)"
|
||||
APT_OPTION=$next_option
|
||||
next_option=$((next_option + 1))
|
||||
fi
|
||||
|
||||
if [[ "$is_oom" == true ]]; then
|
||||
local new_ram=$((RAM_SIZE * 2))
|
||||
local new_cpu=$((CORE_COUNT * 2))
|
||||
echo -e " ${GN}${next_option})${CL} Retry with more resources (RAM: ${RAM_SIZE}→${new_ram} MiB, CPU: ${CORE_COUNT}→${new_cpu} cores)"
|
||||
OOM_OPTION=$next_option
|
||||
next_option=$((next_option + 1))
|
||||
fi
|
||||
|
||||
if [[ "$is_network_issue" == true ]]; then
|
||||
echo -e " ${GN}${next_option})${CL} Retry with DNS override in LXC (8.8.8.8 / 1.1.1.1)"
|
||||
DNS_OPTION=$next_option
|
||||
next_option=$((next_option + 1))
|
||||
fi
|
||||
|
||||
local max_option=$((next_option - 1))
|
||||
|
||||
echo ""
|
||||
echo -en "${YW}Select option [1-${max_option}] (default: 1, auto-remove in 60s): ${CL}"
|
||||
echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||
|
||||
if read -t 60 -r response; then
|
||||
case "${response:-1}" in
|
||||
1)
|
||||
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
||||
# Remove container
|
||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}"
|
||||
echo ""
|
||||
msg_info "Removing container ${CTID}"
|
||||
pct stop "$CTID" &>/dev/null || true
|
||||
pct destroy "$CTID" &>/dev/null || true
|
||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
||||
;;
|
||||
2)
|
||||
echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}"
|
||||
msg_ok "Container ${CTID} removed"
|
||||
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
||||
echo ""
|
||||
msg_warn "Container ${CTID} kept for debugging"
|
||||
|
||||
# Dev mode: Setup MOTD/SSH for debugging access to broken container
|
||||
if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then
|
||||
echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}"
|
||||
if pct exec "$CTID" -- bash -c "
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func)
|
||||
declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true
|
||||
" >/dev/null 2>&1; then
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func)
|
||||
declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true
|
||||
" >/dev/null 2>&1; then
|
||||
local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1)
|
||||
echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}"
|
||||
fi
|
||||
fi
|
||||
exit $install_exit_code
|
||||
;;
|
||||
3)
|
||||
# Retry with verbose mode (full rebuild)
|
||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild...${CL}"
|
||||
pct stop "$CTID" &>/dev/null || true
|
||||
pct destroy "$CTID" &>/dev/null || true
|
||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
||||
echo ""
|
||||
# Get new container ID
|
||||
local old_ctid="$CTID"
|
||||
export CTID=$(get_valid_container_id "$CTID")
|
||||
export VERBOSE="yes"
|
||||
export var_verbose="yes"
|
||||
|
||||
# Show rebuild summary
|
||||
echo -e "${YW}Rebuilding with preserved settings:${CL}"
|
||||
echo -e " Container ID: ${old_ctid} → ${CTID}"
|
||||
echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB"
|
||||
echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}"
|
||||
echo -e " Verbose: ${GN}enabled${CL}"
|
||||
echo ""
|
||||
msg_info "Restarting installation..."
|
||||
# Re-run build_container
|
||||
build_container
|
||||
return $?
|
||||
;;
|
||||
*)
|
||||
# Handle dynamic smart recovery options via named option variables
|
||||
local handled=false
|
||||
|
||||
if [[ -n "${APT_OPTION}" && "${response}" == "${APT_OPTION}" ]]; then
|
||||
# APT/DPKG in-place repair: fix broken package state and re-run install script
|
||||
handled=true
|
||||
echo -e "\n${TAB}${HOLD}${YW}Repairing APT/DPKG state in container ${CTID}...${CL}"
|
||||
pct exec "$CTID" -- bash -c "
|
||||
DEBIAN_FRONTEND=noninteractive dpkg --configure -a 2>/dev/null || true
|
||||
apt-get -f install -y 2>/dev/null || true
|
||||
apt-get clean 2>/dev/null
|
||||
apt-get update 2>/dev/null || true
|
||||
" >/dev/null 2>&1 || true
|
||||
echo -e "${BFR}${CM}${GN}APT/DPKG state repaired in container ${CTID}${CL}"
|
||||
echo ""
|
||||
export VERBOSE="yes"
|
||||
export var_verbose="yes"
|
||||
|
||||
echo -e "${YW}Re-running installation in existing container ${CTID}:${CL}"
|
||||
echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores | Disk: ${DISK_SIZE} GB"
|
||||
echo -e " Verbose: ${GN}enabled${CL}"
|
||||
echo ""
|
||||
msg_info "Re-running installation script..."
|
||||
|
||||
# Re-run install script in existing container (don't destroy/recreate)
|
||||
set +Eeuo pipefail
|
||||
trap - ERR
|
||||
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)"
|
||||
local apt_retry_exit=$?
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler' ERR
|
||||
|
||||
# Check for error flag from retry
|
||||
local apt_retry_code=0
|
||||
if [[ -n "${SESSION_ID:-}" ]]; then
|
||||
local retry_error_flag="/root/.install-${SESSION_ID}.failed"
|
||||
if pct exec "$CTID" -- test -f "$retry_error_flag" 2>/dev/null; then
|
||||
apt_retry_code=$(pct exec "$CTID" -- cat "$retry_error_flag" 2>/dev/null || echo "1")
|
||||
pct exec "$CTID" -- rm -f "$retry_error_flag" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $apt_retry_code -eq 0 && $apt_retry_exit -ne 0 ]]; then
|
||||
apt_retry_code=$apt_retry_exit
|
||||
fi
|
||||
|
||||
if [[ $apt_retry_code -eq 0 ]]; then
|
||||
msg_ok "Installation completed successfully after APT repair!"
|
||||
post_update_to_api "done" "0" "force"
|
||||
return 0
|
||||
else
|
||||
msg_error "Installation still failed after APT repair (exit code: ${apt_retry_code})"
|
||||
install_exit_code=$apt_retry_code
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${OOM_OPTION}" && "${response}" == "${OOM_OPTION}" ]]; then
|
||||
# Retry with doubled resources
|
||||
handled=true
|
||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more resources...${CL}"
|
||||
pct stop "$CTID" &>/dev/null || true
|
||||
pct destroy "$CTID" &>/dev/null || true
|
||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
||||
echo ""
|
||||
local old_ctid="$CTID"
|
||||
local old_ram="$RAM_SIZE"
|
||||
local old_cpu="$CORE_COUNT"
|
||||
export CTID=$(get_valid_container_id "$CTID")
|
||||
export RAM_SIZE=$((RAM_SIZE * 2))
|
||||
export CORE_COUNT=$((CORE_COUNT * 2))
|
||||
export var_ram="$RAM_SIZE"
|
||||
export var_cpu="$CORE_COUNT"
|
||||
export VERBOSE="yes"
|
||||
export var_verbose="yes"
|
||||
|
||||
echo -e "${YW}Rebuilding with increased resources:${CL}"
|
||||
echo -e " Container ID: ${old_ctid} → ${CTID}"
|
||||
echo -e " RAM: ${old_ram} → ${GN}${RAM_SIZE}${CL} MiB (x2)"
|
||||
echo -e " CPU: ${old_cpu} → ${GN}${CORE_COUNT}${CL} cores (x2)"
|
||||
echo -e " Disk: ${DISK_SIZE} GB | Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}"
|
||||
echo -e " Verbose: ${GN}enabled${CL}"
|
||||
echo ""
|
||||
msg_info "Restarting installation..."
|
||||
build_container
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [[ -n "${DNS_OPTION}" && "${response}" == "${DNS_OPTION}" ]]; then
|
||||
# Retry with DNS override in LXC
|
||||
handled=true
|
||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with DNS override...${CL}"
|
||||
pct stop "$CTID" &>/dev/null || true
|
||||
pct destroy "$CTID" &>/dev/null || true
|
||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
||||
echo ""
|
||||
local old_ctid="$CTID"
|
||||
export CTID=$(get_valid_container_id "$CTID")
|
||||
export DNS_RETRY_OVERRIDE="true"
|
||||
export VERBOSE="yes"
|
||||
export var_verbose="yes"
|
||||
|
||||
echo -e "${YW}Rebuilding with DNS override in LXC:${CL}"
|
||||
echo -e " Container ID: ${old_ctid} → ${CTID}"
|
||||
echo -e " DNS: ${GN}8.8.8.8, 1.1.1.1${CL} (inside LXC only)"
|
||||
echo -e " Verbose: ${GN}enabled${CL}"
|
||||
echo ""
|
||||
msg_info "Restarting installation..."
|
||||
build_container
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [[ "$handled" == false ]]; then
|
||||
echo -e "\n${TAB}${YW}Invalid option. Container ${CTID} kept.${CL}"
|
||||
exit $install_exit_code
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
# Timeout - auto-remove
|
||||
echo ""
|
||||
@@ -5530,20 +5253,14 @@ ensure_log_on_host() {
|
||||
# - Exit trap handler for reporting to API telemetry
|
||||
# - Captures exit code and reports to PocketBase using centralized error descriptions
|
||||
# - Uses explain_exit_code() from api.func for consistent error messages
|
||||
# - For non-zero exit codes: posts "failed" status
|
||||
# - For zero exit codes where post_update_to_api was never called:
|
||||
# catches orphaned "installing" records (e.g., script exited cleanly
|
||||
# but description() was never reached)
|
||||
# - Posts failure status with exit code to API (error description resolved automatically)
|
||||
# - Only executes on non-zero exit codes
|
||||
# ------------------------------------------------------------------------------
|
||||
api_exit_script() {
|
||||
local exit_code=$?
|
||||
exit_code=$?
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
ensure_log_on_host
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
elif [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
# Script exited with 0 but never sent a completion status
|
||||
# This catches edge cases like early returns after post_to_api()
|
||||
post_update_to_api "failed" "1"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -5551,6 +5268,5 @@ if command -v pveversion >/dev/null 2>&1; then
|
||||
trap 'api_exit_script' EXIT
|
||||
fi
|
||||
trap 'ensure_log_on_host; post_update_to_api "failed" "$?"' ERR
|
||||
trap 'ensure_log_on_host; post_update_to_api "failed" "129"; exit 129' SIGHUP
|
||||
trap 'ensure_log_on_host; post_update_to_api "failed" "130"; exit 130' SIGINT
|
||||
trap 'ensure_log_on_host; post_update_to_api "failed" "143"; exit 143' SIGTERM
|
||||
|
||||
@@ -37,34 +37,11 @@ if ! declare -f explain_exit_code &>/dev/null; then
|
||||
case "$code" in
|
||||
1) echo "General error / Operation not permitted" ;;
|
||||
2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
|
||||
10) echo "Docker / privileged mode required (unsupported environment)" ;;
|
||||
4) echo "curl: Feature not supported or protocol error" ;;
|
||||
5) echo "curl: Could not resolve proxy" ;;
|
||||
6) echo "curl: DNS resolution failed (could not resolve host)" ;;
|
||||
7) echo "curl: Failed to connect (network unreachable / host down)" ;;
|
||||
8) echo "curl: FTP server reply error" ;;
|
||||
22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
|
||||
23) echo "curl: Write error (disk full or permissions)" ;;
|
||||
25) echo "curl: Upload failed" ;;
|
||||
28) echo "curl: Operation timeout (network slow or server not responding)" ;;
|
||||
30) echo "curl: FTP port command failed" ;;
|
||||
35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
|
||||
56) echo "curl: Receive error (connection reset by peer)" ;;
|
||||
75) echo "Temporary failure (retry later)" ;;
|
||||
78) echo "curl: Remote file not found (404 on FTP/file)" ;;
|
||||
64) echo "Usage error (wrong arguments)" ;;
|
||||
65) echo "Data format error (bad input data)" ;;
|
||||
66) echo "Input file not found (cannot open input)" ;;
|
||||
67) echo "User not found (addressee unknown)" ;;
|
||||
68) echo "Host not found (hostname unknown)" ;;
|
||||
69) echo "Service unavailable" ;;
|
||||
70) echo "Internal software error" ;;
|
||||
71) echo "System error (OS-level failure)" ;;
|
||||
72) echo "Critical OS file missing" ;;
|
||||
73) echo "Cannot create output file" ;;
|
||||
74) echo "I/O error" ;;
|
||||
76) echo "Remote protocol error" ;;
|
||||
77) echo "Permission denied" ;;
|
||||
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)" ;;
|
||||
|
||||
@@ -172,7 +172,7 @@ network_check() {
|
||||
fi
|
||||
|
||||
set -e
|
||||
trap 'error_handler' ERR
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
@@ -1851,26 +1851,16 @@ function download_with_progress() {
|
||||
# Ensures /usr/local/bin is permanently in system PATH.
|
||||
#
|
||||
# Description:
|
||||
# - Adds to /etc/profile.d for login shells (SSH, noVNC)
|
||||
# - Adds to /root/.bashrc for non-login shells (pct enter)
|
||||
# - Adds to /etc/profile.d if not present
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function ensure_usr_local_bin_persist() {
|
||||
# Skip on Proxmox host
|
||||
command -v pveversion &>/dev/null && return
|
||||
|
||||
# Login shells: /etc/profile.d/
|
||||
local PROFILE_FILE="/etc/profile.d/custom_path.sh"
|
||||
if [[ ! -f "$PROFILE_FILE" ]]; then
|
||||
|
||||
if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then
|
||||
echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE"
|
||||
chmod +x "$PROFILE_FILE"
|
||||
fi
|
||||
|
||||
# Non-login shells (pct enter): /root/.bashrc
|
||||
local BASHRC="/root/.bashrc"
|
||||
if [[ -f "$BASHRC" ]] && ! grep -q '/usr/local/bin' "$BASHRC"; then
|
||||
echo 'export PATH="/usr/local/bin:$PATH"' >>"$BASHRC"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -529,21 +529,9 @@ cleanup_vmid() {
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then
|
||||
popd >/dev/null || true
|
||||
fi
|
||||
# Report final telemetry status if post_to_api_vm was called but no update was sent
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||
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"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_root() {
|
||||
|
||||
@@ -100,15 +100,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -100,15 +100,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -100,15 +100,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -104,16 +104,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
# Only send telemetry if post_to_api_vm was called (installing status was sent)
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -101,15 +101,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -100,15 +100,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -105,15 +105,7 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -79,15 +79,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -101,15 +101,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -109,15 +109,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -97,15 +97,7 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -100,15 +100,7 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -99,15 +99,7 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -99,15 +99,8 @@ function cleanup_vmid() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user