Compare commits

..

29 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
c637e504ad Update tools.func 2026-02-27 11:47:46 +01:00
MickLesk
506dcfc99d Improve error outputs across core functions 2026-02-26 22:42:42 +01:00
community-scripts-pr-app[bot]
0d6f5560ff Update CHANGELOG.md (#12367)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 19:21:17 +00:00
Tobias
2f7c7c4ea7 Update overseerr.sh (#12366) 2026-02-26 20:20:41 +01:00
community-scripts-pr-app[bot]
b7f94befba chore: update github-versions.json (#12365)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 18:16:29 +00:00
community-scripts-pr-app[bot]
dc3029822b Update CHANGELOG.md (#12359)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 14:54:54 +00:00
CanbiZ (MickLesk)
3fb677d768 Add workflow to close unauthorized new-script PRs (#12356) 2026-02-26 15:53:47 +01:00
CanbiZ (MickLesk)
e2a83549cb Restrict GitHub source extraction to '# Source:'
When extracting GitHub source URLs in the workflow, only search the "# Source:" line first to avoid matching other URLs (such as license links). Update the grep pipeline to filter for the Source line (case-insensitive) before extracting the https://github.com/... pattern and add explanatory comments.
2026-02-26 14:08:45 +01:00
community-scripts-pr-app[bot]
3911b09d1d chore: update github-versions.json (#12354)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 12:15:04 +00:00
community-scripts-pr-app[bot]
c24e9ead1c Update .app files (#12350)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-26 11:17:15 +01:00
community-scripts-pr-app[bot]
603d5b8f5e Update CHANGELOG.md (#12351)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 10:15:03 +00:00
community-scripts-pr-app[bot]
4fd70137b4 Update CHANGELOG.md (#12349)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 10:14:45 +00:00
community-scripts-pr-app[bot]
7398ffd9f4 Update date in json (#12348)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-26 10:14:37 +00:00
push-app-to-main[bot]
f1c765c534 Kima-Hub (#12319)
* Add kima-hub (ct)

* Change default disk size to 20

Updated the disk size default from 24 to 20.

* Modify date_created and hdd size in kima-hub.json

Updated the creation date and HDD size for kima-hub.

* remove: ai

* Update kima-hub.sh

* Update kima-hub-install.sh

---------

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: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-02-26 11:14:18 +01:00
community-scripts-pr-app[bot]
40aa06940c Update CHANGELOG.md (#12347)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 07:46:30 +00:00
Chris
117786376a [QOL] Immich: add warning regarding library compilation time (#12345) 2026-02-26 08:45:59 +01:00
community-scripts-pr-app[bot]
c5a635cdd7 chore: update github-versions.json (#12346)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 06:22:51 +00:00
community-scripts-pr-app[bot]
165e3f22cd Update CHANGELOG.md (#12344)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 00:19:57 +00:00
community-scripts-pr-app[bot]
2561a50d05 chore: update github-versions.json (#12343)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-26 00:19:34 +00:00
community-scripts-pr-app[bot]
6db5479b26 Update CHANGELOG.md (#12341)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 21:47:40 +00:00
Tobias
e74ddff49a fix: overseer migration (#12340) 2026-02-25 22:47:14 +01:00
community-scripts-pr-app[bot]
80132b0332 Update CHANGELOG.md (#12338)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 20:02:12 +00:00
CanbiZ (MickLesk)
83a19adbb4 tools.func: Improve GitHub/Codeberg API error handling and error output (#12330) 2026-02-25 21:01:45 +01:00
community-scripts-pr-app[bot]
6b196a7c81 chore: update github-versions.json (#12334)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 18:24:21 +00:00
community-scripts-pr-app[bot]
30082a1ba7 Update CHANGELOG.md (#12332)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 17:27:38 +00:00
Tobias
7741caa6ba add: vikunja: daemon reload (#12323) 2026-02-25 18:27:11 +01:00
community-scripts-pr-app[bot]
a3841d3cef Update CHANGELOG.md (#12331)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-25 16:32:03 +00:00
CanbiZ (MickLesk)
89cdabd040 opnsense-VM: Use ip link to verify bridge existence (#12329) 2026-02-25 17:31:41 +01:00
CanbiZ (MickLesk)
cbb82812b2 wger: Use $http_host for proxy Host header (#12327) 2026-02-25 17:31:14 +01:00
18 changed files with 741 additions and 141 deletions

View File

@@ -214,11 +214,12 @@ jobs:
total=$((total + 1)) total=$((total + 1))
slug=$(basename "$script" | sed 's/-install\.sh$//') slug=$(basename "$script" | sed 's/-install\.sh$//')
# Extract Source URL (GitHub only) # Extract Source URL (GitHub only) from the "# Source:" line
# Supports both: # Supports both:
# # Source: https://github.com/owner/repo # # Source: https://github.com/owner/repo
# # Source: https://example.com | Github: https://github.com/owner/repo # # Source: https://example.com | Github: https://github.com/owner/repo
source_url=$(head -20 "$script" | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "") # NOTE: Must filter for "# Source:" line first to avoid matching the License URL
source_url=$(head -20 "$script" | grep -i '# Source:' | grep -oP 'https://github\.com/[^\s|]+' | head -1 || echo "")
if [[ -z "$source_url" ]]; then if [[ -z "$source_url" ]]; then
report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |") report_lines+=("| \`$slug\` | — | — | — | — | ⏭️ No GitHub source |")
continue continue

119
.github/workflows/close-new-script-prs.yml generated vendored Normal file
View File

@@ -0,0 +1,119 @@
name: Close Unauthorized New Script PRs
on:
pull_request_target:
branches: ["main"]
types: [opened, labeled]
jobs:
check-new-script:
if: github.repository == 'community-scripts/ProxmoxVE'
runs-on: coolify-runner
permissions:
pull-requests: write
contents: read
steps:
- name: Close PR if unauthorized new script submission
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const prNumber = pr.number;
const author = pr.user.login;
const authorType = pr.user.type; // "User" or "Bot"
const owner = context.repo.owner;
const repo = context.repo.repo;
// --- Only act on PRs with the "new script" label ---
const labels = pr.labels.map(l => l.name);
if (!labels.includes("new script")) {
core.info(`PR #${prNumber} does not have "new script" label — skipping.`);
return;
}
// --- Allow our bots ---
const allowedBots = [
"push-app-to-main[bot]",
"push-app-to-main",
];
if (allowedBots.includes(author)) {
core.info(`PR #${prNumber} by allowed bot "${author}" — skipping.`);
return;
}
// --- Check if author is a member of the contributor team ---
const teamSlug = "contributor";
let isMember = false;
try {
const { status } = await github.rest.teams.getMembershipForUserInOrg({
org: owner,
team_slug: teamSlug,
username: author,
});
// status 200 means the user is a member (active or pending)
isMember = true;
} catch (error) {
if (error.status === 404) {
isMember = false;
} else {
core.warning(`Could not check team membership for ${author}: ${error.message}`);
// Fallback: check org membership
try {
await github.rest.orgs.checkMembershipForUser({
org: owner,
username: author,
});
isMember = true;
} catch {
isMember = false;
}
}
}
if (isMember) {
core.info(`PR #${prNumber} by contributor "${author}" — skipping.`);
return;
}
// --- Unauthorized: close the PR with a comment ---
core.info(`Closing PR #${prNumber} by "${author}" — not a contributor or allowed bot.`);
const comment = [
`👋 Hi @${author},`,
``,
`Thank you for your interest in contributing a new script!`,
``,
`However, **new scripts must first be submitted to our development repository** for testing and review before they can be merged here.`,
``,
`> 🛑 New scripts must be submitted to [**ProxmoxVED**](https://github.com/community-scripts/ProxmoxVED) for testing.`,
`> PRs without prior testing will be closed.`,
``,
`Please open your PR at **https://github.com/community-scripts/ProxmoxVED** instead.`,
`Once your script has been tested and approved there, it will be pushed to this repository automatically.`,
``,
`This PR will now be closed. Thank you for understanding! 🙏`,
].join("\n");
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: comment,
});
await github.rest.pulls.update({
owner,
repo,
pull_number: prNumber,
state: "closed",
});
// Add a label to indicate why it was closed
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: ["not a script issue"],
});

View File

@@ -407,6 +407,26 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-02-26
### 🆕 New Scripts
- Kima-Hub ([#12319](https://github.com/community-scripts/ProxmoxVE/pull/12319))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- hotfix: overseer version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12366](https://github.com/community-scripts/ProxmoxVE/pull/12366))
- #### ✨ New Features
- [QOL] Immich: add warning regarding library compilation time [@vhsdream](https://github.com/vhsdream) ([#12345](https://github.com/community-scripts/ProxmoxVE/pull/12345))
### 📂 Github
- github: add workflow to autom. close unauthorized new-script PRs [@MickLesk](https://github.com/MickLesk) ([#12356](https://github.com/community-scripts/ProxmoxVE/pull/12356))
## 2026-02-25 ## 2026-02-25
### 🆕 New Scripts ### 🆕 New Scripts
@@ -417,6 +437,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- fix: overseer migration [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12340](https://github.com/community-scripts/ProxmoxVE/pull/12340))
- add: vikunja: daemon reload [@CrazyWolf13](https://github.com/CrazyWolf13) ([#12323](https://github.com/community-scripts/ProxmoxVE/pull/12323))
- opnsense-VM: Use ip link to verify bridge existence [@MickLesk](https://github.com/MickLesk) ([#12329](https://github.com/community-scripts/ProxmoxVE/pull/12329))
- wger: Use $http_host for proxy Host header [@MickLesk](https://github.com/MickLesk) ([#12327](https://github.com/community-scripts/ProxmoxVE/pull/12327))
- Passbolt: Update Nginx config `client_max_body_size` [@tremor021](https://github.com/tremor021) ([#12313](https://github.com/community-scripts/ProxmoxVE/pull/12313)) - Passbolt: Update Nginx config `client_max_body_size` [@tremor021](https://github.com/tremor021) ([#12313](https://github.com/community-scripts/ProxmoxVE/pull/12313))
- Zammad: configure Elasticsearch before zammad start [@MickLesk](https://github.com/MickLesk) ([#12308](https://github.com/community-scripts/ProxmoxVE/pull/12308)) - Zammad: configure Elasticsearch before zammad start [@MickLesk](https://github.com/MickLesk) ([#12308](https://github.com/community-scripts/ProxmoxVE/pull/12308))
@@ -430,6 +454,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- Fix detection of ssh keys [@1-tempest](https://github.com/1-tempest) ([#12230](https://github.com/community-scripts/ProxmoxVE/pull/12230)) - Fix detection of ssh keys [@1-tempest](https://github.com/1-tempest) ([#12230](https://github.com/community-scripts/ProxmoxVE/pull/12230))
- #### ✨ New Features
- tools.func: Improve GitHub/Codeberg API error handling and error output [@MickLesk](https://github.com/MickLesk) ([#12330](https://github.com/community-scripts/ProxmoxVE/pull/12330))
- #### 🔧 Refactor - #### 🔧 Refactor
- core: remove duplicate traps, consolidate error handling and harden signal traps [@MickLesk](https://github.com/MickLesk) ([#12316](https://github.com/community-scripts/ProxmoxVE/pull/12316)) - core: remove duplicate traps, consolidate error handling and harden signal traps [@MickLesk](https://github.com/MickLesk) ([#12316](https://github.com/community-scripts/ProxmoxVE/pull/12316))

6
ct/headers/kima-hub Normal file
View File

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

View File

@@ -97,7 +97,7 @@ EOF
if [[ -f ~/.immich_library_revisions ]]; then if [[ -f ~/.immich_library_revisions ]]; then
libraries=("libjxl" "libheif" "libraw" "imagemagick" "libvips") libraries=("libjxl" "libheif" "libraw" "imagemagick" "libvips")
cd "$BASE_DIR" cd "$BASE_DIR"
msg_info "Checking for updates to custom image-processing libraries" msg_warn "Checking for updates to custom image-processing libraries (recompile time: 2-15min per library)"
$STD git pull $STD git pull
for library in "${libraries[@]}"; do for library in "${libraries[@]}"; do
compile_"$library" compile_"$library"

79
ct/kima-hub.sh Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Chevron7Locked/kima-hub
APP="Kima-Hub"
var_tags="${var_tags:-music;streaming;media}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-8192}"
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/kima-hub ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "kima-hub" "Chevron7Locked/kima-hub"; then
msg_info "Stopping Services"
systemctl stop kima-frontend kima-backend kima-analyzer kima-analyzer-clap
msg_ok "Stopped Services"
msg_info "Backing up Data"
cp /opt/kima-hub/backend/.env /opt/kima-hub-backend-env.bak
cp /opt/kima-hub/frontend/.env /opt/kima-hub-frontend-env.bak
msg_ok "Backed up Data"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball"
msg_info "Restoring Data"
cp /opt/kima-hub-backend-env.bak /opt/kima-hub/backend/.env
cp /opt/kima-hub-frontend-env.bak /opt/kima-hub/frontend/.env
rm -f /opt/kima-hub-backend-env.bak /opt/kima-hub-frontend-env.bak
msg_ok "Restored Data"
msg_info "Rebuilding Backend"
cd /opt/kima-hub/backend
$STD npm install
$STD npm run build
$STD npx prisma generate
$STD npx prisma migrate deploy
msg_ok "Rebuilt Backend"
msg_info "Rebuilding Frontend"
cd /opt/kima-hub/frontend
$STD npm install
$STD npm run build
msg_ok "Rebuilt Frontend"
msg_info "Starting Services"
systemctl start kima-backend kima-frontend kima-analyzer kima-analyzer-clap
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}:3030${CL}"

View File

@@ -28,7 +28,7 @@ function update_script() {
exit exit
fi fi
if [[ -f "$HOME/.overseerr" ]] && [[ "$(cat "$HOME/.overseerr")" == "1.34.0" ]]; then if [[ -f "$HOME/.overseerr" ]] && [[ "$(printf '%s\n' "1.35.0" "$(cat "$HOME/.overseerr")" | sort -V | head -n1)" == "1.35.0" ]]; then
echo echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Overseerr v1.34.0 detected." echo "Overseerr v1.34.0 detected."

View File

@@ -65,6 +65,7 @@ function update_script() {
msg_ok "Stopped Service" msg_ok "Stopped Service"
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary"
$STD systemctl daemon-reload
msg_info "Starting Service" msg_info "Starting Service"
systemctl start vikunja systemctl start vikunja

View File

@@ -1,5 +1,5 @@
{ {
"generated": "2026-02-25T12:14:52Z", "generated": "2026-02-26T18:16:20Z",
"versions": [ "versions": [
{ {
"slug": "2fauth", "slug": "2fauth",
@@ -109,9 +109,9 @@
{ {
"slug": "bazarr", "slug": "bazarr",
"repo": "morpheus65535/bazarr", "repo": "morpheus65535/bazarr",
"version": "v1.5.5", "version": "v1.5.6",
"pinned": false, "pinned": false,
"date": "2026-02-01T18:00:34Z" "date": "2026-02-26T11:33:11Z"
}, },
{ {
"slug": "bentopdf", "slug": "bentopdf",
@@ -151,9 +151,9 @@
{ {
"slug": "booklore", "slug": "booklore",
"repo": "booklore-app/BookLore", "repo": "booklore-app/BookLore",
"version": "v2.0.1", "version": "v2.0.2",
"pinned": false, "pinned": false,
"date": "2026-02-24T04:15:33Z" "date": "2026-02-25T19:59:20Z"
}, },
{ {
"slug": "bookstack", "slug": "bookstack",
@@ -242,9 +242,9 @@
{ {
"slug": "cosmos", "slug": "cosmos",
"repo": "azukaar/Cosmos-Server", "repo": "azukaar/Cosmos-Server",
"version": "v0.20.2", "version": "v0.21.2",
"pinned": false, "pinned": false,
"date": "2026-01-24T00:12:39Z" "date": "2026-02-26T11:32:33Z"
}, },
{ {
"slug": "cronicle", "slug": "cronicle",
@@ -270,16 +270,16 @@
{ {
"slug": "databasus", "slug": "databasus",
"repo": "databasus/databasus", "repo": "databasus/databasus",
"version": "v3.16.2", "version": "v3.16.3",
"pinned": false, "pinned": false,
"date": "2026-02-22T21:10:12Z" "date": "2026-02-25T19:57:26Z"
}, },
{ {
"slug": "dawarich", "slug": "dawarich",
"repo": "Freika/dawarich", "repo": "Freika/dawarich",
"version": "1.2.0", "version": "1.3.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T22:33:56Z" "date": "2026-02-25T19:30:25Z"
}, },
{ {
"slug": "discopanel", "slug": "discopanel",
@@ -452,9 +452,9 @@
{ {
"slug": "gitea-mirror", "slug": "gitea-mirror",
"repo": "RayLabsHQ/gitea-mirror", "repo": "RayLabsHQ/gitea-mirror",
"version": "v3.9.4", "version": "v3.9.5",
"pinned": false, "pinned": false,
"date": "2026-02-24T06:17:56Z" "date": "2026-02-26T05:32:12Z"
}, },
{ {
"slug": "glance", "slug": "glance",
@@ -606,16 +606,16 @@
{ {
"slug": "invoiceninja", "slug": "invoiceninja",
"repo": "invoiceninja/invoiceninja", "repo": "invoiceninja/invoiceninja",
"version": "v5.12.66", "version": "v5.12.68",
"pinned": false, "pinned": false,
"date": "2026-02-24T09:12:50Z" "date": "2026-02-25T19:38:19Z"
}, },
{ {
"slug": "jackett", "slug": "jackett",
"repo": "Jackett/Jackett", "repo": "Jackett/Jackett",
"version": "v0.24.1205", "version": "v0.24.1218",
"pinned": false, "pinned": false,
"date": "2026-02-25T05:49:14Z" "date": "2026-02-26T05:55:11Z"
}, },
{ {
"slug": "jellystat", "slug": "jellystat",
@@ -627,9 +627,9 @@
{ {
"slug": "joplin-server", "slug": "joplin-server",
"repo": "laurent22/joplin", "repo": "laurent22/joplin",
"version": "v3.5.12", "version": "v3.5.13",
"pinned": false, "pinned": false,
"date": "2026-01-17T14:20:33Z" "date": "2026-02-25T21:19:11Z"
}, },
{ {
"slug": "jotty", "slug": "jotty",
@@ -666,12 +666,19 @@
"pinned": false, "pinned": false,
"date": "2026-02-20T09:19:45Z" "date": "2026-02-20T09:19:45Z"
}, },
{
"slug": "kima-hub",
"repo": "Chevron7Locked/kima-hub",
"version": "v1.5.7",
"pinned": false,
"date": "2026-02-23T23:58:59Z"
},
{ {
"slug": "kimai", "slug": "kimai",
"repo": "kimai/kimai", "repo": "kimai/kimai",
"version": "2.49.0", "version": "2.50.0",
"pinned": false, "pinned": false,
"date": "2026-02-15T20:40:19Z" "date": "2026-02-25T20:13:51Z"
}, },
{ {
"slug": "kitchenowl", "slug": "kitchenowl",
@@ -711,9 +718,9 @@
{ {
"slug": "kubo", "slug": "kubo",
"repo": "ipfs/kubo", "repo": "ipfs/kubo",
"version": "v0.39.0", "version": "v0.40.0",
"pinned": false, "pinned": false,
"date": "2025-11-27T03:47:38Z" "date": "2026-02-25T23:16:17Z"
}, },
{ {
"slug": "kutt", "slug": "kutt",
@@ -809,9 +816,9 @@
{ {
"slug": "mail-archiver", "slug": "mail-archiver",
"repo": "s1t5/mail-archiver", "repo": "s1t5/mail-archiver",
"version": "2602.3", "version": "2602.4",
"pinned": false, "pinned": false,
"date": "2026-02-22T20:24:18Z" "date": "2026-02-26T08:43:01Z"
}, },
{ {
"slug": "managemydamnlife", "slug": "managemydamnlife",
@@ -823,9 +830,9 @@
{ {
"slug": "manyfold", "slug": "manyfold",
"repo": "manyfold3d/manyfold", "repo": "manyfold3d/manyfold",
"version": "v0.133.0", "version": "v0.133.1",
"pinned": false, "pinned": false,
"date": "2026-02-25T10:40:26Z" "date": "2026-02-26T15:50:34Z"
}, },
{ {
"slug": "mealie", "slug": "mealie",
@@ -963,9 +970,9 @@
{ {
"slug": "oauth2-proxy", "slug": "oauth2-proxy",
"repo": "oauth2-proxy/oauth2-proxy", "repo": "oauth2-proxy/oauth2-proxy",
"version": "v7.14.2", "version": "v7.14.3",
"pinned": false, "pinned": false,
"date": "2026-01-18T00:26:09Z" "date": "2026-02-26T14:10:21Z"
}, },
{ {
"slug": "ombi", "slug": "ombi",
@@ -1054,9 +1061,9 @@
{ {
"slug": "paperless-gpt", "slug": "paperless-gpt",
"repo": "icereed/paperless-gpt", "repo": "icereed/paperless-gpt",
"version": "v0.25.0", "version": "v0.25.1",
"pinned": false, "pinned": false,
"date": "2026-02-16T08:31:48Z" "date": "2026-02-26T14:50:11Z"
}, },
{ {
"slug": "paperless-ngx", "slug": "paperless-ngx",
@@ -1166,9 +1173,9 @@
{ {
"slug": "prometheus", "slug": "prometheus",
"repo": "prometheus/prometheus", "repo": "prometheus/prometheus",
"version": "v3.9.1", "version": "v3.10.0",
"pinned": false, "pinned": false,
"date": "2026-01-07T17:05:53Z" "date": "2026-02-26T01:19:51Z"
}, },
{ {
"slug": "prometheus-alertmanager", "slug": "prometheus-alertmanager",
@@ -1264,9 +1271,9 @@
{ {
"slug": "radicale", "slug": "radicale",
"repo": "Kozea/Radicale", "repo": "Kozea/Radicale",
"version": "v3.6.0", "version": "v3.6.1",
"pinned": false, "pinned": false,
"date": "2026-01-10T06:56:46Z" "date": "2026-02-24T06:36:23Z"
}, },
{ {
"slug": "rclone", "slug": "rclone",
@@ -1292,9 +1299,9 @@
{ {
"slug": "recyclarr", "slug": "recyclarr",
"repo": "recyclarr/recyclarr", "repo": "recyclarr/recyclarr",
"version": "v8.3.1", "version": "v8.3.2",
"pinned": false, "pinned": false,
"date": "2026-02-25T01:01:31Z" "date": "2026-02-25T22:39:51Z"
}, },
{ {
"slug": "reitti", "slug": "reitti",
@@ -1390,9 +1397,9 @@
{ {
"slug": "signoz", "slug": "signoz",
"repo": "SigNoz/signoz-otel-collector", "repo": "SigNoz/signoz-otel-collector",
"version": "v0.144.1", "version": "v0.144.2",
"pinned": false, "pinned": false,
"date": "2026-02-25T05:57:17Z" "date": "2026-02-26T05:57:26Z"
}, },
{ {
"slug": "silverbullet", "slug": "silverbullet",
@@ -1600,9 +1607,9 @@
{ {
"slug": "tunarr", "slug": "tunarr",
"repo": "chrisbenincasa/tunarr", "repo": "chrisbenincasa/tunarr",
"version": "v1.1.16", "version": "v1.1.17",
"pinned": false, "pinned": false,
"date": "2026-02-23T21:24:47Z" "date": "2026-02-25T19:56:36Z"
}, },
{ {
"slug": "uhf", "slug": "uhf",
@@ -1663,9 +1670,9 @@
{ {
"slug": "vikunja", "slug": "vikunja",
"repo": "go-vikunja/vikunja", "repo": "go-vikunja/vikunja",
"version": "v1.1.0", "version": "v2.0.0",
"pinned": false, "pinned": false,
"date": "2026-02-09T10:34:29Z" "date": "2026-02-25T13:58:47Z"
}, },
{ {
"slug": "wallabag", "slug": "wallabag",
@@ -1779,6 +1786,13 @@
"pinned": false, "pinned": false,
"date": "2026-02-24T15:15:46Z" "date": "2026-02-24T15:15:46Z"
}, },
{
"slug": "zerobyte",
"repo": "restic/restic",
"version": "v0.18.1",
"pinned": false,
"date": "2025-09-21T18:24:38Z"
},
{ {
"slug": "zigbee2mqtt", "slug": "zigbee2mqtt",
"repo": "Koenkk/zigbee2mqtt", "repo": "Koenkk/zigbee2mqtt",

View File

@@ -51,6 +51,10 @@
{ {
"text": "Logs: `/var/log/immich`", "text": "Logs: `/var/log/immich`",
"type": "info" "type": "info"
},
{
"text": "During first install, 5 custom libraries need to be compiled from source. Depending on your CPU, this can take anywhere between 15 minutes and 2 hours. Please be patient. Touch grass or something.",
"type": "warning"
} }
] ]
} }

View File

@@ -0,0 +1,48 @@
{
"name": "Kima-Hub",
"slug": "kima-hub",
"categories": [
13
],
"date_created": "2026-02-26",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3030,
"documentation": "https://github.com/Chevron7Locked/kima-hub#readme",
"website": "https://github.com/Chevron7Locked/kima-hub",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kima-hub.webp",
"config_path": "/opt/kima-hub/backend/.env",
"description": "Self-hosted, on-demand audio streaming platform with AI-powered vibe matching, mood detection, smart playlists, and Lidarr/Audiobookshelf integration.",
"install_methods": [
{
"type": "default",
"script": "ct/kima-hub.sh",
"resources": {
"cpu": 4,
"ram": 8192,
"hdd": 20,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": [
{
"text": "First user to register becomes the administrator.",
"type": "info"
},
{
"text": "Mount your music library to /music in the container.",
"type": "warning"
},
{
"text": "Audio analysis (mood/vibe detection) requires significant RAM (2-4GB per worker).",
"type": "info"
}
]
}

View File

@@ -154,7 +154,7 @@ sed -i -e "/^#shared_preload/s/^#//;/^shared_preload/s/''/'vchord.so'/" /etc/pos
systemctl restart postgresql.service systemctl restart postgresql.service
PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_GRANT_SUPERUSER="true" PG_DB_SKIP_ALTER_ROLE="true" setup_postgresql_db PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_GRANT_SUPERUSER="true" PG_DB_SKIP_ALTER_ROLE="true" setup_postgresql_db
msg_info "Compiling Custom Photo-processing Library (extreme patience)" msg_warn "Compiling Custom Photo-processing Libraries (can take anywhere from 15min to 2h)"
LD_LIBRARY_PATH=/usr/local/lib LD_LIBRARY_PATH=/usr/local/lib
export LD_RUN_PATH=/usr/local/lib export LD_RUN_PATH=/usr/local/lib
STAGING_DIR=/opt/staging STAGING_DIR=/opt/staging

212
install/kima-hub-install.sh Normal file
View File

@@ -0,0 +1,212 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Chevron7Locked/kima-hub
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y \
build-essential \
git \
openssl \
ffmpeg \
python3 \
python3-pip \
python3-dev \
python3-numpy \
redis-server
msg_ok "Installed Dependencies"
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
PG_DB_NAME="kima" PG_DB_USER="kima" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
NODE_VERSION="20" setup_nodejs
msg_info "Configuring Redis"
systemctl enable -q --now redis-server
msg_ok "Configured Redis"
fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball"
msg_info "Installing Python Dependencies"
export PIP_BREAK_SYSTEM_PACKAGES=1
$STD pip3 install --no-cache-dir \
tensorflow \
essentia-tensorflow \
redis \
psycopg2-binary \
laion-clap \
torch \
torchaudio \
librosa \
transformers \
pgvector \
python-dotenv \
requests
msg_ok "Installed Python Dependencies"
msg_info "Downloading Essentia ML Models"
mkdir -p /opt/kima-hub/models
cd /opt/kima-hub/models
curl -fsSL -o msd-musicnn-1.pb "https://essentia.upf.edu/models/autotagging/msd/msd-musicnn-1.pb"
curl -fsSL -o mood_happy-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_happy/mood_happy-msd-musicnn-1.pb"
curl -fsSL -o mood_sad-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_sad/mood_sad-msd-musicnn-1.pb"
curl -fsSL -o mood_relaxed-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_relaxed/mood_relaxed-msd-musicnn-1.pb"
curl -fsSL -o mood_aggressive-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_aggressive/mood_aggressive-msd-musicnn-1.pb"
curl -fsSL -o mood_party-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_party/mood_party-msd-musicnn-1.pb"
curl -fsSL -o mood_acoustic-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_acoustic/mood_acoustic-msd-musicnn-1.pb"
curl -fsSL -o mood_electronic-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/mood_electronic/mood_electronic-msd-musicnn-1.pb"
curl -fsSL -o danceability-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/danceability/danceability-msd-musicnn-1.pb"
curl -fsSL -o voice_instrumental-msd-musicnn-1.pb "https://essentia.upf.edu/models/classification-heads/voice_instrumental/voice_instrumental-msd-musicnn-1.pb"
msg_ok "Downloaded Essentia ML Models"
msg_info "Downloading CLAP Model"
curl -fsSL -o /opt/kima-hub/models/music_audioset_epoch_15_esc_90.14.pt "https://huggingface.co/lukewys/laion_clap/resolve/main/music_audioset_epoch_15_esc_90.14.pt"
msg_ok "Downloaded CLAP Model"
msg_info "Building Backend"
cd /opt/kima-hub/backend
$STD npm ci
$STD npm run build
msg_ok "Built Backend"
msg_info "Configuring Backend"
SESSION_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
cat <<EOF >/opt/kima-hub/backend/.env
NODE_ENV=production
DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}
REDIS_URL=redis://localhost:6379
PORT=3006
MUSIC_PATH=/music
TRANSCODE_CACHE_PATH=/opt/kima-hub/cache/transcodes
SESSION_SECRET=${SESSION_SECRET}
SETTINGS_ENCRYPTION_KEY=${ENCRYPTION_KEY}
INTERNAL_API_SECRET=$(openssl rand -hex 16)
EOF
msg_ok "Configured Backend"
msg_info "Running Database Migrations"
cd /opt/kima-hub/backend
$STD npx prisma generate
$STD npx prisma migrate deploy
msg_ok "Ran Database Migrations"
msg_info "Building Frontend"
cd /opt/kima-hub/frontend
$STD npm ci
export NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:3006
$STD npm run build
msg_ok "Built Frontend"
msg_info "Configuring Frontend"
cat <<EOF >/opt/kima-hub/frontend/.env
NODE_ENV=production
BACKEND_URL=http://localhost:3006
PORT=3030
EOF
msg_ok "Configured Frontend"
msg_info "Creating Directories"
mkdir -p /opt/kima-hub/cache/transcodes
mkdir -p /music
msg_ok "Created Directories"
msg_info "Creating Services"
cat <<EOF >/etc/systemd/system/kima-backend.service
[Unit]
Description=Kima Hub Backend
After=network.target postgresql.service redis-server.service
Wants=postgresql.service redis-server.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/kima-hub/backend
EnvironmentFile=/opt/kima-hub/backend/.env
ExecStart=/usr/bin/node /opt/kima-hub/backend/dist/index.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/kima-frontend.service
[Unit]
Description=Kima Hub Frontend
After=network.target kima-backend.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/kima-hub/frontend
EnvironmentFile=/opt/kima-hub/frontend/.env
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/kima-analyzer.service
[Unit]
Description=Kima Hub Audio Analyzer (Essentia)
After=network.target postgresql.service redis-server.service kima-backend.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/kima-hub/services/audio-analyzer
Environment=DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}
Environment=REDIS_URL=redis://localhost:6379
Environment=MUSIC_PATH=/music
Environment=BATCH_SIZE=10
Environment=SLEEP_INTERVAL=5
Environment=NUM_WORKERS=2
Environment=THREADS_PER_WORKER=1
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer/analyzer.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
cat <<EOF >/etc/systemd/system/kima-analyzer-clap.service
[Unit]
Description=Kima Hub CLAP Audio Analyzer
After=network.target postgresql.service redis-server.service kima-backend.service kima-analyzer.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/kima-hub/services/audio-analyzer-clap
Environment=DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}
Environment=REDIS_URL=redis://localhost:6379
Environment=BACKEND_URL=http://localhost:3006
Environment=MUSIC_PATH=/music
Environment=SLEEP_INTERVAL=5
Environment=NUM_WORKERS=1
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer-clap/analyzer.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now kima-backend kima-frontend kima-analyzer kima-analyzer-clap
msg_ok "Created Services"
motd_ssh
customize
cleanup_lxc

View File

@@ -164,7 +164,7 @@ server {
location / { location / {
proxy_pass http://127.0.0.1:8000; proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host; proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off; proxy_redirect off;

View File

@@ -118,7 +118,7 @@ maxkeys_check() {
# Exit if kernel parameters are unavailable # Exit if kernel parameters are unavailable
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" msg_error "Unable to read kernel key parameters. Ensure proper permissions."
exit 1 exit 1
fi fi
@@ -135,19 +135,19 @@ maxkeys_check() {
# Check if key or byte usage is near limits # Check if key or byte usage is near limits
failure=0 failure=0
if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" msg_warn "Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys})"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1 failure=1
fi fi
if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" msg_warn "Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes})"
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
failure=1 failure=1
fi fi
# Provide next steps if issues are detected # Provide next steps if issues are detected
if [[ "$failure" -eq 1 ]]; then if [[ "$failure" -eq 1 ]]; then
echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" msg_error "Kernel key limits exceeded - see suggestions above"
exit 1 exit 1
fi fi
@@ -2034,6 +2034,7 @@ advanced_settings() {
((STEP++)) ((STEP++))
else else
whiptail --msgbox "Default bridge 'vmbr0' not found!\n\nPlease configure a network bridge in Proxmox first." 10 58 whiptail --msgbox "Default bridge 'vmbr0' not found!\n\nPlease configure a network bridge in Proxmox first." 10 58
msg_error "Default bridge 'vmbr0' not found"
exit 1 exit 1
fi fi
else else
@@ -3049,7 +3050,7 @@ install_script() {
CHOICE="" CHOICE=""
;; ;;
*) *)
echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" msg_error "Invalid option: $CHOICE"
exit 1 exit 1
;; ;;
esac esac
@@ -3128,12 +3129,12 @@ check_container_resources() {
current_cpu=$(nproc) current_cpu=$(nproc)
if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then
echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" msg_warn "Under-provisioned: Required ${var_cpu} CPU/${var_ram}MB RAM, Current ${current_cpu} CPU/${current_ram}MB RAM"
echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n"
echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? <yes/No> " echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? <yes/No> "
read -r prompt read -r prompt
if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then
echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" msg_error "Aborted: under-provisioned LXC (${current_cpu} CPU/${current_ram}MB RAM < ${var_cpu} CPU/${var_ram}MB RAM)"
exit 1 exit 1
fi fi
else else
@@ -3152,11 +3153,11 @@ check_container_storage() {
local used_size=$(df /boot --output=used | tail -n 1) local used_size=$(df /boot --output=used | tail -n 1)
usage=$((100 * used_size / total_size)) usage=$((100 * used_size / total_size))
if ((usage > 80)); then if ((usage > 80)); then
echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" msg_warn "Storage is dangerously low (${usage}% used on /boot)"
echo -ne "Continue anyway? <y/N> " echo -ne "Continue anyway? <y/N> "
read -r prompt read -r prompt
if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then
echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" msg_error "Aborted: storage too low (${usage}% used)"
exit 1 exit 1
fi fi
fi fi
@@ -3546,10 +3547,16 @@ build_container() {
# Build PCT_OPTIONS as string for export # Build PCT_OPTIONS as string for export
TEMP_DIR=$(mktemp -d) TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null pushd "$TEMP_DIR" >/dev/null
local _func_url
if [ "$var_os" == "alpine" ]; then if [ "$var_os" == "alpine" ]; then
export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/alpine-install.func)" _func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/alpine-install.func"
else else
export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func)" _func_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/install.func"
fi
export FUNCTIONS_FILE_PATH="$(curl -fsSL "$_func_url")"
if [[ -z "$FUNCTIONS_FILE_PATH" || ${#FUNCTIONS_FILE_PATH} -lt 100 ]]; then
msg_error "Failed to download install functions from: $_func_url"
exit 1
fi fi
# Core exports for install.func # Core exports for install.func
@@ -3920,7 +3927,9 @@ EOF
fi fi
sleep 1 sleep 1
if [ "$i" -eq 10 ]; then if [ "$i" -eq 10 ]; then
msg_error "LXC Container did not reach running state" local ct_status
ct_status=$(pct status "$CTID" 2>/dev/null || echo "unknown")
msg_error "LXC Container did not reach running state (status: ${ct_status})"
exit 1 exit 1
fi fi
done done
@@ -3944,7 +3953,7 @@ EOF
if [ -z "$ip_in_lxc" ]; then if [ -z "$ip_in_lxc" ]; then
msg_error "No IP assigned to CT $CTID after 20s" msg_error "No IP assigned to CT $CTID after 20s"
echo -e "${YW}Troubleshooting:${CL}" msg_custom "🔧" "${YW}" "Troubleshooting:"
echo " • Verify bridge ${BRG} exists and has connectivity" echo " • Verify bridge ${BRG} exists and has connectivity"
echo " • Check if DHCP server is reachable (if using DHCP)" echo " • Check if DHCP server is reachable (if using DHCP)"
echo " • Verify static IP configuration (if using static IP)" echo " • Verify static IP configuration (if using static IP)"
@@ -3966,8 +3975,7 @@ EOF
done done
if [ "$ping_success" = false ]; then if [ "$ping_success" = false ]; then
msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed" msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed - installation will continue"
echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}"
else else
msg_ok "Network in LXC is reachable (ping)" msg_ok "Network in LXC is reachable (ping)"
fi fi
@@ -4011,7 +4019,10 @@ EOF
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF' EOF'
pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" || {
msg_error "Failed to install base packages in Alpine container"
exit 1
}
else else
sleep 3 sleep 3
LANG=${LANG:-en_US.UTF-8} LANG=${LANG:-en_US.UTF-8}
@@ -4908,8 +4919,7 @@ create_lxc_container() {
return 0 return 0
fi fi
echo msg_info "An update for the Proxmox LXC stack is available"
echo "An update for the Proxmox LXC stack is available:"
echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}"
echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}"
echo echo
@@ -4961,7 +4971,6 @@ create_lxc_container() {
exit 205 exit 205
} }
if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
echo -e "ID '$CTID' is already in use."
unset CTID unset CTID
msg_error "Cannot use ID that is already in use." msg_error "Cannot use ID that is already in use."
exit 206 exit 206
@@ -5019,17 +5028,40 @@ create_lxc_container() {
msg_info "Validating storage '$CONTAINER_STORAGE'" msg_info "Validating storage '$CONTAINER_STORAGE'"
STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1) STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1)
if [[ -z "$STORAGE_TYPE" ]]; then
msg_error "Storage '$CONTAINER_STORAGE' not found in /etc/pve/storage.cfg"
exit 213
fi
case "$STORAGE_TYPE" in case "$STORAGE_TYPE" in
iscsidirect) exit 212 ;; iscsidirect)
iscsi | zfs) exit 213 ;; msg_error "Storage '$CONTAINER_STORAGE' uses iSCSI-direct which does not support container rootfs."
cephfs) exit 219 ;; exit 212
pbs) exit 224 ;; ;;
iscsi | zfs)
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support container rootdir content."
exit 213
;;
cephfs)
msg_error "Storage '$CONTAINER_STORAGE' uses CephFS which is not supported for LXC rootfs."
exit 219
;;
pbs)
msg_error "Storage '$CONTAINER_STORAGE' is a Proxmox Backup Server — cannot be used for containers."
exit 224
;;
linstor | rbd | nfs | cifs) linstor | rbd | nfs | cifs)
pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217 if ! pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null; then
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) is not accessible or inactive."
exit 217
fi
;; ;;
esac esac
pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213 if ! pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE"; then
msg_error "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) does not support 'rootdir' content."
exit 213
fi
msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated" msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated"
msg_info "Validating template storage '$TEMPLATE_STORAGE'" msg_info "Validating template storage '$TEMPLATE_STORAGE'"
@@ -5102,8 +5134,7 @@ create_lxc_container() {
# If still no template, try to find alternatives # If still no template, try to find alternatives
if [[ -z "$TEMPLATE" ]]; then if [[ -z "$TEMPLATE" ]]; then
echo "" msg_warn "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..."
echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..."
# Get all available versions for this OS type # Get all available versions for this OS type
AVAILABLE_VERSIONS=() AVAILABLE_VERSIONS=()
@@ -5377,13 +5408,19 @@ create_lxc_container() {
if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH" 2>/dev/null || echo 0)" -lt 1000000 ]]; then
msg_info "Template file missing or too small downloading" msg_info "Template file missing or too small downloading"
rm -f "$TEMPLATE_PATH" rm -f "$TEMPLATE_PATH"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 || {
msg_error "Failed to download template '$TEMPLATE' to storage '$TEMPLATE_STORAGE'"
exit 222
}
msg_ok "Template downloaded" msg_ok "Template downloaded"
elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then
if [[ -n "$ONLINE_TEMPLATE" ]]; then if [[ -n "$ONLINE_TEMPLATE" ]]; then
msg_info "Template appears corrupted re-downloading" msg_info "Template appears corrupted re-downloading"
rm -f "$TEMPLATE_PATH" rm -f "$TEMPLATE_PATH"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1 || {
msg_error "Failed to re-download template '$TEMPLATE'"
exit 222
}
msg_ok "Template re-downloaded" msg_ok "Template re-downloaded"
else else
msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." msg_warn "Template appears corrupted, but no online version exists. Skipping re-download."
@@ -5425,20 +5462,17 @@ create_lxc_container() {
if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then
# Local fallback also failed - check for LXC stack version issue # Local fallback also failed - check for LXC stack version issue
if grep -qiE 'unsupported .* version' "$LOGFILE"; then if grep -qiE 'unsupported .* version' "$LOGFILE"; then
echo msg_warn "pct reported 'unsupported version' LXC stack might be too old for this template"
echo "pct reported 'unsupported ... version' your LXC stack might be too old for this template."
echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
offer_lxc_stack_upgrade_and_maybe_retry "yes" offer_lxc_stack_upgrade_and_maybe_retry "yes"
rc=$? rc=$?
case $rc in case $rc in
0) : ;; # success - container created, continue 0) : ;; # success - container created, continue
2) 2)
echo "Upgrade was declined. Please update and re-run: msg_error "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve"
apt update && apt install --only-upgrade pve-container lxc-pve"
exit 231 exit 231
;; ;;
3) 3)
echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE"
exit 231 exit 231
;; ;;
esac esac
@@ -5457,20 +5491,17 @@ create_lxc_container() {
else else
# Already on local storage and still failed - check LXC stack version # Already on local storage and still failed - check LXC stack version
if grep -qiE 'unsupported .* version' "$LOGFILE"; then if grep -qiE 'unsupported .* version' "$LOGFILE"; then
echo msg_warn "pct reported 'unsupported version' LXC stack might be too old for this template"
echo "pct reported 'unsupported ... version' your LXC stack might be too old for this template."
echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
offer_lxc_stack_upgrade_and_maybe_retry "yes" offer_lxc_stack_upgrade_and_maybe_retry "yes"
rc=$? rc=$?
case $rc in case $rc in
0) : ;; # success - container created, continue 0) : ;; # success - container created, continue
2) 2)
echo "Upgrade was declined. Please update and re-run: msg_error "Upgrade declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve"
apt update && apt install --only-upgrade pve-container lxc-pve"
exit 231 exit 231
;; ;;
3) 3)
echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" msg_error "Upgrade and/or retry failed. Please inspect: $LOGFILE"
exit 231 exit 231
;; ;;
esac esac

View File

@@ -276,7 +276,7 @@ shell_check() {
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
echo -e "\nExiting..." echo -e "\nExiting..."
sleep 2 sleep 2
exit exit 1
fi fi
} }
@@ -293,7 +293,7 @@ root_check() {
msg_error "Please run this script as root." msg_error "Please run this script as root."
echo -e "\nExiting..." echo -e "\nExiting..."
sleep 2 sleep 2
exit exit 1
fi fi
} }
@@ -345,11 +345,10 @@ pve_check() {
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
arch_check() { arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then if [ "$(dpkg --print-architecture)" != "amd64" ]; then
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" msg_error "This script will not work with PiMox (ARM architecture detected)."
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" msg_warn "Visit https://github.com/asylumexp/Proxmox for ARM64 support."
echo -e "Exiting..."
sleep 2 sleep 2
exit exit 1
fi fi
} }
@@ -530,7 +529,9 @@ silent() {
if [[ $rc -ne 0 ]]; then if [[ $rc -ne 0 ]]; then
# Source explain_exit_code if needed # Source explain_exit_code if needed
if ! declare -f explain_exit_code >/dev/null 2>&1; then if ! declare -f explain_exit_code >/dev/null 2>&1; then
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) if ! source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func); then
explain_exit_code() { echo "unknown (error_handler.func download failed)"; }
fi
fi fi
local explanation local explanation
@@ -785,8 +786,8 @@ fatal() {
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
exit_script() { exit_script() {
clear clear
echo -e "\n${CROSS}${RD}User exited script${CL}\n" msg_error "User exited script"
exit exit 0
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -807,6 +808,7 @@ get_header() {
if [ ! -s "$local_header_path" ]; then if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then
msg_warn "Failed to download header: $header_url"
return 1 return 1
fi fi
fi fi
@@ -847,10 +849,10 @@ header_info() {
ensure_tput() { ensure_tput() {
if ! command -v tput >/dev/null 2>&1; then if ! command -v tput >/dev/null 2>&1; then
if grep -qi 'alpine' /etc/os-release; then if grep -qi 'alpine' /etc/os-release; then
apk add --no-cache ncurses >/dev/null 2>&1 apk add --no-cache ncurses >/dev/null 2>&1 || msg_warn "Failed to install ncurses (tput may be unavailable)"
elif command -v apt-get >/dev/null 2>&1; then elif command -v apt-get >/dev/null 2>&1; then
apt-get update -qq >/dev/null apt-get update -qq >/dev/null
apt-get install -y -qq ncurses-bin >/dev/null 2>&1 apt-get install -y -qq ncurses-bin >/dev/null 2>&1 || msg_warn "Failed to install ncurses-bin (tput may be unavailable)"
fi fi
fi fi
} }
@@ -1310,6 +1312,7 @@ prompt_select() {
# Validate options # Validate options
if [[ $num_options -eq 0 ]]; then if [[ $num_options -eq 0 ]]; then
msg_warn "prompt_select called with no options"
echo "" >&2 echo "" >&2
return 1 return 1
fi fi
@@ -1552,22 +1555,30 @@ check_or_create_swap() {
local swap_size_mb local swap_size_mb
swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60) swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60)
if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then
msg_error "Invalid size input. Aborting." msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)"
return 1 return 1
fi fi
local swap_file="/swapfile" local swap_file="/swapfile"
msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" msg_info "Creating ${swap_size_mb}MB swap file at $swap_file"
if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress && if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then
chmod 600 "$swap_file" && msg_error "Failed to allocate swap file (dd failed)"
mkswap "$swap_file" &&
swapon "$swap_file"; then
msg_ok "Swap file created and activated successfully"
else
msg_error "Failed to create or activate swap"
return 1 return 1
fi fi
if ! chmod 600 "$swap_file"; then
msg_error "Failed to set permissions on $swap_file"
return 1
fi
if ! mkswap "$swap_file"; then
msg_error "Failed to format swap file (mkswap failed)"
return 1
fi
if ! swapon "$swap_file"; then
msg_error "Failed to activate swap (swapon failed)"
return 1
fi
msg_ok "Swap file created and activated successfully"
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -1649,7 +1660,7 @@ function get_lxc_ip() {
LOCAL_IP="$(get_current_ip || true)" LOCAL_IP="$(get_current_ip || true)"
if [[ -z "$LOCAL_IP" ]]; then if [[ -z "$LOCAL_IP" ]]; then
msg_error "Could not determine LOCAL_IP" msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)"
return 1 return 1
fi fi
fi fi

View File

@@ -201,6 +201,7 @@ install_packages_with_retry() {
fi fi
done done
msg_error "Failed to install packages after $((max_retries + 1)) attempts: ${packages[*]}"
return 1 return 1
} }
@@ -231,6 +232,7 @@ upgrade_packages_with_retry() {
fi fi
done done
msg_error "Failed to upgrade packages after $((max_retries + 1)) attempts: ${packages[*]}"
return 1 return 1
} }
@@ -675,6 +677,7 @@ verify_repo_available() {
if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then
return 0 return 0
fi fi
msg_warn "Repository not available: ${repo_url} (suite: ${suite})"
return 1 return 1
} }
@@ -839,6 +842,7 @@ github_api_call() {
esac esac
done done
msg_error "GitHub API call failed after ${max_retries} attempts: ${url}"
return 1 return 1
} }
@@ -900,6 +904,7 @@ codeberg_api_call() {
esac esac
done done
msg_error "Codeberg API call failed after ${max_retries} attempts: ${url}"
return 1 return 1
} }
@@ -1369,7 +1374,9 @@ setup_deb822_repo() {
[[ -n "$enabled" ]] && echo "Enabled: $enabled" [[ -n "$enabled" ]] && echo "Enabled: $enabled"
} >/etc/apt/sources.list.d/${name}.sources } >/etc/apt/sources.list.d/${name}.sources
$STD apt update $STD apt update || {
msg_warn "apt update failed after adding repository: ${name}"
}
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -1377,12 +1384,16 @@ setup_deb822_repo() {
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
hold_package_version() { hold_package_version() {
local package="$1" local package="$1"
$STD apt-mark hold "$package" $STD apt-mark hold "$package" || {
msg_warn "Failed to hold package version: ${package}"
}
} }
unhold_package_version() { unhold_package_version() {
local package="$1" local package="$1"
$STD apt-mark unhold "$package" $STD apt-mark unhold "$package" || {
msg_warn "Failed to unhold package version: ${package}"
}
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -1412,6 +1423,7 @@ enable_and_start_service() {
local service="$1" local service="$1"
if ! systemctl enable "$service" &>/dev/null; then if ! systemctl enable "$service" &>/dev/null; then
msg_error "Failed to enable service: $service"
return 1 return 1
fi fi
@@ -1454,6 +1466,7 @@ extract_version_from_json() {
version=$(echo "$json" | jq -r ".${field} // empty") version=$(echo "$json" | jq -r ".${field} // empty")
if [[ -z "$version" ]]; then if [[ -z "$version" ]]; then
msg_warn "JSON field '${field}' is empty in API response"
return 1 return 1
fi fi
@@ -1473,8 +1486,9 @@ get_latest_github_release() {
local temp_file=$(mktemp) local temp_file=$(mktemp)
if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then
msg_warn "GitHub API call failed for ${repo}"
rm -f "$temp_file" rm -f "$temp_file"
return 0 return 1
fi fi
local version local version
@@ -1483,7 +1497,7 @@ get_latest_github_release() {
if [[ -z "$version" ]]; then if [[ -z "$version" ]]; then
msg_error "Could not determine latest version for ${repo}" msg_error "Could not determine latest version for ${repo}"
return 0 return 1
fi fi
echo "$version" echo "$version"
@@ -1499,8 +1513,9 @@ get_latest_codeberg_release() {
# Codeberg API: get all releases and pick the first non-draft/non-prerelease # Codeberg API: get all releases and pick the first non-draft/non-prerelease
if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then
msg_warn "Codeberg API call failed for ${repo}"
rm -f "$temp_file" rm -f "$temp_file"
return 0 return 1
fi fi
local version local version
@@ -1515,7 +1530,7 @@ get_latest_codeberg_release() {
if [[ -z "$version" ]]; then if [[ -z "$version" ]]; then
msg_error "Could not determine latest version for ${repo}" msg_error "Could not determine latest version for ${repo}"
return 0 return 1
fi fi
echo "$version" echo "$version"
@@ -1646,6 +1661,7 @@ get_latest_gh_tag() {
sort -V | tail -n1) sort -V | tail -n1)
if [[ -z "$latest" ]]; then if [[ -z "$latest" ]]; then
msg_warn "No matching tags found for ${repo}${prefix:+ (prefix: $prefix)}"
return 1 return 1
fi fi
@@ -1881,7 +1897,7 @@ check_for_codeberg_release() {
releases_json=$(curl -fsSL --max-time 20 \ releases_json=$(curl -fsSL --max-time 20 \
-H 'Accept: application/json' \ -H 'Accept: application/json' \
"https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || { "https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || {
msg_error "Unable to fetch releases for ${app}" msg_error "Unable to fetch releases for ${app} (codeberg.org/api/v1/repos/${source}/releases)"
return 1 return 1
} }
@@ -2014,12 +2030,12 @@ function download_with_progress() {
if [[ -z "$content_length" ]]; then if [[ -z "$content_length" ]]; then
if ! curl -fL# -o "$output" "$url"; then if ! curl -fL# -o "$output" "$url"; then
msg_error "Download failed" msg_error "Download failed: $url"
return 1 return 1
fi fi
else else
if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then
msg_error "Download failed" msg_error "Download failed: $url"
return 1 return 1
fi fi
fi fi
@@ -2562,7 +2578,10 @@ _gh_scan_older_releases() {
-H 'Accept: application/vnd.github+json' \ -H 'Accept: application/vnd.github+json' \
-H 'X-GitHub-Api-Version: 2022-11-28' \ -H 'X-GitHub-Api-Version: 2022-11-28' \
"${header[@]}" \ "${header[@]}" \
"https://api.github.com/repos/${repo}/releases?per_page=15" 2>/dev/null) || return 1 "https://api.github.com/repos/${repo}/releases?per_page=15" 2>/dev/null) || {
msg_warn "Failed to fetch older releases for ${repo}"
return 1
}
local count local count
count=$(echo "$releases_list" | jq 'length') count=$(echo "$releases_list" | jq 'length')
@@ -3104,7 +3123,9 @@ function setup_composer() {
# Scenario 1: Already installed - just self-update # Scenario 1: Already installed - just self-update
if [[ -n "$INSTALLED_VERSION" ]]; then if [[ -n "$INSTALLED_VERSION" ]]; then
msg_info "Update Composer $INSTALLED_VERSION" msg_info "Update Composer $INSTALLED_VERSION"
$STD "$COMPOSER_BIN" self-update --no-interaction || true $STD "$COMPOSER_BIN" self-update --no-interaction || {
msg_warn "Composer self-update failed, continuing with current version"
}
local UPDATED_VERSION local UPDATED_VERSION
UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}')
cache_installed_version "composer" "$UPDATED_VERSION" cache_installed_version "composer" "$UPDATED_VERSION"
@@ -3140,7 +3161,9 @@ function setup_composer() {
fi fi
chmod +x "$COMPOSER_BIN" chmod +x "$COMPOSER_BIN"
$STD "$COMPOSER_BIN" self-update --no-interaction || true $STD "$COMPOSER_BIN" self-update --no-interaction || {
msg_warn "Composer self-update failed after fresh install"
}
local FINAL_VERSION local FINAL_VERSION
FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}')
@@ -5211,7 +5234,9 @@ function setup_mysql() {
ensure_apt_working || return 1 ensure_apt_working || return 1
# Perform upgrade with retry logic (non-fatal if fails) # Perform upgrade with retry logic (non-fatal if fails)
upgrade_packages_with_retry "mysql-server" "mysql-client" || true upgrade_packages_with_retry "mysql-server" "mysql-client" || {
msg_warn "MySQL package upgrade had issues, continuing with current version"
}
cache_installed_version "mysql" "$MYSQL_VERSION" cache_installed_version "mysql" "$MYSQL_VERSION"
msg_ok "Update MySQL $MYSQL_VERSION" msg_ok "Update MySQL $MYSQL_VERSION"
@@ -5401,7 +5426,9 @@ function setup_nodejs() {
} }
# Force APT cache refresh after repository setup # Force APT cache refresh after repository setup
$STD apt update $STD apt update || {
msg_warn "apt update failed after Node.js repository setup"
}
ensure_dependencies curl ca-certificates gnupg ensure_dependencies curl ca-certificates gnupg
@@ -5644,7 +5671,10 @@ EOF
if [[ "$DISTRO_ID" == "ubuntu" ]]; then if [[ "$DISTRO_ID" == "ubuntu" ]]; then
# Ubuntu: Use ondrej/php PPA # Ubuntu: Use ondrej/php PPA
msg_info "Adding ondrej/php PPA for Ubuntu" msg_info "Adding ondrej/php PPA for Ubuntu"
$STD apt install -y software-properties-common $STD apt install -y software-properties-common || {
msg_error "Failed to install software-properties-common"
return 1
}
# Don't use $STD for add-apt-repository as it uses background processes # Don't use $STD for add-apt-repository as it uses background processes
add-apt-repository -y ppa:ondrej/php >>"$(get_active_logfile)" 2>&1 add-apt-repository -y ppa:ondrej/php >>"$(get_active_logfile)" 2>&1
else else
@@ -5655,7 +5685,9 @@ EOF
} }
fi fi
ensure_apt_working || return 1 ensure_apt_working || return 1
$STD apt update $STD apt update || {
msg_warn "apt update failed after PHP repository setup"
}
# Get available PHP version from repository # Get available PHP version from repository
local AVAILABLE_PHP_VERSION="" local AVAILABLE_PHP_VERSION=""
@@ -5950,7 +5982,9 @@ function setup_postgresql() {
} }
fi fi
$STD systemctl enable --now postgresql 2>/dev/null || true $STD systemctl enable --now postgresql 2>/dev/null || {
msg_warn "Failed to enable/start PostgreSQL service"
}
# Add PostgreSQL binaries to PATH # Add PostgreSQL binaries to PATH
if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then
@@ -5964,7 +5998,9 @@ function setup_postgresql() {
if [[ -n "$PG_MODULES" ]]; then if [[ -n "$PG_MODULES" ]]; then
IFS=',' read -ra MODULES <<<"$PG_MODULES" IFS=',' read -ra MODULES <<<"$PG_MODULES"
for module in "${MODULES[@]}"; do for module in "${MODULES[@]}"; do
$STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || {
msg_warn "Failed to install PostgreSQL module: ${module}"
}
done done
fi fi
} }
@@ -6623,7 +6659,9 @@ function setup_clickhouse() {
ensure_apt_working || return 1 ensure_apt_working || return 1
# Perform upgrade with retry logic (non-fatal if fails) # Perform upgrade with retry logic (non-fatal if fails)
upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || true upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || {
msg_warn "ClickHouse package upgrade had issues, continuing with current version"
}
cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION"
msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" msg_ok "Update ClickHouse $CLICKHOUSE_VERSION"
return 0 return 0
@@ -6758,7 +6796,9 @@ function setup_rust() {
} }
# Update to latest patch version # Update to latest patch version
$STD rustup update "$RUST_TOOLCHAIN" </dev/null || true $STD rustup update "$RUST_TOOLCHAIN" </dev/null || {
msg_warn "Rust toolchain update had issues"
}
# Ensure PATH is updated for current shell session # Ensure PATH is updated for current shell session
export PATH="$CARGO_BIN:$PATH" export PATH="$CARGO_BIN:$PATH"
@@ -7160,7 +7200,10 @@ function setup_docker() {
docker-ce-cli \ docker-ce-cli \
containerd.io \ containerd.io \
docker-buildx-plugin \ docker-buildx-plugin \
docker-compose-plugin docker-compose-plugin || {
msg_error "Failed to update Docker packages"
return 1
}
msg_ok "Updated Docker to $DOCKER_LATEST_VERSION" msg_ok "Updated Docker to $DOCKER_LATEST_VERSION"
else else
msg_ok "Docker is up-to-date ($DOCKER_CURRENT_VERSION)" msg_ok "Docker is up-to-date ($DOCKER_CURRENT_VERSION)"
@@ -7172,7 +7215,10 @@ function setup_docker() {
docker-ce-cli \ docker-ce-cli \
containerd.io \ containerd.io \
docker-buildx-plugin \ docker-buildx-plugin \
docker-compose-plugin docker-compose-plugin || {
msg_error "Failed to install Docker packages"
return 1
}
DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1) DOCKER_CURRENT_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1)
msg_ok "Installed Docker $DOCKER_CURRENT_VERSION" msg_ok "Installed Docker $DOCKER_CURRENT_VERSION"

View File

@@ -288,8 +288,8 @@ function default_settings() {
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}"
if ! grep -q "^iface ${BRG}" /etc/network/interfaces; then if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces" msg_error "Bridge '${BRG}' does not exist"
exit exit
else else
echo -e "${DGN}Using LAN Bridge: ${BGN}${BRG}${CL}" echo -e "${DGN}Using LAN Bridge: ${BGN}${BRG}${CL}"
@@ -305,8 +305,8 @@ function default_settings() {
if [ "$NETWORK_MODE" = "dual" ]; then if [ "$NETWORK_MODE" = "dual" ]; then
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}" echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}" echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
if ! grep -q "^iface ${WAN_BRG}" /etc/network/interfaces; then if ! ip link show "${WAN_BRG}" &>/dev/null; then
msg_error "Bridge '${WAN_BRG}' does not exist in /etc/network/interfaces" msg_error "Bridge '${WAN_BRG}' does not exist"
exit exit
else else
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}" echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
@@ -424,8 +424,8 @@ function advanced_settings() {
if [ -z $BRG ]; then if [ -z $BRG ]; then
BRG="vmbr0" BRG="vmbr0"
fi fi
if ! grep -q "^iface ${BRG}" /etc/network/interfaces; then if ! ip link show "${BRG}" &>/dev/null; then
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces" msg_error "Bridge '${BRG}' does not exist"
exit exit
fi fi
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}" echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
@@ -474,8 +474,8 @@ function advanced_settings() {
if [ -z $WAN_BRG ]; then if [ -z $WAN_BRG ]; then
WAN_BRG="vmbr1" WAN_BRG="vmbr1"
fi fi
if ! grep -q "^iface ${WAN_BRG}" /etc/network/interfaces; then if ! ip link show "${WAN_BRG}" &>/dev/null; then
msg_error "WAN Bridge '${WAN_BRG}' does not exist in /etc/network/interfaces" msg_error "WAN Bridge '${WAN_BRG}' does not exist"
exit exit
fi fi
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}" echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"