Compare commits

...

101 Commits

Author SHA1 Message Date
community-scripts-pr-app[bot] bb4c1f2fbf Update CHANGELOG.md (#18458)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-04 16:00:26 +00:00
community-scripts-pr-app[bot] 793fba1a53 Update CHANGELOG.md (#18457)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-04 15:54:56 +00:00
community-scripts-pr-app[bot] 06b788ee81 Update CHANGELOG.md (#18454)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-04 09:23:17 +00:00
CanbiZ (MickLesk) 667da6a8fb Install n8n past its unsatisfiable canvas dependencies (#18439)
n8n-nodes-base 2.46.0 moved @napi-rs/canvas-linux-arm64-musl and
-linux-x64-musl from optional into hard dependencies. npm enforces
os/cpu/libc on those, and no combination satisfies both arm64 and x64,
so every install fails with EBADPLATFORM. n8n 2.46.4 reached the latest
tag on 2026-11-03 and the failures started four minutes later.

--force skips the platform check. The matching gnu binary is still
installed through @napi-rs/canvas's own optional dependencies, so canvas
keeps working.
2026-11-04 10:22:46 +01:00
community-scripts-pr-app[bot] e40a258a27 Update CHANGELOG.md (#18452)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-04 07:04:53 +00:00
CanbiZ (MickLesk) ff5aac3889 Teable: add Redis cache (fixes #18449) (#18450) 2026-11-04 08:04:23 +01:00
community-scripts-pr-app[bot] e00d14a3ce Update CHANGELOG.md (#18445)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 15:45:50 +00:00
CanbiZ (MickLesk) 5ee75b7460 Wiki.js: install 3.x (#18382)
* Wiki.js: install 3.x

Wiki.js 3 lives in requarks/wiki, needs Node.js 26 and creates its own ltree and pg_trgm extensions in the wiki schema, and starts with node --no-experimental-webstorage backend. dataPath moves out of the app folder, which every update replaces; the admin password is generated and saved to ~/wikijs.creds. Updates follow upstream's guide and put Puppeteer back when it was installed. 2.x containers keep getting 2.x updates from requarks/wiki-v2, since 3.x cannot upgrade them in place, and are told how to export and import.

* Refactor credential output to use heredoc syntax
2026-11-03 16:45:19 +01:00
community-scripts-pr-app[bot] 069ab1b8df Update CHANGELOG.md (#18444)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:59:39 +00:00
community-scripts-pr-app[bot] 209eb2a003 Update CHANGELOG.md (#18443)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:59:24 +00:00
community-scripts-pr-app[bot] 594b1e82bb Update CHANGELOG.md (#18442)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:59:05 +00:00
CanbiZ (MickLesk) bffc871aa9 Flag instead of closing on first PR template miss; add script-request eligibility check (#18384)
close-invalid-pr-template.yml now comments and labels (missing pr template,
stale) instead of closing immediately, clears itself once fixed. stale_pr_close.yml
gains the engagement-gated/keep-open/maintainer-reset behavior already used in
DevScripts, so a template-stale PR only closes via its own re-check.

request-script.yml discussion template gets an optional Justification field;
a new workflow verifies stars/age against GitHub for the submitted repo and
either does nothing (eligible), labels investigation (ineligible + justified),
or closes the discussion (ineligible, no justification).

Moved the AI Assistance section to the end of pull_request_template.md.
2026-11-03 14:58:57 +01:00
CanbiZ (MickLesk) d77c65093b Say why an update-apps backup or restore failed (#18405)
vzdump and pct restore ran with their output thrown away, so a failed
backup only said "Backup failed for container N" (exit 235). That is the
second most common update-apps failure and none of them can be told
apart. Keep the output in the run log and put its error line in the
message, e.g. "CT is locked (backup)".

var_backup_storage was matched with grep -w, so "truenas" passed when
only "truenas-backup" exists and vzdump then failed on a storage that
does not exist. Match the whole name, and list the valid ones in the
error instead of a spinner line after it.

Name the app in "Update failed for container N", so a failed update
says which app's update failed. Declining the start dialog now exits 0
like the other tools instead of failing with whiptail's 1.
2026-11-03 14:58:35 +01:00
community-scripts-pr-app[bot] f51d21ff9a Update CHANGELOG.md (#18441)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:32:03 +00:00
community-scripts-pr-app[bot] 27493df7a0 Update CHANGELOG.md (#18440)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:31:40 +00:00
CanbiZ (MickLesk) 0973d1ccfb OpenWrt VM: read the stable release from downloads.openwrt.org (#18411)
* OpenWrt VM: read the stable release from downloads.openwrt.org

The version came from a line of text on the openwrt.org wiki front page.
One host got that page without the line three times in a row while
others resolved fine at the same moment, and the run ended with exit 115
before anything was downloaded. .versions.json on the download server
is what the OpenWrt firmware selector reads and names the stable release
explicitly.

* Update vm/openwrt-vm.sh

Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>

---------

Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-11-03 14:31:32 +01:00
CanbiZ (MickLesk) 11acd8db75 Keep host tools running when clear does not know the terminal (#18412)
clear exits 1 for a TERM the host has no terminfo entry for, such as
xterm-ghostty or xterm-kitty over SSH, and when TERM is unset under cron.
The tools that run under errexit died on it in header_info within a
second, before showing anything, which telemetry counts as
"exit_code=1 | General error" with no step for update-lxcs, update-repo,
fstrim, clean-lxcs, execute and storage-share-helper.

Fall back to the escape sequence the engine's _cs_clear uses.
2026-11-03 14:30:57 +01:00
community-scripts-pr-app[bot] e39450ec48 Update CHANGELOG.md (#18438)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:26:52 +00:00
CanbiZ (MickLesk) 2c4fbc01eb all-templates: answering No or Cancel is not a failure (#18423)
Both whiptail prompts ran bare under errexit, so No on the first question
or Cancel in the template list ended in the error handler and was reported
as a failed run (3 of 5 failures in three days).

Monitor-All is now stopped right before the download instead of at the
start, so leaving at a prompt no longer leaves it stopped.
2026-11-03 14:26:31 +01:00
CanbiZ (MickLesk) dd7a659f22 homebrew: skip service accounts when picking the user, survive a failed apt update (#18424)
The user was the first passwd entry with a UID from 1000 up. On a Proxmox
host with Ceph that is ceph (UID 64045, shell nologin), and su - ceph fails
with "This account is currently not available". Accounts without a login
shell are skipped now.

apt update on a host with the enterprise repository and no subscription exits
100 on the 401. apt_update_safe retries, says why and carries on, as the other
scripts already do since #17462.
2026-11-03 14:26:23 +01:00
CanbiZ (MickLesk) f9039c2ee7 Fedora VM: let the engine install libguestfs-tools (#18422)
The script ran its own apt-get update before installing
libguestfs-tools, so one broken or stale repository on the host (a 404
for a hand-added proxmox.com or debian.org entry) ended the run with
exit 100. vm_prepare_cloud_image installs the tools itself and carries
on when an update fails, so the install can still succeed from the
repositories that did update.
2026-11-03 14:26:21 +01:00
community-scripts-pr-app[bot] 44666826df Update CHANGELOG.md (#18437)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:24:46 +00:00
community-scripts-pr-app[bot] 64a89e3463 Update CHANGELOG.md (#18436)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:24:19 +00:00
CanbiZ (MickLesk) 0d4e703d70 Cinephage: run the camoufox binary the package now ships (#18426)
Cinephage depends on camoufox instead of camoufox-js, whose bin is called camoufox, so the install stopped at "node_modules/.bin/camoufox-js: No such file or directory" (6 runs in three days).
2026-11-03 14:24:13 +01:00
CanbiZ (MickLesk) 5fa9846d37 Immich: fetch VectorChord from its new repo, skip Java, get GeoNames first (#18397)
- VectorChord moved to supervc-stack/VectorChord. With the GitHub API
  rate limited, core reads the release from github.com, finds no asset
  under the old name and stops with "No suitable .deb asset found".
- mise installed every tool in Immich's mise.toml. Java (for the Dart
  OpenAPI generator), terragrunt and opentofu are not needed for the
  plugin build, and the Java index timed out on slow links.
- GeoNames failed after the 1-3 h library build; it now downloads first.
- Stop on x86-64 CPUs without SSE4.2: the web build ends there with
  "Illegal instruction" after the libraries are built.
2026-11-03 14:23:44 +01:00
community-scripts-pr-app[bot] e32161b18a Update CHANGELOG.md (#18435)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:23:29 +00:00
CanbiZ (MickLesk) 92737744ce AlmaLinux VM: report failures through the engine's error handler (#18418)
The script replaced the engine's error_handler with one that sent the
failed command as the exit code, so every failure reached telemetry as
exit 199 "exit code not reported", without the failed command or
category. Its signal traps sent words instead of codes the same way.

It also ran its own unguarded apt-get update for libguestfs-tools, so a
host with the enterprise repository and no subscription (401) stopped
there. vm_prepare_cloud_image installs the tools itself, tolerating a
repository that fails to update.
2026-11-03 14:23:03 +01:00
CanbiZ (MickLesk) 2fbb6e261c add-tailscale-lxc: start a stopped container and check its OS first (#18421)
The container menu lists stopped containers too, and pct exec on one exits
255: 12 of 41 failures in three days. Start it the way add-netbird-lxc does
and wait for its default route.

The install runs under bash, which an Alpine container from the template does
not have; lxc-attach then exits 127 before the Alpine branch is reached.
Install bash there first, and stop with a clear message on any OS other than
Debian, Ubuntu or Alpine instead of failing on apt-get.
2026-11-03 14:22:52 +01:00
CanbiZ (MickLesk) 39b3ed29ad WireGuard: give the Debian container 1 GB for WGDashboard (#18419)
WGDashboard builds its frontend with Vite during the install, and at 512 MB the kernel killed node: "Failed to compile frontend", then wg-dashboard would not start (11 runs in three days). The build finishes at 1 GB.
2026-11-03 14:20:44 +01:00
community-scripts-pr-app[bot] 9bdc6ae13b Update CHANGELOG.md (#18434)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:14:47 +00:00
community-scripts-pr-app[bot] 9aff2f1090 Update CHANGELOG.md (#18433)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:14:34 +00:00
CanbiZ (MickLesk) d6e4427ac9 zabbix: use os specific apt packages (#18417)
* Zabbix: install on Debian 12 again

The install and the update fetched Zabbix's release package for Debian 13 and php8.4-pgsql, so a Debian 12 container stopped with "Unable to locate package php8.4-pgsql" (12 runs in three days). Zabbix publishes 7.0 and 7.4 for Debian 12 too: take the container's Debian version and the distribution's php-pgsql.

* Zabbix: leave the update as it was

The update refuses Debian 12 on purpose, so its release URL stays as it was.
2026-11-03 14:14:17 +01:00
CanbiZ (MickLesk) 770d89bb90 Refuse to run the PBS 4 upgrade on a Proxmox VE host (#18416)
Nothing checked that the host is a Backup Server. On a PVE host the
first step stopped only because tar found no /etc/proxmox-backup (exit
2); answering "no" to that backup would have gone on to delete
/etc/apt/sources.list.d/*.list, add the PBS repositories and
dist-upgrade. Stop with the same message post-pbs-install uses.
2026-11-03 14:14:11 +01:00
community-scripts-pr-app[bot] 3aef3bb084 Update CHANGELOG.md (#18432)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:13:16 +00:00
CanbiZ (MickLesk) e2b09a9fcd Discourse: build libvips 8.18 for its image worker (#18430)
Discourse enabled libvips image processing by default and its worker refuses anything older than 8.18; Debian 13 ships 8.16, so the service failed to boot after an update and on fresh installs. Build the latest libvips release from source like discourse_docker does, on install and on update when a new release is out.
2026-11-03 14:13:09 +01:00
CanbiZ (MickLesk) 96710d22f2 netdata: survive a failed apt update, leave quietly on Cancel (#18427)
The add-on runs on the Proxmox host, where the enterprise repository without a
subscription answers 401 and apt update exits 100 (2 of 3 failures in three
days). apt_update_safe retries, points at the enterprise repository and
carries on; the netdata repository was fetched fine.

Cancel in the first menu was reported as a failed run.
2026-11-03 14:12:57 +01:00
CanbiZ (MickLesk) cfecd0ed05 Wazuh: show the installer's own error when it fails (#18428)
wazuh-install.sh writes everything to ~/wazuh-install.output, so a failed run left only "bash exited with status 1" in the log and the telemetry (8 runs in three days, three of them with the default resources). The end of that output now goes into the install log.
2026-11-03 14:12:50 +01:00
community-scripts-pr-app[bot] 480ba6d90f Update CHANGELOG.md (#18431)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 13:01:23 +00:00
CanbiZ (MickLesk) 61dcda815a Keep trimming when pct fstrim fails on one container (#18413)
pct fstrim exits 255 when the container is locked, for example while a
backup runs ("CT is locked (backup)"). Under errexit the command
substitution ended the whole run there, before the output check below it
and before the remaining containers, and telemetry filed it as
"DPKG: Fatal internal error". Print the result and go on instead.
2026-11-03 14:00:54 +01:00
CanbiZ (MickLesk) 2340816c4e Do not abort execute-lxcs on a container that is not Debian or Ubuntu (#18414)
The skip message printed $name, which is only set once a container has
run the command. When a non-Debian container (Alpine, for example) came
before any Debian one, set -u ended the run there with "name: unbound
variable" and exit 1; later it printed the previous container's name.
2026-11-03 13:59:54 +01:00
CanbiZ (MickLesk) 7700d7ce36 Offer only container-capable storages as the converter's restore target (#18415)
The target list came from storages with "images" content, but pct
restore puts the container's rootfs there and refuses a storage without
"rootdir" ("storage 'X' does not support container directories"). A
storage that holds VM disks only was offered, and picking it ended the
run after the backup with "Conversion failed" (exit 235).
2026-11-03 13:59:51 +01:00
community-scripts-pr-app[bot] d701955118 Update CHANGELOG.md (#18429)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 12:54:54 +00:00
CanbiZ (MickLesk) 8dc0d17740 Let post-pve-install go on when legacy sources are kept (#18403)
On a PVE 9 host without deb822 .sources files the script asks whether to
disable the legacy .list sources, then read the answer from $? on the
next line. Under set -e a "No" ended the whole run with exit 1 right
there, so the "Kept legacy sources as-is" branch never ran and nothing
after it was offered. Test whiptail in the if instead.
2026-11-03 13:54:14 +01:00
community-scripts-pr-app[bot] 17ac5c491c Update CHANGELOG.md (#18410)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:56:15 +00:00
community-scripts-pr-app[bot] 111d9e189e Update CHANGELOG.md (#18409)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:55:57 +00:00
community-scripts-pr-app[bot] 4910c72f81 Update CHANGELOG.md (#18408)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:55:42 +00:00
community-scripts-pr-app[bot] 93fbb2de00 Update CHANGELOG.md (#18407)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:55:11 +00:00
community-scripts-pr-app[bot] 9bf0070762 Update CHANGELOG.md (#18402)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:35:54 +00:00
community-scripts-pr-app[bot] 35c43ad344 Update CHANGELOG.md (#18401)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:35:19 +00:00
community-scripts-pr-app[bot] d9abcadd01 Update CHANGELOG.md (#18400)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:18:22 +00:00
community-scripts-pr-app[bot] 9e879e8e0c Update CHANGELOG.md (#18399)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 09:01:48 +00:00
community-scripts-pr-app[bot] 413fbcf58a Update CHANGELOG.md (#18398)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 08:57:59 +00:00
community-scripts-pr-app[bot] bc9d4f19ae Update CHANGELOG.md (#18396)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 08:48:28 +00:00
community-scripts-pr-app[bot] 9c9bbb4995 Update CHANGELOG.md (#18395)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 08:48:11 +00:00
community-scripts-pr-app[bot] 5e5513522f Update CHANGELOG.md (#18394)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-03 08:09:03 +00:00
Connor Gallopo 1820e2f772 move tracearr to node 26 (#18393) 2026-11-03 09:08:34 +01:00
community-scripts-pr-app[bot] 2fbdddefd0 Update CHANGELOG.md (#18385)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-02 10:43:35 +00:00
community-scripts-pr-app[bot] 9f20cf42a6 Update CHANGELOG.md (#18383)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-02 10:20:24 +00:00
CanbiZ (MickLesk) 62be65b496 FileFlows: replace the program folders on update (#18380)
Since the release of 2026-10-28, Server/ holds only FileFlows' own assemblies and the rest comes from ../Libraries. Copying the archive over an older install left the old third-party DLLs in Server/, which the runtime loads first, and the server fails with 'Could not load file or assembly Microsoft.Data.Sqlite, Version=10.0.13.0'. Upstream's own upgrade removes Server, FlowRunner, Libraries and Agent the same way; Data stays.
2026-11-02 11:19:59 +01:00
community-scripts-pr-app[bot] 24ebf4d5ac Update CHANGELOG.md (#18381)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-02 08:49:14 +00:00
community-scripts-pr-app[bot] 047819b7f3 Update CHANGELOG.md (#18379)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-02 08:08:34 +00:00
github-actions[bot] 21b7a89a88 anchor: bump Node.js from 24 to 26 (#18373)
* anchor: bump Node.js from 24 to 26

* Add NODE_VERSION and NODE_MODULE to setup_nodejs

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-11-02 09:07:55 +01:00
community-scripts-pr-app[bot] 1c3f6a9f89 Update CHANGELOG.md (#18378)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-02 08:00:33 +00:00
github-actions[bot] e5535c2bfe guardian: bump Node.js from 24 to 26 (#18375)
* guardian: bump Node.js from 24 to 26

* Set NODE_VERSION to 26 before updating Guardian

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-11-02 09:00:09 +01:00
github-actions[bot] 236242ee86 nametag: bump Node.js from 20 to 24 (#18376)
* nametag: bump Node.js from 20 to 24

* Update NODE_VERSION in nametag deployment script

Set NODE_VERSION to 24 during deployment process.

* Increase default RAM from 2048MB to 3072MB

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
2026-11-02 09:00:01 +01:00
community-scripts-pr-app[bot] 7774a1a28d Update CHANGELOG.md (#18371)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 20:22:29 +00:00
push-app-to-main[bot] e82cc0efc8 Amazon Linux (#18369)
* Add amazonlinux (ct)

* minor fixes

---------

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>
2026-11-01 21:21:56 +01:00
community-scripts-pr-app[bot] a87c7556a9 Update CHANGELOG.md (#18370)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 20:20:39 +00:00
push-app-to-main[bot] d3abb64d4d Add cachyos-vm (vm) (#18368)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-11-01 21:20:14 +01:00
community-scripts-pr-app[bot] f0cf766ec5 Update CHANGELOG.md (#18367)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 20:18:46 +00:00
push-app-to-main[bot] b3df8b8b8f Razzia (#18366)
* Add razzia (ct)

* arm64

---------

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>
2026-11-01 21:18:19 +01:00
community-scripts-pr-app[bot] 60722fe556 Update CHANGELOG.md (#18364)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 10:18:33 +00:00
Nico 8ca359311c fix(zerobyte): build the bundled local agent (#18363) 2026-11-01 11:18:04 +01:00
community-scripts-pr-app[bot] 95c2dc0687 Update CHANGELOG.md (#18358)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 00:04:00 +00:00
community-scripts-pr-app[bot] 8c1618aac2 Archive old changelog entries (#18357)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-01 00:03:35 +00:00
community-scripts-pr-app[bot] 25b9f5f8c7 Update CHANGELOG.md (#18356)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 21:30:12 +00:00
CanbiZ (MickLesk) 694f899575 Debian VM: keep a static IP without a snippets storage (#18346)
The MAC-matched network snippet is a workaround for Ubuntu 26.04's NIC rename. Without a storage for snippets the run failed after the VM was built; Debian now keeps the static ipconfig0 Proxmox already set, as it did before September.
2026-10-31 22:29:45 +01:00
community-scripts-pr-app[bot] b9a2d39851 Update CHANGELOG.md (#18355)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 21:14:13 +00:00
CanbiZ (MickLesk) bcb72dc9b8 Ubuntu VM: check for a snippets storage before building a static-IP VM (#18347)
Ubuntu 26.04 needs the MAC-matched network snippet, and without a storage for snippets the run failed only after the VM was created. It now stops before downloading the image.
2026-10-31 22:13:45 +01:00
community-scripts-pr-app[bot] e65ff59c97 Update CHANGELOG.md (#18354)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 20:52:06 +00:00
CanbiZ (MickLesk) f4388eacb3 Docker VM: take the imported disk from vm_import_disk (#18341)
Same parser as haos-vm: PVE 9.2's import message and the pvesm fallback both missed the new volume.
2026-10-31 21:51:31 +01:00
community-scripts-pr-app[bot] 33f8cd35cd Update CHANGELOG.md (#18353)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 20:46:32 +00:00
community-scripts-pr-app[bot] d19be124e4 Update CHANGELOG.md (#18351)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 20:46:11 +00:00
CanbiZ (MickLesk) a03b9d6471 Twenty: move to Node.js 26 (#18337)
Twenty v2.54 requires node ^26.5.1 and yarn install refuses Node 24.
2026-10-31 21:46:03 +01:00
CanbiZ (MickLesk) 5598553755 Norish: fetch Obscura from the repo Norish pins (#18338)
* Norish: fetch Obscura from the repo Norish pins

Norish now builds Obscura from norish-recipes/obscura (pin.env OBSCURA_REPO); the v0.2.5-norish.1 tag does not exist in h4ckf0r0day/obscura, so installs and updates stopped with a GitHub 404.

* Norish: run the v0.26 server on Node.js 26

v0.26.0 dropped dist-server and starts with node --import tsx server/index.ts from apps/web, and requires Node 26. Updates rewrite the unit.
2026-10-31 21:45:48 +01:00
community-scripts-pr-app[bot] ff9dabb4f4 Update CHANGELOG.md (#18350)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 20:45:02 +00:00
community-scripts-pr-app[bot] fa4d0d1ede Update CHANGELOG.md (#18349)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 20:44:39 +00:00
CanbiZ (MickLesk) 507d70ad57 HAOS VM: take the imported disk from vm_import_disk (#18342)
PVE 9.2 reports the import as 'Successfully imported disk as unused0:...', which the local parser missed, and its pvesm fallback matched the VMID column, so imports on local-zfs ended in 'Unable to determine imported disk reference'.
2026-10-31 21:44:33 +01:00
CanbiZ (MickLesk) 5e78d293e6 Aurral: build the pnpm workspace and run backend/server.ts (#18340)
* Aurral: build the pnpm workspace and run backend/server.ts

v2.12.0 moved from npm workspaces to pnpm and the backend entry to server.ts, so npm ci --workspace frontend found no workspace. Updates point existing units at server.ts.

* Aurral: install only the frontend and backend packages

The workspace root's prepare script runs vp config, which needs git; the upstream image filters the install to the two packages as well.

* Aurral: install git for the workspace prepare script

The root prepare script runs vp config, which aborts when git is missing and only notes the absent .git directory otherwise.
2026-10-31 21:44:15 +01:00
CanbiZ (MickLesk) 35de3d31cd PS5-MQTT: build the yarn workspace and run the packaged server (#18339)
* PS5-MQTT: build the yarn workspace and run the packaged server

v1.8.0 turned the repo into a yarn 4 workspace with Plug'n'Play: ps5-mqtt/package.json is gone and npm install there fails. The server now runs from the packaged tree with its own node_modules, as upstream's image does; updates move the unit's working directory.

* PS5-MQTT: give the build 2 GB of RAM

Building the client and server bundles with rspack is killed by the OOM killer at 512 MB.
2026-10-31 21:44:09 +01:00
community-scripts-pr-app[bot] 08f9c31c92 Update CHANGELOG.md (#18345)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 18:55:37 +00:00
community-scripts-pr-app[bot] 9d364c3d4e Update CHANGELOG.md (#18344)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 18:53:41 +00:00
community-scripts-pr-app[bot] 8a5daca0ed Update CHANGELOG.md (#18343)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 18:52:21 +00:00
community-scripts-pr-app[bot] f34cc91d62 Update CHANGELOG.md (#18336)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 15:14:08 +00:00
CanbiZ (MickLesk) c566b53552 Drop the legacy engine copies from misc/ (#18294)
* Drop the legacy engine copies from misc/

Nothing has loaded misc/*.func since ct/, install/, vm/ and tools/ moved to
community-scripts/core. misc/api.func stays until vm/owncloud-vm.sh gets its
DevScripts rewrite.

misc/images/ moves to .github/assets/. The tombstone stub for removed scripts
sourced misc/build.func and called variables(), which core's PVE engine does
not define; it now sources core.func.

* Drop misc/api.func

vm/owncloud-vm.sh was its last caller; DevScripts holds that script's rewrite
onto core/pve/vm-core.func. Merge this after the promotion.
2026-11-01 01:13:41 +10:00
community-scripts-pr-app[bot] 0e65b9469e Update CHANGELOG.md (#18335)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 15:10:35 +00:00
Florian ef7f811a7a Jellyseerr, Overseerr, Paperless-ngx, Vikunja: skip migration prompts when unattended (#18321)
* fix(jellyseerr,overseerr): skip the Seerr migration prompt when unattended

Both scripts ask whether to migrate to Seerr before rewriting /usr/bin/update.
With PHS_SILENT=1 and stdin open the read blocks and stalls the whole batch;
with stdin closed it fails under the ERR trap, so the container is recorded as
FAILED and the backup is restored.

Skip with exit 75, which update-apps.sh already renders as "skipped - requires
interactive mode". Interactive behaviour is unchanged.

* fix(paperless-ngx): skip migration prompts when unattended

Two prompts can appear during an update: the v3 encryption confirmation and the
uv-migration snapshot confirmation. Unattended, the first either blocks or fails
the container; the second sits after the services are already stopped, so a
failure leaves Paperless down.

Guard both with exit 75. The v3 guard sits behind the encryption notices, so a
skip still logs why. The uv guard is placed before the services are stopped, so
skipping never leaves the container half-updated. Interactive behaviour is
unchanged.

* fix(vikunja): skip the 1.0 migration prompt when unattended

The 1.0 config migration asks twice with a 30 second timeout and exits 254 when
it gets no answer, which update-apps.sh treats as a failed container. The config
file choice that follows also waits up to 60 seconds.

Skip the migration prompt with exit 75 when unattended, and take the documented
default (keep the current config) for the second question without waiting.
Interactive behaviour is unchanged.
2026-11-01 01:10:10 +10:00
community-scripts-pr-app[bot] b357b7843d Update CHANGELOG.md (#18334)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 12:24:18 +00:00
CanbiZ (MickLesk) daf1a50364 Discourse: track the Ruby version Discourse requires (#18329)
* fix(discourse): track the Ruby version Discourse requires

Discourse main now pins `ruby "~> 4.0.0"` in its Gemfile, so `bundle install`
aborts with exit 18 on containers built against the hardcoded Ruby 3.4.4. The
update path never touched Ruby at all, so every existing install breaks.

Discourse ships no .ruby-version, so read the pinned version from
Gemfile.lock's RUBY VERSION stanza and hand it to setup_ruby — on install
(repo is now cloned before the runtime setup) and on update (after the git
pull, so a bumped requirement is picked up). Also silence bundler's root
warning, split the update into separate progress steps, and skip the
unused global Rails gem.

* move ruby into msg block

* move git

Removed comment about Ruby version in install script.
2026-10-31 13:23:52 +01:00
community-scripts-pr-app[bot] 1d190bdf25 Update CHANGELOG.md (#18333)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-31 11:12:33 +00:00
Tom Frenzel 2ace018034 MatterJS-Server: Add time sync (#18330)
* feat(matterjs-server): add time sync

* chore(matterjs-server): remove linebreaks
2026-10-31 21:12:08 +10:00
107 changed files with 1494 additions and 25272 deletions
+18 -2
View File
@@ -58,9 +58,9 @@ body:
Before continuing, verify that the requested project meets the minimum requirements below.
**Projects with fewer than 1,000 stars are generally not eligible for a script request.**
**Projects with fewer than 1,000 stars, or younger than 6 months, are generally not eligible for a script request.**
Exceptions are only considered where there is a clearly verifiable, significant public adoption signal.
An automated check verifies the star count and project age for GitHub-hosted projects. If the project doesn't meet these requirements, fill in **Justification for an exception** below — without one, the request is closed automatically. With one, it's flagged for manual review instead. Exceptions are rare and require a clearly verifiable, significant public adoption signal.
- type: input
id: repository-stars
@@ -134,6 +134,22 @@ body:
- label: I understand that requests outside the project scope may be closed without an extensive explanation.
required: true
- type: textarea
id: justification
attributes:
label: Justification for an exception
description: >-
Only needed if the project has fewer than 1,000 stars or is younger than 6 months (e.g. it has no GitHub
presence, so stars don't apply). Explain the significant public adoption signal. Leave blank if the project
meets the normal requirements — requests that don't meet the requirements and have no justification here are
closed automatically.
placeholder: |
Example:
No public GitHub repository, but the project has 50k+ active installs reported by its own telemetry
dashboard and is covered by several established self-hosting publications.
validations:
required: false
- type: markdown
attributes:
value: |

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

+1 -13
View File
@@ -77,17 +77,6 @@
"excludeGlobs": []
}
],
"core": [
{
"fileStatus": null,
"includeGlobs": [
"misc/*.func"
],
"excludeGlobs": [
"misc/api.func"
]
}
],
"documentation": [
{
"fileStatus": null,
@@ -114,8 +103,7 @@
{
"fileStatus": null,
"includeGlobs": [
"api/**",
"misc/api.func"
"api/**"
],
"excludeGlobs": []
}
+221
View File
@@ -1,3 +1,224 @@
## 2026-10-31
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Docker VM: take the imported disk from vm_import_disk [@MickLesk](https://github.com/MickLesk) ([#18341](https://github.com/community-scripts/ProxmoxVE/pull/18341))
- Twenty: move to Node.js 26 [@MickLesk](https://github.com/MickLesk) ([#18337](https://github.com/community-scripts/ProxmoxVE/pull/18337))
- Norish: fetch Obscura from the repo Norish pins [@MickLesk](https://github.com/MickLesk) ([#18338](https://github.com/community-scripts/ProxmoxVE/pull/18338))
- HAOS VM: take the imported disk from vm_import_disk [@MickLesk](https://github.com/MickLesk) ([#18342](https://github.com/community-scripts/ProxmoxVE/pull/18342))
- Aurral: build the pnpm workspace and run backend/server.ts [@MickLesk](https://github.com/MickLesk) ([#18340](https://github.com/community-scripts/ProxmoxVE/pull/18340))
- PS5-MQTT: build the yarn workspace and run the packaged server [@MickLesk](https://github.com/MickLesk) ([#18339](https://github.com/community-scripts/ProxmoxVE/pull/18339))
- Jellyseerr, Overseerr, Paperless-ngx, Vikunja: skip migration prompts when unattended [@florianpunz](https://github.com/florianpunz) ([#18321](https://github.com/community-scripts/ProxmoxVE/pull/18321))
- Discourse: track the Ruby version Discourse requires [@MickLesk](https://github.com/MickLesk) ([#18329](https://github.com/community-scripts/ProxmoxVE/pull/18329))
- #### ✨ New Features
- MatterJS-Server: Add time sync [@tomfrenzel](https://github.com/tomfrenzel) ([#18330](https://github.com/community-scripts/ProxmoxVE/pull/18330))
- #### 🔧 Refactor
- Debian VM: keep a static IP without a snippets storage [@MickLesk](https://github.com/MickLesk) ([#18346](https://github.com/community-scripts/ProxmoxVE/pull/18346))
- Ubuntu VM: check for a snippets storage before building a static-IP VM [@MickLesk](https://github.com/MickLesk) ([#18347](https://github.com/community-scripts/ProxmoxVE/pull/18347))
### 💾 Core
- feat: add setup_java_memory: size Java services from the CT's RAM on Proxmox [@MickLesk](https://github.com/MickLesk) ([core#181](https://github.com/community-scripts/core/pull/181))
- analytics: name the Proxmox tasks when a container stops mid-install [@MickLesk](https://github.com/MickLesk) ([core#184](https://github.com/community-scripts/core/pull/184))
- PVE: do not abort when the spinner PID file cannot be written [@MickLesk](https://github.com/MickLesk) ([core#183](https://github.com/community-scripts/core/pull/183))
### 📂 Github
- Drop the legacy engine copies from misc/ [@MickLesk](https://github.com/MickLesk) ([#18294](https://github.com/community-scripts/ProxmoxVE/pull/18294))
## 2026-10-30
### 🆕 New Scripts
- Reconya ([#18325](https://github.com/community-scripts/ProxmoxVE/pull/18325))
- Blinko ([#18324](https://github.com/community-scripts/ProxmoxVE/pull/18324))
- ChartDB ([#18323](https://github.com/community-scripts/ProxmoxVE/pull/18323))
## 2026-10-29
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Karakeep: fix monolith release asset pattern [@MohamedBassem](https://github.com/MohamedBassem) ([#18317](https://github.com/community-scripts/ProxmoxVE/pull/18317))
- Vikunja: fold the package's service block into the config's service section [@MickLesk](https://github.com/MickLesk) ([#18311](https://github.com/community-scripts/ProxmoxVE/pull/18311))
- Stirling-PDF: size JVM heap from container memory, not host RAM [@aashishvanand](https://github.com/aashishvanand) ([#18292](https://github.com/community-scripts/ProxmoxVE/pull/18292))
- fix(authentik): migrate systemd units when still on pre-2026.8 paths [@CervezaStallone](https://github.com/CervezaStallone) ([#18307](https://github.com/community-scripts/ProxmoxVE/pull/18307))
- fix(dawarich): serve .mjs MapLibre modules as JavaScript [@ricbaral](https://github.com/ricbaral) ([#18304](https://github.com/community-scripts/ProxmoxVE/pull/18304))
- #### ✨ New Features
- VictoriaLogs: enable syslog ingestion on TCP/UDP 514 [@opastorello](https://github.com/opastorello) ([#18300](https://github.com/community-scripts/ProxmoxVE/pull/18300))
### 💾 Core
- feat: Performance - load host data in the background and stop reporting failed pre-checks [@MickLesk](https://github.com/MickLesk) ([core#179](https://github.com/community-scripts/core/pull/179))
### 📚 Documentation
- gh: let the bot set oidc_url and license [@MickLesk](https://github.com/MickLesk) ([#18313](https://github.com/community-scripts/ProxmoxVE/pull/18313))
## 2026-10-28
### 🆕 New Scripts
- VictoriaLogs ([#18282](https://github.com/community-scripts/ProxmoxVE/pull/18282))
- Deemix ([#18272](https://github.com/community-scripts/ProxmoxVE/pull/18272))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(vm/opnsense): increase startup wait from 90 to 180 seconds [@MTlyx](https://github.com/MTlyx) ([#18086](https://github.com/community-scripts/ProxmoxVE/pull/18086))
- apache-tomcat: restore the update backup from its real store [@mazelado](https://github.com/mazelado) ([#18145](https://github.com/community-scripts/ProxmoxVE/pull/18145))
- AdventureLog: clean-install on update and drop the DRF <3.15 pin [@angusmaul](https://github.com/angusmaul) ([#18281](https://github.com/community-scripts/ProxmoxVE/pull/18281))
- #### 🔧 Refactor
- Refactor: ownCloud VM [@MickLesk](https://github.com/MickLesk) ([#18295](https://github.com/community-scripts/ProxmoxVE/pull/18295))
### 💾 Core
- Keep the console hint out of the default output [@MickLesk](https://github.com/MickLesk) ([core#176](https://github.com/community-scripts/core/pull/176))
### 📚 Documentation
- Move a promoted script's telemetry history to ProxmoxVE [@MickLesk](https://github.com/MickLesk) ([#18271](https://github.com/community-scripts/ProxmoxVE/pull/18271))
### ❔ Uncategorized
- chore(ct): sync tdarr defaults with PocketBase [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18289](https://github.com/community-scripts/ProxmoxVE/pull/18289))
## 2026-10-27
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Zabbix: leave out the NVIDIA agent2 plugin that breaks the all-plugins install [@MickLesk](https://github.com/MickLesk) ([#18262](https://github.com/community-scripts/ProxmoxVE/pull/18262))
- Omada: detect latest version from product download page [@ChrisKappe](https://github.com/ChrisKappe) ([#18248](https://github.com/community-scripts/ProxmoxVE/pull/18248))
- LanguageTool: count ngram prompt retries without tripping errexit [@MickLesk](https://github.com/MickLesk) ([#18258](https://github.com/community-scripts/ProxmoxVE/pull/18258))
- Overseerr: point fresh installs to Seerr [@MickLesk](https://github.com/MickLesk) ([#18259](https://github.com/community-scripts/ProxmoxVE/pull/18259))
- stalwart: pin to v0.16.27 until v1 migration is supported [@MickLesk](https://github.com/MickLesk) ([#18252](https://github.com/community-scripts/ProxmoxVE/pull/18252))
- #### ✨ New Features
- TurnKey: offer only current appliances, stop on arm64 [@MickLesk](https://github.com/MickLesk) ([#18265](https://github.com/community-scripts/ProxmoxVE/pull/18265))
- #### 🔧 Refactor
- NPMplus: let the ACME e-mail stay empty again, Let's Encrypt does not need one [@MickLesk](https://github.com/MickLesk) ([#18263](https://github.com/community-scripts/ProxmoxVE/pull/18263))
### 🗑️ Deleted Scripts
- Remove: Readarr [@MickLesk](https://github.com/MickLesk) ([#18264](https://github.com/community-scripts/ProxmoxVE/pull/18264))
### 💾 Core
- Say the same thing on both hosts while a container is created [@MickLesk](https://github.com/MickLesk) ([core#173](https://github.com/community-scripts/core/pull/173))
- Skip the IPv6 sysctl when the host kernel has no IPv6 [@MickLesk](https://github.com/MickLesk) ([core#171](https://github.com/community-scripts/core/pull/171))
- Stop reporting a closed console as a user abort, and shield the host's own work [@MickLesk](https://github.com/MickLesk) ([core#172](https://github.com/community-scripts/core/pull/172))
## 2026-10-26
### 🆕 New Scripts
- Vane ([#18168](https://github.com/community-scripts/ProxmoxVE/pull/18168))
- HomeTube ([#18172](https://github.com/community-scripts/ProxmoxVE/pull/18172))
- Honcho ([#18169](https://github.com/community-scripts/ProxmoxVE/pull/18169))
- YTPTube ([#18174](https://github.com/community-scripts/ProxmoxVE/pull/18174))
- Stump ([#18173](https://github.com/community-scripts/ProxmoxVE/pull/18173))
- Casdoor ([#18170](https://github.com/community-scripts/ProxmoxVE/pull/18170))
- Streamystats ([#18171](https://github.com/community-scripts/ProxmoxVE/pull/18171))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Immich: lock only this platform when mise installs the plugin tools [@MickLesk](https://github.com/MickLesk) ([#18241](https://github.com/community-scripts/ProxmoxVE/pull/18241))
- Fix service name case for Splunkd [@CerberusStyle](https://github.com/CerberusStyle) ([#18238](https://github.com/community-scripts/ProxmoxVE/pull/18238))
- VMs: pass the machine type, CPU, disk cache and discard settings to qm [@MickLesk](https://github.com/MickLesk) ([#18232](https://github.com/community-scripts/ProxmoxVE/pull/18232))
- excalidash: bump Node.js from 20 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18213](https://github.com/community-scripts/ProxmoxVE/pull/18213))
- readmeabook: bump Node.js from 20 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18217](https://github.com/community-scripts/ProxmoxVE/pull/18217))
- homepage: bump Node.js from 22 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18214](https://github.com/community-scripts/ProxmoxVE/pull/18214))
- remove old -install.sh (verdaccio) [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18218](https://github.com/community-scripts/ProxmoxVE/pull/18218))
- meshcentral: bump Node.js from 22 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18215](https://github.com/community-scripts/ProxmoxVE/pull/18215))
- #### ✨ New Features
- MeshCentral: update the npm package, not just the OS [@MickLesk](https://github.com/MickLesk) ([#18243](https://github.com/community-scripts/ProxmoxVE/pull/18243))
### 💾 Core
- feat: let an install survive a closed console / dont sighup installs [@MickLesk](https://github.com/MickLesk) ([core#167](https://github.com/community-scripts/core/pull/167))
- feat: create containers from the linuxcontainers.org catalog when Proxmox has no template [@MickLesk](https://github.com/MickLesk) ([core#25](https://github.com/community-scripts/core/pull/25))
### 📚 Documentation
- Ref: Docs / Readme [@MickLesk](https://github.com/MickLesk) ([#18233](https://github.com/community-scripts/ProxmoxVE/pull/18233))
## 2026-10-25
### 🆕 New Scripts
- Dreeve ([#18167](https://github.com/community-scripts/ProxmoxVE/pull/18167))
- TaskView ([#18166](https://github.com/community-scripts/ProxmoxVE/pull/18166))
- EmDash ([#18165](https://github.com/community-scripts/ProxmoxVE/pull/18165))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(mealie): install ffmpeg for video recipe import [@clartek](https://github.com/clartek) ([#18210](https://github.com/community-scripts/ProxmoxVE/pull/18210))
- Termix: step through 2.9.2 when updating from 2.8 or older [@MickLesk](https://github.com/MickLesk) ([#18158](https://github.com/community-scripts/ProxmoxVE/pull/18158))
- Element Synapse: remove the registration line by its text, not line 34 [@MickLesk](https://github.com/MickLesk) ([#18201](https://github.com/community-scripts/ProxmoxVE/pull/18201))
- Element Synapse: drop the ::1 listener so a container without IPv6 starts [@MickLesk](https://github.com/MickLesk) ([#18180](https://github.com/community-scripts/ProxmoxVE/pull/18180))
- Heimdall: generate the app key without the production prompt [@MickLesk](https://github.com/MickLesk) ([#18156](https://github.com/community-scripts/ProxmoxVE/pull/18156))
- Frigate: fetch the Intel graphics key with retries before building the media driver [@MickLesk](https://github.com/MickLesk) ([#18192](https://github.com/community-scripts/ProxmoxVE/pull/18192))
- Pterodactyl Panel: default the admin's first and last name when left empty [@MickLesk](https://github.com/MickLesk) ([#18177](https://github.com/community-scripts/ProxmoxVE/pull/18177))
- Jellyseerr: point fresh installs to Seerr instead of a missing install script [@MickLesk](https://github.com/MickLesk) ([#18183](https://github.com/community-scripts/ProxmoxVE/pull/18183))
- Pi-hole: download the installer before running it [@MickLesk](https://github.com/MickLesk) ([#18182](https://github.com/community-scripts/ProxmoxVE/pull/18182))
- OmniTools: give the build a git commit to read [@MickLesk](https://github.com/MickLesk) ([#18181](https://github.com/community-scripts/ProxmoxVE/pull/18181))
- iVentoy: stop early in an unprivileged container [@MickLesk](https://github.com/MickLesk) ([#18179](https://github.com/community-scripts/ProxmoxVE/pull/18179))
- Immich: stop early on Debian 12 instead of failing at apt [@MickLesk](https://github.com/MickLesk) ([#18178](https://github.com/community-scripts/ProxmoxVE/pull/18178))
- paperless-ngx: fix missing index folder [@Segelzwerg](https://github.com/Segelzwerg) ([#18142](https://github.com/community-scripts/ProxmoxVE/pull/18142))
- Anytype Server: keep the .env across updates and recreate a lost one [@MickLesk](https://github.com/MickLesk) ([#18155](https://github.com/community-scripts/ProxmoxVE/pull/18155))
- changedetection: build Browserless from its lockfile on update [@MickLesk](https://github.com/MickLesk) ([#18157](https://github.com/community-scripts/ProxmoxVE/pull/18157))
- Zitadel: identify the machine by hostname when it has no private IPv4 [@MickLesk](https://github.com/MickLesk) ([#18139](https://github.com/community-scripts/ProxmoxVE/pull/18139))
- fix(bar-assistant): publish starter media catalog so new bars provision [@och29](https://github.com/och29) ([#18130](https://github.com/community-scripts/ProxmoxVE/pull/18130))
- #### ✨ New Features
- Gramps-Web: run the Celery task queue with Redis [@MickLesk](https://github.com/MickLesk) ([#18141](https://github.com/community-scripts/ProxmoxVE/pull/18141))
- #### 💥 Breaking Changes
- Hermes Agent: install, update and run everything as the hermes user [@MickLesk](https://github.com/MickLesk) ([#18203](https://github.com/community-scripts/ProxmoxVE/pull/18203))
- tubearchivist: fix: prepare tubearchivist for v0.6.0 [@CrazyWolf13](https://github.com/CrazyWolf13) ([#17867](https://github.com/community-scripts/ProxmoxVE/pull/17867))
- #### 🔧 Refactor
- TREK: build 5.0 with 4 GB RAM and without dependency install scripts [@MickLesk](https://github.com/MickLesk) ([#18194](https://github.com/community-scripts/ProxmoxVE/pull/18194))
### 💾 Core
- alpine: fix uv release url [@mfelipe](https://github.com/mfelipe) ([core#168](https://github.com/community-scripts/core/pull/168))
- Set npm's script and git policy for root too, so --prefix keeps it [@MickLesk](https://github.com/MickLesk) ([core#170](https://github.com/community-scripts/core/pull/170))
- Upgrade Node.js in place when an app package depends on it [@MickLesk](https://github.com/MickLesk) ([core#169](https://github.com/community-scripts/core/pull/169))
### 🧰 Tools
- #### ✨ New Features
- post-pmg-install: hide the subscription nag in the PMG 9 mobile quarantine [@MickLesk](https://github.com/MickLesk) ([#18140](https://github.com/community-scripts/ProxmoxVE/pull/18140))
- post-pve-install: hide the mobile subscription nag in every UI language [@MickLesk](https://github.com/MickLesk) ([#18143](https://github.com/community-scripts/ProxmoxVE/pull/18143))
## 2026-10-24
### 🚀 Updated Scripts
+10 -10
View File
@@ -17,16 +17,6 @@ Fixes #
**Tested on:** <!-- e.g. PVE 9.2 / Debian 13 / fresh install + update. Write "not tested" if you could not run it. -->
---
## 🤖 AI Assistance (**X** in brackets)
> If you used an AI tool (GitHub Copilot, Claude, ChatGPT, etc.) to write or generate any scripts in this PR, you **must** confirm compliance below.
> Select exactly one option.
- [ ] **No AI used** – Scripts were written without AI assistance.
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/DevScripts/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/DevScripts/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
---
## 🛠️ Type of Change (**X** in brackets)
@@ -70,3 +60,13 @@ Guidance:
-->
<!-- The advisory block is only active once it is OUTSIDE this comment. -->
---
## 🤖 AI Assistance (**X** in brackets)
> If you used an AI tool (GitHub Copilot, Claude, ChatGPT, etc.) to write or generate any scripts in this PR, you **must** confirm compliance below.
> Select exactly one option.
- [ ] **No AI used** – Scripts were written without AI assistance.
- [ ] **AI was used** – I confirm the scripts were built using [`AGENTS.md`](https://github.com/community-scripts/DevScripts/blob/main/AGENTS.md) and [`.github/agents/pve-script-creator.agent.md`](https://github.com/community-scripts/DevScripts/blob/main/.github/agents/pve-script-creator.agent.md) as guidance, and the output has been reviewed and corrected to match those guidelines.
+44 -40
View File
@@ -1,4 +1,4 @@
name: Close PRs Missing Template
name: Flag PRs Missing Template
on:
pull_request_target:
@@ -14,10 +14,12 @@ jobs:
issues: write
contents: read
steps:
- name: Close PR if it does not follow the PR template
- name: Check PR against the template, comment and label instead of closing
uses: actions/github-script@v9
with:
script: |
const MARKER = "<!-- pr-template-check -->";
const LABELS = ["missing pr template", "stale"];
const pr = context.payload.pull_request;
const prNumber = pr.number;
const author = pr.user.login;
@@ -44,11 +46,11 @@ jobs:
return;
}
const labels = pr.labels.map((label) => label.name);
const prLabels = pr.labels.map((label) => label.name);
const skipLabels = ["automated pr", "keep-open"];
if (skipLabels.some((label) => labels.includes(label))) {
core.info(`PR #${prNumber} has a skip label (${labels.join(", ")}) — skipping template validation.`);
if (skipLabels.some((label) => prLabels.includes(label))) {
core.info(`PR #${prNumber} has a skip label (${prLabels.join(", ")}) — skipping template validation.`);
return;
}
@@ -114,50 +116,52 @@ jobs:
failures.push("At least one **Type of Change** checkbox must be checked.");
}
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: prNumber, per_page: 100
});
const mine = comments.find(c => c.body && c.body.includes(MARKER));
if (failures.length === 0) {
core.info(`PR #${prNumber} follows the PR template.`);
if (mine && !mine.body.startsWith(MARKER + "\n✅")) {
await github.rest.issues.updateComment({
owner, repo, comment_id: mine.id,
body: MARKER + "\n✅ **Template check passed.** Thanks for fixing the description — the `stale` mark has been removed.",
});
}
for (const name of LABELS) {
if (prLabels.includes(name)) {
await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name });
}
}
return;
}
core.info(`Closing PR #${prNumber} — template validation failed.`);
core.info(`PR #${prNumber} — template validation failed, flagging instead of closing.`);
const templateUrl =
"https://github.com/community-scripts/ProxmoxVE/blob/main/.github/pull_request_template.md";
const failureList = failures.map((item) => `- ${item}`).join("\n");
const comment = [
`👋 Hi @${author},`,
``,
`This pull request was closed because it does not follow the [PR template](${templateUrl}).`,
``,
`Please fix the following and open a new PR (or reopen this one after updating the description):`,
``,
failureList,
``,
`> Use the template sections, fill in the description, check all prerequisite boxes, and select at least one type of change.`,
``,
`Maintainers can add the \`keep-open\` label to exempt a PR from this check.`,
``,
`Thank you for contributing! 🙏`,
].join("\n");
const text =
MARKER + "\n" +
`👋 Hi @${author},\n\n` +
`This pull request does not follow the [PR template](${templateUrl}) yet:\n\n` +
failureList + "\n\n" +
`**Edit the PR description** to fix it — no new PR needed. This check runs again on every edit and clears itself once everything is in place.\n\n` +
`Until then the PR is marked \`stale\` and will be closed in **7 days**.\n\n` +
`Maintainers can add the \`keep-open\` label to exempt a PR from this check.\n\n` +
`Thank you for contributing! 🙏`;
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: comment,
});
if (mine) {
if (mine.body !== text) {
await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body: text });
}
} else {
await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: text });
}
await github.rest.pulls.update({
owner,
repo,
pull_number: prNumber,
state: "closed",
});
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: ["missing pr template"],
});
const toAdd = LABELS.filter(name => !prLabels.includes(name));
if (toAdd.length > 0) {
await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: toAdd });
}
+2 -3
View File
@@ -267,15 +267,14 @@ jobs:
const stubPath = path.join('ct', slug + '.sh');
const content = [
"#!/usr/bin/env bash",
"source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)",
"source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/core/main/core/core.func)",
"# Copyright (c) 2021-2026 community-scripts ORG",
"# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE",
"",
`APP="${appName.replace(/"/g, '\\"')}"`,
"",
'header_info "$APP"',
'variables',
'color',
'header_info',
"",
'msg_error "This script is no longer available in community-scripts."',
`msg_error "${deletedMessage.replace(/"/g, '\\"')}"`,
+2 -2
View File
@@ -184,8 +184,8 @@ jobs:
);
}
lines.push(
'Only the script itself comes from this branch. Whatever it loads, the engine',
'or the `misc/` helpers, still comes from `main`.',
'Only the script itself comes from this branch. The engine it loads still',
'comes from community-scripts/core on `main`.',
);
}
+141
View File
@@ -0,0 +1,141 @@
name: Request Script Eligibility Check
# category.slug is the same string GitHub uses in the "new discussion" URL
# (?category=request-script, linked from every repo's issue chooser), so this
# guard matches the live category without needing its numeric id.
on:
discussion:
types: [created, edited, reopened]
jobs:
check-eligibility:
if: github.repository == 'community-scripts/ProxmoxVE' && github.event.discussion.category.slug == 'request-script'
runs-on: ubuntu-latest
permissions:
discussions: write
issues: write
contents: read
steps:
- name: Verify stars/age, flag or close
uses: actions/github-script@v9
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const discussion = context.payload.discussion;
if (!discussion || discussion.state === "closed") {
core.info("Discussion missing or already closed — skipping.");
return;
}
const body = discussion.body || "";
const lines = body.split("\n");
const fields = {};
let current = null;
for (const line of lines) {
const m = line.match(/^###\s+(.+?)\s*$/);
if (m) {
current = m[1].trim();
fields[current] = [];
} else if (current) {
fields[current].push(line);
}
}
function fieldText(label) {
const raw = (fields[label] || []).join("\n").trim();
return raw === "_No response_" ? "" : raw;
}
const sourceRepo = fieldText("Source Repository");
const justification = fieldText("Justification for an exception");
let eligible = false;
let verified = false;
const ghMatch = sourceRepo.match(/^https?:\/\/(?:www\.)?github\.com\/([^\/\s]+)\/([^\/\s#?]+?)(?:\.git)?\/?(?:[#?].*)?$/i);
if (ghMatch) {
const [, repoOwner, repoName] = ghMatch;
try {
const { data: repoData } = await github.rest.repos.get({ owner: repoOwner, repo: repoName });
const stars = repoData.stargazers_count;
const createdAt = new Date(repoData.created_at);
const now = new Date();
const ageMonths =
(now.getFullYear() - createdAt.getFullYear()) * 12 +
(now.getMonth() - createdAt.getMonth()) -
(now.getDate() < createdAt.getDate() ? 1 : 0);
eligible = stars >= 1000 && ageMonths >= 6;
verified = true;
core.info(`Verified ${repoOwner}/${repoName}: ${stars} stars, ${ageMonths} months old, eligible=${eligible}`);
} catch (err) {
core.warning(`Could not verify ${repoOwner}/${repoName}: ${err.message} — treating as unverifiable.`);
}
} else {
core.info("Source Repository is not a github.com URL — cannot auto-verify, treating as unverifiable.");
}
if (eligible) {
core.info(`Discussion #${discussion.number} meets the requirements — no action.`);
return;
}
const labels = (discussion.labels || []).map(l => l.name);
const reasonLine = verified
? "the source repository does not meet the 1,000-star / 6-month minimum adoption requirement"
: "the source repository's star count and age could not be verified automatically";
if (justification) {
if (labels.includes("investigation")) {
core.info(`Discussion #${discussion.number} already flagged for investigation — skipping.`);
return;
}
try {
await github.rest.issues.createLabel({
owner, repo, name: "investigation",
color: "fbca04",
description: "Below the normal script-request threshold; a justification was given and needs maintainer review.",
});
} catch (err) {
if (err.status !== 422) throw err;
}
const labelQuery = await github.graphql(
`query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { label(name: "investigation") { id } } }`,
{ owner, repo }
);
const labelNodeId = labelQuery.repository.label.id;
await github.graphql(
`mutation($labelableId: ID!, $labelIds: [ID!]!) { addLabelsToLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) { clientMutationId } }`,
{ labelableId: discussion.node_id, labelIds: [labelNodeId] }
);
await github.graphql(
`mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { comment { id } } }`,
{
discussionId: discussion.node_id,
body:
`Thanks for the justification — ${reasonLine}, so this has been flagged \`investigation\` for a maintainer ` +
`to review manually rather than closed automatically.`,
}
);
} else {
await github.graphql(
`mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { comment { id } } }`,
{
discussionId: discussion.node_id,
body:
`This request doesn't meet the minimum requirements (1,000 stars, project at least 6 months old) — ` +
`${reasonLine}, and no justification was given. Closing automatically.\n\n` +
`If you believe this project qualifies for an exception, edit this discussion to fill in ` +
`**Justification for an exception** and reopen it — it will then be flagged for manual review instead of closed again.`,
}
);
await github.graphql(
`mutation($discussionId: ID!) { closeDiscussion(input: { discussionId: $discussionId, reason: OUTDATED }) { discussion { id } } }`,
{ discussionId: discussion.node_id }
);
}
+95 -73
View File
@@ -22,90 +22,112 @@ jobs:
const now = new Date();
const owner = context.repo.owner;
const repo = context.repo.repo;
// --- When stale label is added, comment immediately ---
const TEMPLATE_LABEL = "missing pr template";
const MAINTAINERS = ["OWNER", "MEMBER", "COLLABORATOR"];
const days = d => (now - d) / (1000 * 60 * 60 * 24);
const staleNotice = author =>
`@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`;
// Only fires for a maintainer's label: labels set with GITHUB_TOKEN raise no event.
if (context.eventName === "pull_request_target" && context.payload.action === "labeled") {
const label = context.payload.label?.name;
if (label === "stale") {
const author = context.payload.pull_request.user.login;
const pr = context.payload.pull_request;
const labels = (pr.labels || []).map(l => l.name);
if (context.payload.label?.name === "stale" && !labels.includes(TEMPLATE_LABEL)) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: context.payload.pull_request.number,
body: `@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`
owner, repo, issue_number: pr.number, body: staleNotice(pr.user.login)
});
}
return;
}
// --- Scheduled run: check all stale PRs ---
const { data: prs } = await github.rest.pulls.list({
owner,
repo,
state: "open",
per_page: 100
});
const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: "open", per_page: 100 });
for (const pr of prs) {
const hasStale = pr.labels.some(l => l.name === "stale");
if (!hasStale) continue;
// Get timeline events to find when stale label was added
const { data: events } = await github.rest.issues.listEvents({
owner,
repo,
issue_number: pr.number,
per_page: 100
});
// Find the most recent time the stale label was added
const staleLabelEvents = events
.filter(e => e.event === "labeled" && e.label?.name === "stale")
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
if (staleLabelEvents.length === 0) continue;
const staleLabelDate = new Date(staleLabelEvents[0].created_at);
const daysSinceStale = (now - staleLabelDate) / (1000 * 60 * 60 * 24);
// Check for new commits since stale label was added
const { data: commits } = await github.rest.pulls.listCommits({
owner,
repo,
pull_number: pr.number
});
const lastCommitDate = new Date(commits[commits.length - 1].commit.author.date);
const labels = pr.labels.map(l => l.name);
const author = pr.user.login;
// If there are new commits after the stale label, remove it
if (lastCommitDate > staleLabelDate) {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pr.number,
name: "stale"
const hasStale = labels.includes("stale");
if (labels.includes("keep-open")) {
if (hasStale) {
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
}
continue;
}
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: pr.number, per_page: 100
});
const commits = await github.paginate(github.rest.pulls.listCommits, {
owner, repo, pull_number: pr.number, per_page: 100
});
const lastCommitDate = commits.length > 0
? new Date(commits[commits.length - 1].commit.author.date)
: new Date(pr.created_at);
const humanComments = comments.filter(c => c.user?.type !== "Bot");
// -------------------------------------------------------
// Auto-label PRs with no activity in the last 14 days,
// but ONLY if someone other than the author has engaged.
// -------------------------------------------------------
if (!hasStale) {
const reviews = await github.paginate(github.rest.pulls.listReviews, {
owner, repo, pull_number: pr.number, per_page: 100
});
const engaged =
reviews.some(r => r.user?.type !== "Bot" && r.user?.login !== author) ||
humanComments.some(c => c.user?.login !== author);
if (!engaged) continue;
const lastCommentDate = humanComments.length > 0
? new Date(humanComments[humanComments.length - 1].created_at)
: null;
const lastActivityDate = lastCommentDate && lastCommentDate > lastCommitDate
? lastCommentDate
: lastCommitDate;
if (days(lastActivityDate) > 14) {
await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: ["stale"] });
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: staleNotice(author) });
}
continue;
}
// -------------------------------------------------------
// Manage already-stale PRs
// -------------------------------------------------------
const events = await github.paginate(github.rest.issues.listEvents, {
owner, repo, issue_number: pr.number, per_page: 100
});
const staleDates = events
.filter(e => e.event === "labeled" && e.label?.name === "stale")
.map(e => new Date(e.created_at));
if (staleDates.length === 0) continue;
const staleLabelDate = new Date(Math.max(...staleDates));
const isTemplateIssue = labels.includes(TEMPLATE_LABEL);
// A push ends an inactivity mark; a template mark only ends via close-invalid-pr-template.yml's re-check.
if (!isTemplateIssue && lastCommitDate > staleLabelDate) {
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
await github.rest.issues.createComment({
owner,
repo,
issue_number: pr.number,
owner, repo, issue_number: pr.number,
body: `@${author} Recent activity detected. Removing stale label.`
});
continue;
}
// If 7 days have passed since stale label, close the PR
else if (daysSinceStale > 7) {
await github.rest.pulls.update({
owner,
repo,
pull_number: pr.number,
state: "closed"
});
await github.rest.issues.createComment({
owner,
repo,
issue_number: pr.number,
body: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
});
}
// A maintainer comment after the mark restarts the 7 days.
const maintainerDates = humanComments
.filter(c => MAINTAINERS.includes(c.author_association) && c.user?.login !== author)
.map(c => new Date(c.created_at))
.filter(d => d > staleLabelDate);
const clockStart = maintainerDates.length > 0 ? new Date(Math.max(...maintainerDates)) : staleLabelDate;
if (days(clockStart) <= 7) continue;
await github.rest.issues.createComment({
owner, repo, issue_number: pr.number,
body: isTemplateIssue
? `@${author} Closing: the PR template requirements listed above were still missing 7 days later. Once they are in place, please open a new pull request.`
: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
});
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: "closed" });
}
+2 -2
View File
@@ -5,8 +5,8 @@ name: Sync ct/install to Incus
#
# The actual mirroring + transformation lives in community-scripts/Incus
# (.github/workflows/sync-scripts.yml): it rewrites each script's bootstrap line
# to load the engine from community-scripts/core (ProxmoxVE's misc/build.func is
# a monolith with no Incus backend), so a plain 1:1 copy would break every Incus
# to load the engine from community-scripts/core (ct scripts here load its PVE
# backend; Incus needs the Incus one), so a plain 1:1 copy would break every Incus
# script. We therefore only fire a repository_dispatch and let that single
# source of truth do the work (and auto-approve + merge its own PR).
#
+147 -166
View File
@@ -1,6 +1,6 @@
<div align="center">
<a href="#">
<img src="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo.png" height="100px" />
<img src="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/.github/assets/logo.png" height="100px" />
</a>
</div>
<h1 align="center">Changelog</h1>
@@ -122,6 +122,9 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
@@ -135,7 +138,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
<details>
<summary><h4>October (24 entries)</h4></summary>
<summary><h4>October (31 entries)</h4></summary>
[View October 2026 Changelog](.github/changelogs/2026/10.md)
@@ -568,6 +571,147 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-11-04
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- n8n: install past its unsatisfiable canvas dependencies [@MickLesk](https://github.com/MickLesk) ([#18439](https://github.com/community-scripts/ProxmoxVE/pull/18439))
- Teable: add Redis cache [@MickLesk](https://github.com/MickLesk) ([#18450](https://github.com/community-scripts/ProxmoxVE/pull/18450))
### 💾 Core
- feat: read the MOTD's OS version at login [@MickLesk](https://github.com/MickLesk) ([core#207](https://github.com/community-scripts/core/pull/207))
- feat: skip screen clear when there is no terminal | pin only the scripts own app with var_appversion [@MickLesk](https://github.com/MickLesk) ([core#203](https://github.com/community-scripts/core/pull/203))
## 2026-11-03
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Fedora VM: let the engine install libguestfs-tools [@MickLesk](https://github.com/MickLesk) ([#18422](https://github.com/community-scripts/ProxmoxVE/pull/18422))
- Cinephage: run the camoufox binary the package now ships [@MickLesk](https://github.com/MickLesk) ([#18426](https://github.com/community-scripts/ProxmoxVE/pull/18426))
- AlmaLinux VM: report failures through the engine's error handler [@MickLesk](https://github.com/MickLesk) ([#18418](https://github.com/community-scripts/ProxmoxVE/pull/18418))
- WireGuard: give the Debian container 1 GB for WGDashboard [@MickLesk](https://github.com/MickLesk) ([#18419](https://github.com/community-scripts/ProxmoxVE/pull/18419))
- zabbix: use os specific apt packages [@MickLesk](https://github.com/MickLesk) ([#18417](https://github.com/community-scripts/ProxmoxVE/pull/18417))
- Discourse: build libvips 8.18 for its image worker [@MickLesk](https://github.com/MickLesk) ([#18430](https://github.com/community-scripts/ProxmoxVE/pull/18430))
- Wazuh: show the installer's own error when it fails [@MickLesk](https://github.com/MickLesk) ([#18428](https://github.com/community-scripts/ProxmoxVE/pull/18428))
- Tracearr: move to Node.js 26 [@connorgallopo](https://github.com/connorgallopo) ([#18393](https://github.com/community-scripts/ProxmoxVE/pull/18393))
- #### ✨ New Features
- Wiki.js: install 3.x [@MickLesk](https://github.com/MickLesk) ([#18382](https://github.com/community-scripts/ProxmoxVE/pull/18382))
- #### 🔧 Refactor
- OpenWrt VM: read the stable release from downloads.openwrt.org [@MickLesk](https://github.com/MickLesk) ([#18411](https://github.com/community-scripts/ProxmoxVE/pull/18411))
- Immich: fetch VectorChord from its new repo, skip Java, get GeoNames first [@MickLesk](https://github.com/MickLesk) ([#18397](https://github.com/community-scripts/ProxmoxVE/pull/18397))
### 💾 Core
- fix: put the reason for a VM failure into its trace [@MickLesk](https://github.com/MickLesk) ([core#200](https://github.com/community-scripts/core/pull/200))
- fix: check the bridge before creating a container, and its state after start [@MickLesk](https://github.com/MickLesk) ([core#199](https://github.com/community-scripts/core/pull/199))
- ref: offer to remove only the container this run created [@MickLesk](https://github.com/MickLesk) ([core#198](https://github.com/community-scripts/core/pull/198))
- feat: report a cancelled tool dialog as an abort and name the failing command [@MickLesk](https://github.com/MickLesk) ([core#197](https://github.com/community-scripts/core/pull/197))
- feat: check KVM and the host's threads before building a VM [@MickLesk](https://github.com/MickLesk) ([core#196](https://github.com/community-scripts/core/pull/196))
- ref: give a host tool one log file for the whole run [@MickLesk](https://github.com/MickLesk) ([core#194](https://github.com/community-scripts/core/pull/194))
- fix: keep a piped stdin when output moves to the log after a hangup [@MickLesk](https://github.com/MickLesk) ([core#193](https://github.com/community-scripts/core/pull/193))
- fix: pve-LXC: say when the container went away during its setup [@MickLesk](https://github.com/MickLesk) ([core#192](https://github.com/community-scripts/core/pull/192))
- fix: treat Esc in a VM wizard dialog as leaving the script [@MickLesk](https://github.com/MickLesk) ([core#190](https://github.com/community-scripts/core/pull/190))
- ref: count a container stopped during a detached install as an abort [@MickLesk](https://github.com/MickLesk) ([core#187](https://github.com/community-scripts/core/pull/187))
- feat: find the assets of a renamed GitHub repo without the API [@MickLesk](https://github.com/MickLesk) ([core#188](https://github.com/community-scripts/core/pull/188))
- fix: restore npm when an interrupted update left it behind [@MickLesk](https://github.com/MickLesk) ([core#189](https://github.com/community-scripts/core/pull/189))
- feat: add var_cmode to set the container console mode [@MickLesk](https://github.com/MickLesk) ([core#180](https://github.com/community-scripts/core/pull/180))
### 🧰 Tools
- #### 🐞 Bug Fixes
- all-templates: answering No or Cancel is not a failure [@MickLesk](https://github.com/MickLesk) ([#18423](https://github.com/community-scripts/ProxmoxVE/pull/18423))
- homebrew: skip service accounts when picking the user, survive a failed apt update [@MickLesk](https://github.com/MickLesk) ([#18424](https://github.com/community-scripts/ProxmoxVE/pull/18424))
- add-tailscale-lxc: start a stopped container and check its OS first [@MickLesk](https://github.com/MickLesk) ([#18421](https://github.com/community-scripts/ProxmoxVE/pull/18421))
- netdata: survive a failed apt update, leave quietly on Cancel [@MickLesk](https://github.com/MickLesk) ([#18427](https://github.com/community-scripts/ProxmoxVE/pull/18427))
- Privilege-Converter: offer only container-capable storages as the converter's restore target [@MickLesk](https://github.com/MickLesk) ([#18415](https://github.com/community-scripts/ProxmoxVE/pull/18415))
- let post-pve-install go on when legacy sources are kept [@MickLesk](https://github.com/MickLesk) ([#18403](https://github.com/community-scripts/ProxmoxVE/pull/18403))
- #### 🔧 Refactor
- update-apps: say why an backup or restore failed [@MickLesk](https://github.com/MickLesk) ([#18405](https://github.com/community-scripts/ProxmoxVE/pull/18405))
- Keep host tools running when clear does not know the terminal [@MickLesk](https://github.com/MickLesk) ([#18412](https://github.com/community-scripts/ProxmoxVE/pull/18412))
- Refuse to run the PBS 4 upgrade on a Proxmox VE host [@MickLesk](https://github.com/MickLesk) ([#18416](https://github.com/community-scripts/ProxmoxVE/pull/18416))
- fstrim: keep trimming when pct fstrim fails on one container [@MickLesk](https://github.com/MickLesk) ([#18413](https://github.com/community-scripts/ProxmoxVE/pull/18413))
- execute-lxc: do not abort on a container that is not Debian or Ubuntu [@MickLesk](https://github.com/MickLesk) ([#18414](https://github.com/community-scripts/ProxmoxVE/pull/18414))
### 📚 Documentation
- github: flag instead of closing on first PR template miss; add script-request eligibility check [@MickLesk](https://github.com/MickLesk) ([#18384](https://github.com/community-scripts/ProxmoxVE/pull/18384))
## 2026-11-02
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- FileFlows: replace the program folders on update [@MickLesk](https://github.com/MickLesk) ([#18380](https://github.com/community-scripts/ProxmoxVE/pull/18380))
- anchor: bump Node.js from 24 to 26 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18373](https://github.com/community-scripts/ProxmoxVE/pull/18373))
- guardian: bump Node.js from 24 to 26 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18375](https://github.com/community-scripts/ProxmoxVE/pull/18375))
- nametag: bump Node.js from 20 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#18376](https://github.com/community-scripts/ProxmoxVE/pull/18376))
### 💾 Core
- github: trim theater, add signal to the engine bug report; move AI block to end of PR template [@MickLesk](https://github.com/MickLesk) ([core#186](https://github.com/community-scripts/core/pull/186))
- fix: do not wait forever for verbose output a daemon keeps open [@MickLesk](https://github.com/MickLesk) ([core#185](https://github.com/community-scripts/core/pull/185))
## 2026-11-01
### 🆕 New Scripts
- Amazon Linux ([#18369](https://github.com/community-scripts/ProxmoxVE/pull/18369))
- CachyOS VM ([#18368](https://github.com/community-scripts/ProxmoxVE/pull/18368))
- Razzia ([#18366](https://github.com/community-scripts/ProxmoxVE/pull/18366))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(zerobyte): build the bundled local agent [@nicotsx](https://github.com/nicotsx) ([#18363](https://github.com/community-scripts/ProxmoxVE/pull/18363))
## 2026-10-31
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Docker VM: take the imported disk from vm_import_disk [@MickLesk](https://github.com/MickLesk) ([#18341](https://github.com/community-scripts/ProxmoxVE/pull/18341))
- Twenty: move to Node.js 26 [@MickLesk](https://github.com/MickLesk) ([#18337](https://github.com/community-scripts/ProxmoxVE/pull/18337))
- Norish: fetch Obscura from the repo Norish pins [@MickLesk](https://github.com/MickLesk) ([#18338](https://github.com/community-scripts/ProxmoxVE/pull/18338))
- HAOS VM: take the imported disk from vm_import_disk [@MickLesk](https://github.com/MickLesk) ([#18342](https://github.com/community-scripts/ProxmoxVE/pull/18342))
- Aurral: build the pnpm workspace and run backend/server.ts [@MickLesk](https://github.com/MickLesk) ([#18340](https://github.com/community-scripts/ProxmoxVE/pull/18340))
- PS5-MQTT: build the yarn workspace and run the packaged server [@MickLesk](https://github.com/MickLesk) ([#18339](https://github.com/community-scripts/ProxmoxVE/pull/18339))
- Jellyseerr, Overseerr, Paperless-ngx, Vikunja: skip migration prompts when unattended [@florianpunz](https://github.com/florianpunz) ([#18321](https://github.com/community-scripts/ProxmoxVE/pull/18321))
- Discourse: track the Ruby version Discourse requires [@MickLesk](https://github.com/MickLesk) ([#18329](https://github.com/community-scripts/ProxmoxVE/pull/18329))
- #### ✨ New Features
- MatterJS-Server: Add time sync [@tomfrenzel](https://github.com/tomfrenzel) ([#18330](https://github.com/community-scripts/ProxmoxVE/pull/18330))
- #### 🔧 Refactor
- Debian VM: keep a static IP without a snippets storage [@MickLesk](https://github.com/MickLesk) ([#18346](https://github.com/community-scripts/ProxmoxVE/pull/18346))
- Ubuntu VM: check for a snippets storage before building a static-IP VM [@MickLesk](https://github.com/MickLesk) ([#18347](https://github.com/community-scripts/ProxmoxVE/pull/18347))
### 💾 Core
- feat: add setup_java_memory: size Java services from the CT's RAM on Proxmox [@MickLesk](https://github.com/MickLesk) ([core#181](https://github.com/community-scripts/core/pull/181))
- analytics: name the Proxmox tasks when a container stops mid-install [@MickLesk](https://github.com/MickLesk) ([core#184](https://github.com/community-scripts/core/pull/184))
- PVE: do not abort when the spinner PID file cannot be written [@MickLesk](https://github.com/MickLesk) ([core#183](https://github.com/community-scripts/core/pull/183))
### 📂 Github
- Drop the legacy engine copies from misc/ [@MickLesk](https://github.com/MickLesk) ([#18294](https://github.com/community-scripts/ProxmoxVE/pull/18294))
## 2026-10-30
### 🆕 New Scripts
@@ -1302,167 +1446,4 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 💾 Core
- QoL: message blocks, clean-install keep list, password helper, network limits [@MickLesk](https://github.com/MickLesk) ([core#61](https://github.com/community-scripts/core/pull/61))
## 2026-10-01
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- romm: keep the asset links in sync with ROMM_BASE_PATH [@MickLesk](https://github.com/MickLesk) ([#17610](https://github.com/community-scripts/ProxmoxVE/pull/17610))
- teslamate: build from the elixir directory since 4.3.0 [@MickLesk](https://github.com/MickLesk) ([#17612](https://github.com/community-scripts/ProxmoxVE/pull/17612))
- #### ✨ New Features
- feat(glance): add alpine os [@skilletfun](https://github.com/skilletfun) ([#17605](https://github.com/community-scripts/ProxmoxVE/pull/17605))
- feat(blocky): add alpine os [@skilletfun](https://github.com/skilletfun) ([#17637](https://github.com/community-scripts/ProxmoxVE/pull/17637))
- #### 🔧 Refactor
- several scripts: apt instead of apt-get, cleanup_lxc, nginx_enable_site, drop needless daemon-reloads [@MickLesk](https://github.com/MickLesk) ([#17624](https://github.com/community-scripts/ProxmoxVE/pull/17624))
- several scripts: use setup_postgresql_db / setup_mariadb_db instead of hand-written SQL [@MickLesk](https://github.com/MickLesk) ([#17622](https://github.com/community-scripts/ProxmoxVE/pull/17622))
### 💾 Core
- Offer to raise the kernel key limits instead of only naming them [@MickLesk](https://github.com/MickLesk) ([core#81](https://github.com/community-scripts/core/pull/81))
### 🧰 Tools
- #### 🔧 Refactor
- tools, turnkey: load core instead of the legacy misc functions [@MickLesk](https://github.com/MickLesk) ([#17621](https://github.com/community-scripts/ProxmoxVE/pull/17621))
### 📚 Documentation
- chore: drop the local header generation; headers come from core [@MickLesk](https://github.com/MickLesk) ([#17623](https://github.com/community-scripts/ProxmoxVE/pull/17623))
## 2026-09-30
### 🆕 New Scripts
- Shoko ([#17617](https://github.com/community-scripts/ProxmoxVE/pull/17617))
- SIlo ([#17614](https://github.com/community-scripts/ProxmoxVE/pull/17614))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(blocky): restart on failure and wait for network-online [@haplo](https://github.com/haplo) ([#17601](https://github.com/community-scripts/ProxmoxVE/pull/17601))
- SparkyFitness: update sed replacement [@tomfrenzel](https://github.com/tomfrenzel) ([#17604](https://github.com/community-scripts/ProxmoxVE/pull/17604))
### 💾 Core
- vm: autologin on the serial console for images without cloud-init [@MickLesk](https://github.com/MickLesk) ([core#80](https://github.com/community-scripts/core/pull/80))
- download_gpg_key: make dearmored keyrings world-readable [@MickLesk](https://github.com/MickLesk) ([core#79](https://github.com/community-scripts/core/pull/79))
## 2026-09-29
### 🆕 New Scripts
- Anki Sync Server ([#17416](https://github.com/community-scripts/ProxmoxVE/pull/17416))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(kaneo): build with Vite+ instead of Turbo [@tinsever](https://github.com/tinsever) ([#17599](https://github.com/community-scripts/ProxmoxVE/pull/17599))
- tianji: raise the Node heap for the static build on install [@MickLesk](https://github.com/MickLesk) ([#17582](https://github.com/community-scripts/ProxmoxVE/pull/17582))
- fix(romm): snapshot Redis hourly like the upstream image [@DenislavDenev](https://github.com/DenislavDenev) ([#17562](https://github.com/community-scripts/ProxmoxVE/pull/17562))
- checkmate: bump Node.js from 22 to 24 [@github-actions[bot]](https://github.com/github-actions[bot]) ([#17554](https://github.com/community-scripts/ProxmoxVE/pull/17554))
- #### 🔧 Refactor
- Drop the scripts base pin from every ct/ script [@MickLesk](https://github.com/MickLesk) ([#17585](https://github.com/community-scripts/ProxmoxVE/pull/17585))
- odoo: move to Debian 13 and stay on the installed major on update [@MickLesk](https://github.com/MickLesk) ([#17581](https://github.com/community-scripts/ProxmoxVE/pull/17581))
- manyfold: use upstream f3d for headless renders on amd64 [@MickLesk](https://github.com/MickLesk) ([#17583](https://github.com/community-scripts/ProxmoxVE/pull/17583))
### 💾 Core
- Default the scripts base to ProxmoxVE [@MickLesk](https://github.com/MickLesk) ([core#76](https://github.com/community-scripts/core/pull/76))
### 🧰 Tools
- #### 🐞 Bug Fixes
- all-templates: handle the arch column of pveam available [@MickLesk](https://github.com/MickLesk) ([#17579](https://github.com/community-scripts/ProxmoxVE/pull/17579))
### 📂 Github
- Skip automated core pull requests in the changelog [@MickLesk](https://github.com/MickLesk) ([#17589](https://github.com/community-scripts/ProxmoxVE/pull/17589))
- Post a test command for vm/, tools/ and turnkey/ changes too [@MickLesk](https://github.com/MickLesk) ([#17587](https://github.com/community-scripts/ProxmoxVE/pull/17587))
- Remove the PocketBase AI bot [@MickLesk](https://github.com/MickLesk) ([#17591](https://github.com/community-scripts/ProxmoxVE/pull/17591))
## 2026-09-28
### 🚀 Updated Scripts
- Immich: Pin version to 3.2.4 [@vhsdream](https://github.com/vhsdream) ([#17564](https://github.com/community-scripts/ProxmoxVE/pull/17564))
- #### 🐞 Bug Fixes
- fix(autocaliweb): wire .env into all service units, not just autocali… [@bobartlett](https://github.com/bobartlett) ([#17561](https://github.com/community-scripts/ProxmoxVE/pull/17561))
- Zipline: Change cp command [@Mraedis](https://github.com/Mraedis) ([#17559](https://github.com/community-scripts/ProxmoxVE/pull/17559))
- Borg-UI: start through upstream's start.sh so migrations run [@MickLesk](https://github.com/MickLesk) ([#17563](https://github.com/community-scripts/ProxmoxVE/pull/17563))
- linkding: serve favicons and preview images [@MickLesk](https://github.com/MickLesk) ([#17557](https://github.com/community-scripts/ProxmoxVE/pull/17557))
- SparkyFitness: Fill in the nginx resolver 1.7.3 added [@MickLesk](https://github.com/MickLesk) ([#17556](https://github.com/community-scripts/ProxmoxVE/pull/17556))
- #### 🔧 Refactor
- Plane: Replace MinIO installation with Silo [@MickLesk](https://github.com/MickLesk) ([#17347](https://github.com/community-scripts/ProxmoxVE/pull/17347))
## 2026-09-27
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- fix(aurral): bump to Node 26 and bypass strict engine pin [@JodyVanden](https://github.com/JodyVanden) ([#17543](https://github.com/community-scripts/ProxmoxVE/pull/17543))
- Tracearr: fetch map tiles on install/update [@durzo](https://github.com/durzo) ([#17544](https://github.com/community-scripts/ProxmoxVE/pull/17544))
- fix(autocaliweb): define INSTALL_DIR before first use [@bobartlett](https://github.com/bobartlett) ([#17545](https://github.com/community-scripts/ProxmoxVE/pull/17545))
## 2026-09-26
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Manyfold: make /opt/manyfold_data writable for the manyfold user [@sergstepanenko](https://github.com/sergstepanenko) ([#17534](https://github.com/community-scripts/ProxmoxVE/pull/17534))
- steamcmd: update itself before installing Satisfactory [@MickLesk](https://github.com/MickLesk) ([#17526](https://github.com/community-scripts/ProxmoxVE/pull/17526))
- AudioMuse-AI: build noavx2 environment on Python 3.11 [@MickLesk](https://github.com/MickLesk) ([#17528](https://github.com/community-scripts/ProxmoxVE/pull/17528))
- LobeHub: pin lobehub/ui at 5.49 while LobeHub's stable release needs it [@MickLesk](https://github.com/MickLesk) ([#17531](https://github.com/community-scripts/ProxmoxVE/pull/17531))
- Keep Trilium updates on the server releases [@MickLesk](https://github.com/MickLesk) ([#17525](https://github.com/community-scripts/ProxmoxVE/pull/17525))
- NginxProxyManager: generate admin config and repair certbot's venv on update [@MickLesk](https://github.com/MickLesk) ([#17523](https://github.com/community-scripts/ProxmoxVE/pull/17523))
- Komodo: add KOMODO_HOST | fix broken spinner [@MickLesk](https://github.com/MickLesk) ([#17481](https://github.com/community-scripts/ProxmoxVE/pull/17481))
- romm: run rq cron and a scans worker for RomM 5.3 [@davidbb](https://github.com/davidbb) ([#17502](https://github.com/community-scripts/ProxmoxVE/pull/17502))
- #### 💥 Breaking Changes
- Semaphore: fix BoltDB migration by pinning 2.18.30 [@MickLesk](https://github.com/MickLesk) ([#17439](https://github.com/community-scripts/ProxmoxVE/pull/17439))
### 💾 Core
- Keep a dash-led bridge comment out of whiptail's options [@MickLesk](https://github.com/MickLesk) ([core#74](https://github.com/community-scripts/core/pull/74))
## 2026-09-25
### 🆕 New Scripts
- RustFS ([#17499](https://github.com/community-scripts/ProxmoxVE/pull/17499))
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Ignis: take the Obsidian version from release tag [@MickLesk](https://github.com/MickLesk) ([#17503](https://github.com/community-scripts/ProxmoxVE/pull/17503))
- #### 🔧 Refactor
- Refactor: Umbrel OS VM [@MickLesk](https://github.com/MickLesk) ([#17505](https://github.com/community-scripts/ProxmoxVE/pull/17505))
- Docker-LXC: remove Portainer installation from install [@MickLesk](https://github.com/MickLesk) ([#17493](https://github.com/community-scripts/ProxmoxVE/pull/17493))
### 💾 Core
- Incus: make prompts visible for end-user [@MickLesk](https://github.com/MickLesk) ([core#73](https://github.com/community-scripts/core/pull/73))
- incus: do not let an install script block on an invisible prompt [@MickLesk](https://github.com/MickLesk) ([core#71](https://github.com/community-scripts/core/pull/71))
- tel: report progress from installs only, not updates [@MickLesk](https://github.com/MickLesk) ([core#72](https://github.com/community-scripts/core/pull/72))
- QoL: message blocks, clean-install keep list, password helper, network limits [@MickLesk](https://github.com/MickLesk) ([core#61](https://github.com/community-scripts/core/pull/61))
+2 -3
View File
@@ -1,6 +1,6 @@
<div align="center">
<a href="misc/images/community-scripts.svg"><img src="misc/images/community-scripts.png" width="112" height="112" alt="Community Scripts — Proxmox VE logo" /></a>
<!-- Editable artwork: misc/images/community-scripts.svg; PNG export: 512 × 512. -->
<a href=".github/assets/community-scripts.svg"><img src=".github/assets/community-scripts.png" width="112" height="112" alt="Community Scripts — Proxmox VE logo" /></a>
<!-- Editable artwork: .github/assets/community-scripts.svg; PNG export: 512 × 512. -->
<h1>Community Scripts — Proxmox VE</h1>
<p><strong>One-command installations for services, containers and VMs on Proxmox VE</strong><br/>
@@ -104,7 +104,6 @@ Virtual machine scripts need the same versions as containers.
| `vm/` | Virtual machine scripts |
| `tools/pve/` | Host tooling: post-install, hardware passthrough, maintenance |
| `turnkey/` | TurnKey Linux appliance helper |
| `misc/` | Legacy engine copies — current scripts load [core](https://github.com/community-scripts/core) at runtime, so changes here never reach a running install |
Application metadata — name, description, logo, tags — is managed on the website,
not in this repository. Use the **Report Issue** link on any script page to
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/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://aws.amazon.com/linux/
APP="Amazon Linux"
var_tags="${var_tags:-os}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-8}"
var_os="${var_os:-amazonlinux}"
var_version="${var_version:-2023}"
var_arm64="${var_arm64:-no}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
msg_info "Updating Amazon Linux LXC"
$STD dnf -y upgrade
msg_ok "Updated Amazon Linux LXC"
cleanup_lxc
exit
}
start
build_container
description
msg_ok "Completed successfully!"
msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!"
+1
View File
@@ -39,6 +39,7 @@ function update_script() {
create_backup /opt/anchor/.env
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "anchor" "ZhFahim/anchor" "tarball"
NODE_VERSION="26" NODE_MODULE="pnpm" setup_nodejs
msg_info "Building Server"
cd /opt/anchor/server
+6 -7
View File
@@ -37,19 +37,16 @@ function update_script() {
msg_ok "Stopped Service"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "aurral" "lklynet/aurral" "tarball"
NODE_VERSION="26" setup_nodejs
NODE_VERSION="26" NODE_MODULE="pnpm" setup_nodejs
ensure_dependencies git
msg_info "Updating Aurral"
cd /opt/aurral
export VITE_APP_VERSION="$(cat ~/.aurral)"
export VITE_GITHUB_REPO="lklynet/aurral"
export VITE_RELEASE_CHANNEL="stable"
# aurral's .npmrc sets engine-strict=true and pins an exact node patch (e.g. 26.8.x),
# which NodeSource can't match since it only ships the latest patch per major.
export npm_config_engine_strict=false
$STD npm ci --workspace frontend --include-workspace-root=false
$STD npm run build --workspace frontend
$STD npm ci --workspace backend --omit=dev --include=optional --include-workspace-root=false
$STD pnpm install --frozen-lockfile --config.engine-strict=false --filter aurral-frontend --filter aurral-backend
$STD pnpm --filter aurral-frontend run build
mkdir -p /opt/aurral_data
cat <<EOF >/opt/aurral/aurral.env
NODE_ENV=production
@@ -57,6 +54,8 @@ PORT=3001
AURRAL_DATA_DIR=/opt/aurral_data
APP_VERSION=$(cat ~/.aurral)
EOF
sed -i 's#backend/server.js#backend/server.ts#' /etc/systemd/system/aurral.service
systemctl daemon-reload
msg_ok "Updated Aurral"
msg_info "Starting Service"
+1 -1
View File
@@ -48,7 +48,7 @@ function update_script() {
msg_ok "Built Cinephage"
msg_info "Updating Camoufox"
$STD /opt/cinephage/node_modules/.bin/camoufox-js fetch
$STD /opt/cinephage/node_modules/.bin/camoufox fetch
msg_ok "Updated Camoufox"
msg_info "Starting Cinephage"
+28 -2
View File
@@ -51,16 +51,42 @@ function update_script() {
cd /opt/discourse
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
export BUNDLE_SILENCE_ROOT_WARNING=1
set -a
source /opt/discourse/.env
set +a
$STD git pull origin main
$STD bundle install
msg_ok "Updated Discourse"
if check_for_gh_release "libvips" "libvips/libvips"; then
ensure_dependencies meson ninja-build pkg-config libglib2.0-dev libexpat1-dev libjpeg-dev libpng-dev libwebp-dev libtiff-dev libheif-dev libjxl-dev librsvg2-dev libcgif-dev libexif-dev liblcms2-dev liborc-0.4-dev libimagequant-dev libpango1.0-dev
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "libvips" "libvips/libvips" "prebuild" "latest" "/opt/libvips" "vips-*.tar.xz"
msg_info "Building libvips"
cd /opt/libvips
$STD meson setup build --prefix=/usr/local --libdir=lib --buildtype=release -Dmodules=disabled -Dmagick=disabled -Dexamples=false
$STD ninja -C build install
ldconfig
msg_ok "Built libvips"
fi
msg_info "Updating Dependencies"
RUBY_TARGET=$(grep -A1 '^RUBY VERSION' /opt/discourse/Gemfile.lock | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
RUBY_VERSION="${RUBY_TARGET:-4.0.7}" RUBY_INSTALL_RAILS="false" setup_ruby
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
hash -r
cd /opt/discourse
$STD bundle install -j"$(nproc)"
$STD pnpm install
msg_ok "Updated Dependencies"
msg_info "Running Database Migrations"
$STD runuser -u postgres -- psql -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;"
$STD bundle exec rails db:migrate
msg_ok "Ran Database Migrations"
msg_info "Precompiling Assets"
$STD bundle exec rails assets:precompile
msg_ok "Updated Discourse"
msg_ok "Precompiled Assets"
msg_info "Starting Services"
systemctl start discourse discourse-sidekiq
+1
View File
@@ -102,6 +102,7 @@ function update_script() {
fi
msg_ok "Ensured ASP.NET Core Runtime"
rm -rf /opt/fileflows/{Server,FlowRunner,Libraries,Agent}
fetch_and_deploy_from_url "https://fileflows.com/downloads/ff-latest.tar.xz" "/opt/fileflows"
msg_info "Starting Service"
+2
View File
@@ -53,6 +53,8 @@ function update_script() {
msg_ok "Restored Database"
fi
NODE_VERSION="26" setup_nodejs
msg_info "Updating Guardian"
cd /opt/guardian/backend
$STD npm ci
+3 -3
View File
@@ -149,13 +149,13 @@ EOF
PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1)
PG_VERSION=${PG_VERSION:-16}
[[ -f ~/.vchord_version ]] && mv ~/.vchord_version ~/.vectorchord
if check_for_gh_release "VectorChord" "tensorchord/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then
if check_for_gh_release "VectorChord" "supervc-stack/VectorChord" "${VCHORD_RELEASE}" "updated together with Immich after testing"; then
# dead tuples in smart_search/face_search make the REINDEX below fail with
# "missing chunk ... for toast value" on VectorChord 1.0.0 (#15588); must vacuum
# while still on the old extension version, a post-upgrade vacuum errors instead
$STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) smart_search;"
$STD sudo -u postgres psql -d immich -c "VACUUM (ANALYZE) face_search;"
fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${PG_VERSION}-vchord_*_$(arch_resolve).deb"
fetch_and_deploy_gh_release "VectorChord" "supervc-stack/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${PG_VERSION}-vchord_*_$(arch_resolve).deb"
systemctl restart postgresql
$STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vector UPDATE;"
$STD sudo -u postgres psql -d immich -c "ALTER EXTENSION vchord UPDATE;"
@@ -232,7 +232,7 @@ EOF
# plugins
cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg,java,terragrunt,opentofu,npm:@openapitools/openapi-generator-cli
export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
mise_ok=0
for i in 1 2 3; do
+4
View File
@@ -39,6 +39,10 @@ function update_script() {
echo "Seerr is the new unified Jellyseerr and Overseerr."
echo "More info: https://docs.seerr.dev/blog/seerr-release"
echo
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
msg_warn "Seerr migration detected - skipping update, requires an interactive run"
exit 75
fi
read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE
echo
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
+9
View File
@@ -33,6 +33,15 @@ function update_script() {
NODE_VERSION="24" setup_nodejs
SERVICE_FILE="/etc/systemd/system/matterjs-server.service"
if [[ -f "$SERVICE_FILE" ]] && ! grep -Fxq 'Environment=ENABLE_TIME_SYNC=true' "$SERVICE_FILE"; then
msg_info "Enabling MatterJS Time Sync"
sed -i '/^ExecStart=/i Environment=ENABLE_TIME_SYNC=true' "$SERVICE_FILE"
$STD systemctl daemon-reload
$STD systemctl restart matterjs-server
msg_ok "Enabled MatterJS Time Sync"
fi
CURRENT=$(cat /opt/matter-server/node_modules/matter-server/package.json | grep '"version"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
LATEST=$(npm view matter-server version 2>/dev/null)
if [[ $CURRENT != "$LATEST" ]]; then
+4 -1
View File
@@ -46,7 +46,10 @@ EOF
systemctl daemon-reload
fi
$STD npm install -g n8n@latest
# n8n-nodes-base 2.46.0 pins both @napi-rs/canvas musl binaries as hard
# dependencies, which no os/cpu/libc combination can satisfy. Drop --force
# once upstream makes them optional again.
$STD npm install -g n8n@latest --force
systemctl restart n8n
msg_ok "Updated n8n"
msg_ok "Updated successfully!"
+2 -3
View File
@@ -9,7 +9,7 @@ source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_
APP="Nametag"
var_tags="${var_tags:-contacts;crm}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_ram="${var_ram:-3072}"
var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
@@ -37,10 +37,9 @@ function update_script() {
msg_ok "Stopped Service"
create_backup /opt/nametag/.env
CLEAN_INSTALL=1 CLEAN_INSTALL_KEEP="data" fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/opt/nametag"
restore_backup
NODE_VERSION="24" setup_nodejs
msg_info "Rebuilding Application"
cd /opt/nametag
+4 -2
View File
@@ -37,9 +37,9 @@ function update_script() {
msg_ok "Stopped Norish"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "norish" "norish-recipes/norish" "tarball"
NODE_VERSION="22" NODE_MODULE="pnpm@$(jq -r '.packageManager | split("@")[1]' /opt/norish/package.json)" setup_nodejs
NODE_VERSION="26" NODE_MODULE="pnpm@$(jq -r '.packageManager | split("@")[1]' /opt/norish/package.json)" setup_nodejs
PYTHON_VERSION="3.14" setup_uv
fetch_and_deploy_gh_release "obscura" "h4ckf0r0day/obscura" "prebuild" "v$(sed -n 's/^OBSCURA_VERSION=//p' /opt/norish/docker/obscura/pin.env)" "/opt/obscura" "obscura-$(arch_resolve "x86_64" "aarch64")-linux-stealth.tar.gz"
fetch_and_deploy_gh_release "obscura" "$(sed -n 's#^OBSCURA_REPO=https://github.com/##p' /opt/norish/docker/obscura/pin.env)" "prebuild" "v$(sed -n 's/^OBSCURA_VERSION=//p' /opt/norish/docker/obscura/pin.env)" "/opt/obscura" "obscura-$(arch_resolve "x86_64" "aarch64")-linux-stealth.tar.gz"
fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp" "singlefile" "latest" "/usr/local/bin" "$(arch_resolve "yt-dlp_linux" "yt-dlp_linux_aarch64")"
msg_info "Building Norish"
@@ -48,6 +48,8 @@ function update_script() {
$STD uv sync --project /opt/norish/apps/parser-api --locked
NODE_ENV=production SKIP_ENV_VALIDATION=1 NEXT_TELEMETRY_DISABLED=1 $STD pnpm turbo run build --filter "@norish/web"
rm -rf /opt/norish/apps/web/.next/cache /opt/norish/apps/web/.next/standalone
sed -i -e "s|^WorkingDirectory=.*|WorkingDirectory=/opt/norish/apps/web|" -e "s|^ExecStart=.*|ExecStart=/usr/bin/node --import tsx server/index.ts|" /etc/systemd/system/norish.service
systemctl daemon-reload
msg_ok "Built Norish"
msg_info "Starting Norish"
+4
View File
@@ -38,6 +38,10 @@ function update_script() {
echo "Seerr is the new unified Jellyseerr and Overseerr."
echo "More info: https://docs.seerr.dev/blog/seerr-release"
echo
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
msg_warn "Seerr migration detected - skipping update, requires an interactive run"
exit 75
fi
read -rp "Do you want to migrate to Seerr now? (y/N): " MIGRATE
echo
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
+9
View File
@@ -63,6 +63,10 @@ function update_script() {
echo -e "${GATEWAY}${BGN}https://docs.paperless-ngx.com/migration-v3/#encryption-support${CL}"
msg_warn "Before continuing make sure that you do not use encryption or have decrypted all documents."
echo ""
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
msg_warn "Paperless-ngx v3 migration detected - skipping update, requires an interactive run"
exit 75
fi
read -rp "Do you want to continue with the update? (y/N): " MIGRATE
echo
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
@@ -71,6 +75,11 @@ function update_script() {
fi
fi
if [[ "${PHS_SILENT:-0}" == "1" ]] && ! grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then
msg_warn "Paperless-ngx uv migration detected - skipping update, requires an interactive run"
exit 75
fi
msg_info "Stopping all Paperless-ngx Services"
systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue
msg_ok "Stopped all Paperless-ngx Services"
+9 -5
View File
@@ -9,7 +9,7 @@ source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_
APP="PS5-MQTT"
var_tags="${var_tags:-smarthome;automation}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-3}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
@@ -37,12 +37,16 @@ function update_script() {
systemctl stop ps5-mqtt
msg_ok "Stopped service"
fetch_and_deploy_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt" "tarball"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt" "tarball"
msg_info "Configuring ${APP}"
cd /opt/ps5-mqtt/ps5-mqtt/
$STD npm install
$STD npm run build
cd /opt/ps5-mqtt
$STD corepack yarn install --immutable
$STD env PREPARE_ONLY=1 corepack yarn workspace @ps5-mqtt/server run package
cd /opt/ps5-mqtt/ps5-mqtt/server/.packaged/server
$STD npm install --omit=dev --omit=optional
sed -i 's|^WorkingDirectory=.*|WorkingDirectory=/opt/ps5-mqtt/ps5-mqtt/server/.packaged|' /etc/systemd/system/ps5-mqtt.service
systemctl daemon-reload
msg_ok "Configured ${APP}"
msg_info "Starting service"
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Owen Voke (owenvoke)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Ralex91/Razzia
APP="Razzia"
var_tags="${var_tags:-quiz;games}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-3072}"
var_disk="${var_disk:-8}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
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/razzia ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "razzia" "Ralex91/Razzia"; then
msg_info "Stopping Service"
systemctl stop razzia
msg_ok "Stopped Service"
create_backup /opt/razzia/.env
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "razzia" "Ralex91/Razzia" "tarball"
restore_backup
NODE_VERSION="24" NODE_MODULE="pnpm@11" setup_nodejs
msg_info "Building Razzia"
cd /opt/razzia
$STD pnpm install --frozen-lockfile
$STD pnpm build
msg_ok "Built Razzia"
msg_info "Starting Service"
systemctl start razzia
msg_ok "Started Service"
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 "${GATEWAY}${BGN}http://${IP}:3000${CL}"
+10
View File
@@ -32,6 +32,16 @@ function update_script() {
exit
fi
ensure_dependencies redis-server
systemctl enable -q --now redis-server
if ! grep -q "BACKEND_CACHE_REDIS_URI" /opt/teable/.env; then
sed -i '/^BACKEND_CACHE_SQLITE_URI=/d' /opt/teable/.env
sed -i 's|^BACKEND_CACHE_PROVIDER=.*|BACKEND_CACHE_PROVIDER=redis\nBACKEND_CACHE_REDIS_URI=redis://127.0.0.1:6379/0|' /opt/teable/.env
sed -i 's|^After=network.target postgresql.service$|After=network.target postgresql.service redis-server.service|' /etc/systemd/system/teable.service
systemctl daemon-reload
systemctl restart teable
fi
if check_for_gh_release "teable" "teableio/teable"; then
msg_info "Stopping Service"
systemctl stop teable
+1 -1
View File
@@ -30,7 +30,7 @@ function update_script() {
exit
fi
NODE_VERSION="24" setup_nodejs
NODE_VERSION="26" setup_nodejs
msg_info "Updating prestart script"
cat <<EOF >/data/tracearr/prestart.sh
+1 -1
View File
@@ -32,7 +32,7 @@ function update_script() {
exit
fi
NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs
NODE_VERSION="26" NODE_MODULE="corepack" setup_nodejs
if check_for_gh_release "twenty" "twentyhq/twenty"; then
msg_info "Stopping Services"
+9 -1
View File
@@ -36,6 +36,10 @@ function update_script() {
msg_warn "This requires MANUAL config changes in /etc/vikunja/config.yml."
msg_warn "See: https://vikunja.io/changelog/whats-new-in-vikunja-1.0.0/#config-changes"
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
msg_warn "Vikunja 1.0 config migration detected - skipping update, requires an interactive run"
exit 75
fi
read -rp "Continue with update? (y to proceed): " -t 30 CONFIRM1 || exit 254
[[ "$CONFIRM1" =~ ^[yY]$ ]] || exit 0
@@ -60,7 +64,11 @@ function update_script() {
echo -e "${TAB}${YW}How do you want to handle /etc/vikunja/config.yml?${CL}"
echo -e "${TAB} 1) Keep your current config"
echo -e "${TAB} 2) Install the new package maintainer's config"
read -rp " Choose [1/2] (default: 1): " -t 60 CONFIG_CHOICE || CONFIG_CHOICE="1"
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
CONFIG_CHOICE="1"
else
read -rp " Choose [1/2] (default: 1): " -t 60 CONFIG_CHOICE || CONFIG_CHOICE="1"
fi
[[ -z "$CONFIG_CHOICE" ]] && CONFIG_CHOICE="1"
if [[ "$CONFIG_CHOICE" == "2" ]]; then
+35
View File
@@ -30,6 +30,41 @@ function update_script() {
exit
fi
if [[ -d /opt/wikijs/backend ]]; then
NODE_VERSION="26" setup_nodejs
if check_for_gh_release "wikijs" "requarks/wiki"; then
PUPPETEER_INSTALLED=$([[ -d /opt/wikijs/backend/node_modules/puppeteer ]] && echo "true" || echo "false")
msg_info "Stopping Service"
systemctl stop wikijs
msg_ok "Stopped Service"
create_backup /opt/wikijs/config.yml
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
restore_backup
cd /opt/wikijs/backend
if [[ "$(arch_resolve)" == "arm64" ]]; then
msg_info "Building Wiki.js Dependencies"
$STD npm ci --omit=dev
msg_ok "Built Wiki.js Dependencies"
fi
if [[ "$PUPPETEER_INSTALLED" == "true" ]]; then
msg_info "Reinstalling Puppeteer"
$STD npm install --no-save --force --include=optional --no-ignore-scripts --no-audit --no-fund "puppeteer@$(sed -n 's/^installVersion: *//p' modules/extensions/puppeteer/definition.yml)"
msg_ok "Reinstalled Puppeteer"
fi
msg_info "Starting Service"
systemctl start wikijs
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
fi
msg_warn "Wiki.js 3 cannot update a 2.x install in place. Export it under Administration > Utilities > Export for Wiki.js 3.x and import it into a new Wiki.js 3 container."
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
if check_for_gh_release "wikijs" "requarks/wiki-v2"; then
+1 -1
View File
@@ -22,7 +22,7 @@ if [[ "${var_os:-}" == "alpine" ]]; then
var_disk="${var_disk:-1}"
var_version="${var_version:-3.24}"
else
var_ram="${var_ram:-512}"
var_ram="${var_ram:-1024}"
var_disk="${var_disk:-4}"
var_version="${var_version:-13}"
fi
+1
View File
@@ -53,6 +53,7 @@ function update_script() {
cd /opt/zerobyte
$STD bun install
$STD node ./node_modules/vite/bin/vite.js build
$STD bun build apps/agent/src/index.ts --outfile .output/agent/index.mjs --target bun
msg_ok "Built Zerobyte"
msg_info "Starting Service"
+22
View File
@@ -0,0 +1,22 @@
#!/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://aws.amazon.com/linux/
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Base Dependencies"
$STD dnf install -y hostname procps-ng
msg_ok "Installed Base Dependencies"
motd_ssh
customize
cleanup_lxc
+1 -1
View File
@@ -13,7 +13,7 @@ setting_up_container
network_check
update_os
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
NODE_VERSION="26" NODE_MODULE="pnpm" setup_nodejs
PG_VERSION="17" setup_postgresql
PG_DB_NAME="anchor" PG_DB_USER="anchor" setup_postgresql_db
+1 -1
View File
@@ -57,7 +57,7 @@ service:
url: community-scripts/ProxmoxVE
use_prerelease: false
dashboard:
icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/images/logo.png
icon: https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/.github/assets/logo.png
icon_link_to: https://community-scripts.org/
web_url: https://github.com/community-scripts/ProxmoxVE/releases
EOF
+5 -8
View File
@@ -16,13 +16,14 @@ update_os
msg_info "Installing Dependencies"
$STD apt install -y \
build-essential \
git \
python3 \
ffmpeg \
fontconfig \
fonts-dejavu-core
msg_ok "Installed Dependencies"
NODE_VERSION="26" setup_nodejs
NODE_VERSION="26" NODE_MODULE="pnpm" setup_nodejs
fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp" "singlefile" "latest" "/usr/local/bin" "yt-dlp"
fetch_and_deploy_gh_release "aurral" "lklynet/aurral" "tarball"
@@ -32,12 +33,8 @@ cd /opt/aurral
export VITE_APP_VERSION="$(cat ~/.aurral)"
export VITE_GITHUB_REPO="lklynet/aurral"
export VITE_RELEASE_CHANNEL="stable"
# aurral's .npmrc sets engine-strict=true and pins an exact node patch (e.g. 26.8.x),
# which NodeSource can't match since it only ships the latest patch per major.
export npm_config_engine_strict=false
$STD npm ci --workspace frontend --include-workspace-root=false
$STD npm run build --workspace frontend
$STD npm ci --workspace backend --omit=dev --include=optional --include-workspace-root=false
$STD pnpm install --frozen-lockfile --config.engine-strict=false --filter aurral-frontend --filter aurral-backend
$STD pnpm --filter aurral-frontend run build
msg_ok "Built Aurral"
msg_info "Creating Service"
@@ -58,7 +55,7 @@ Type=simple
User=root
WorkingDirectory=/opt/aurral
EnvironmentFile=/opt/aurral/aurral.env
ExecStart=/usr/bin/node backend/server.js
ExecStart=/usr/bin/node backend/server.ts
Restart=on-failure
RestartSec=5
+1 -1
View File
@@ -35,7 +35,7 @@ $STD npm prune --omit=dev
msg_ok "Built Cinephage"
msg_info "Installing Camoufox"
$STD /opt/cinephage/node_modules/.bin/camoufox-js fetch
$STD /opt/cinephage/node_modules/.bin/camoufox fetch
msg_ok "Installed Camoufox"
msg_info "Configuring Cinephage"
+35 -3
View File
@@ -25,12 +25,44 @@ $STD apt install -y \
gsfonts \
brotli \
nginx \
redis-server
redis-server \
meson \
ninja-build \
pkg-config \
libglib2.0-dev \
libexpat1-dev \
libjpeg-dev \
libpng-dev \
libwebp-dev \
libtiff-dev \
libheif-dev \
libjxl-dev \
librsvg2-dev \
libcgif-dev \
libexif-dev \
liblcms2-dev \
liborc-0.4-dev \
libimagequant-dev \
libpango1.0-dev
msg_ok "Installed Dependencies"
fetch_and_deploy_gh_release "libvips" "libvips/libvips" "prebuild" "latest" "/opt/libvips" "vips-*.tar.xz"
msg_info "Building libvips"
cd /opt/libvips
$STD meson setup build --prefix=/usr/local --libdir=lib --buildtype=release -Dmodules=disabled -Dmagick=disabled -Dexamples=false
$STD ninja -C build install
ldconfig
msg_ok "Built libvips"
msg_info "Cloning Discourse"
$STD git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse
msg_ok "Cloned Discourse"
PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql
NODE_VERSION="24" setup_nodejs
RUBY_VERSION="3.4.4" setup_ruby
RUBY_TARGET=$(grep -A1 '^RUBY VERSION' /opt/discourse/Gemfile.lock | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
RUBY_VERSION="${RUBY_TARGET:-4.0.7}" RUBY_INSTALL_RAILS="false" setup_ruby
msg_info "Configuring PostgreSQL for Discourse"
DISCOURSE_DB_PASS=$(random_password 13)
@@ -42,7 +74,6 @@ msg_ok "Configured PostgreSQL for Discourse"
msg_info "Configuring Discourse"
DISCOURSE_SECRET_KEY=$(openssl rand -hex 64)
$STD git clone --depth 1 https://github.com/discourse/discourse.git /opt/discourse
cd /opt/discourse
cat <<EOF >/opt/discourse/.env
RAILS_ENV=production
@@ -77,6 +108,7 @@ export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
export BUNDLE_SILENCE_ROOT_WARNING=1
$STD corepack enable
$STD bundle config set --local deployment true
$STD bundle config set --local without 'test development'
+1 -1
View File
@@ -17,7 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y sqlite3
msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
NODE_VERSION="26" setup_nodejs
fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian"
msg_info "Configuring ${APPLICATION}"
+30 -23
View File
@@ -18,6 +18,11 @@ if is_debian && [[ "$(get_os_version_major)" -lt 13 ]]; then
exit 238
fi
if [[ "$(arch_resolve)" == "amd64" ]] && ! grep -qm1 sse4_2 /proc/cpuinfo; then
msg_error "Immich needs a CPU with SSE4.2: its web build stops with 'Illegal instruction' on older ones."
exit 236
fi
RELEASE="v3.3.5"
if lscpu | grep -q 'GenuineIntel'; then
echo ""
@@ -108,6 +113,7 @@ $STD apt install --no-install-recommends -y \
libhwy-dev \
libwebp-dev \
libaom-dev \
unzip \
ccache
setup_deb822_repo \
@@ -129,6 +135,28 @@ if [[ "$CTTYPE" == "0" && -d /dev/dri ]]; then
fi
msg_ok "Dependencies Installed"
INSTALL_DIR="/opt/${APPLICATION}"
UPLOAD_DIR="${INSTALL_DIR}/upload"
SRC_DIR="${INSTALL_DIR}/source"
APP_DIR="${INSTALL_DIR}/app"
PLUGIN_DIR="${APP_DIR}/plugins/immich-plugin-core"
ML_DIR="${APP_DIR}/machine-learning"
GEO_DIR="${INSTALL_DIR}/geodata"
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
msg_info "Installing GeoNames data"
cd "$GEO_DIR"
curl_with_retry "https://download.geonames.org/export/dump/admin1CodesASCII.txt" "admin1CodesASCII.txt"
curl_with_retry "https://download.geonames.org/export/dump/admin2Codes.txt" "admin2Codes.txt"
curl_with_retry "https://download.geonames.org/export/dump/countryInfo.txt" "countryInfo.txt"
curl_with_retry "https://download.geonames.org/export/dump/cities500.zip" "cities500.zip"
curl_with_retry "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" "ne_10m_admin_0_countries.geojson"
unzip -q cities500.zip
date --iso-8601=seconds | tr -d "\n" >geodata-date.txt
rm cities500.zip
cd ~
msg_ok "Installed GeoNames data"
MISE_ARCH="x64"
[[ "$(arch_resolve)" == "arm64" ]] && MISE_ARCH="arm64"
fetch_and_deploy_gh_release "mise" "jdx/mise" "singlefile" "latest" "/usr/local/bin" "mise-v*-linux-${MISE_ARCH}"
@@ -167,7 +195,7 @@ ACTUAL_PG_VERSION=$(ls /etc/postgresql/ 2>/dev/null | sort -V | tail -1)
ACTUAL_PG_VERSION=${ACTUAL_PG_VERSION:-16}
VCHORD_RELEASE="1.1.1"
fetch_and_deploy_gh_release "VectorChord" "tensorchord/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${ACTUAL_PG_VERSION}-vchord_*_$(arch_resolve).deb"
fetch_and_deploy_gh_release "VectorChord" "supervc-stack/VectorChord" "binary" "${VCHORD_RELEASE}" "/tmp" "postgresql-${ACTUAL_PG_VERSION}-vchord_*_$(arch_resolve).deb"
sed -i "s/^#shared_preload.*/shared_preload_libraries = 'vchord.so'/" /etc/postgresql/${ACTUAL_PG_VERSION}/main/postgresql.conf
systemctl restart postgresql.service
@@ -347,15 +375,6 @@ libvips: $LIBVIPS_REVISION
EOF
msg_ok "Custom Photo-processing Libraries Compiled Successfully"
INSTALL_DIR="/opt/${APPLICATION}"
UPLOAD_DIR="${INSTALL_DIR}/upload"
SRC_DIR="${INSTALL_DIR}/source"
APP_DIR="${INSTALL_DIR}/app"
PLUGIN_DIR="${APP_DIR}/plugins/immich-plugin-core"
ML_DIR="${APP_DIR}/machine-learning"
GEO_DIR="${INSTALL_DIR}/geodata"
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
fetch_and_deploy_gh_release "Immich" "immich-app/immich" "tarball" "${RELEASE}" "$SRC_DIR"
PNPM_VERSION="$(jq -r '.packageManager | split("@")[1] | split("+")[0]' ${SRC_DIR}/package.json)"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
@@ -396,7 +415,7 @@ cp -a web/build "$APP_DIR"/www
cp LICENSE "$APP_DIR"
cd "$SRC_DIR"
export MISE_TRUSTED_CONFIG_PATHS="$SRC_DIR"/mise.toml
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg
export MISE_DISABLE_TOOLS=github:jellyfin/jellyfin-ffmpeg,java,terragrunt,opentofu,npm:@openapitools/openapi-generator-cli
export MISE_LOCKFILE_MODE=merge MISE_LOCKFILE_PLATFORMS="linux-$(arch_resolve x64 arm64)"
mise_ok=0
for i in 1 2 3; do
@@ -481,20 +500,8 @@ grep -rlE "'/build'" | xargs -n1 sed -i "s|'/build'|'$APP_DIR'|g"
sed -i "s@\"/cache\"@\"$INSTALL_DIR/cache\"@g" "$ML_DIR"/immich_ml/config.py
ln -s "$UPLOAD_DIR" "$APP_DIR"/upload
ln -s "$UPLOAD_DIR" "$ML_DIR"/upload
msg_info "Installing GeoNames data"
cd "$GEO_DIR"
curl_with_retry "https://download.geonames.org/export/dump/admin1CodesASCII.txt" "admin1CodesASCII.txt"
curl_with_retry "https://download.geonames.org/export/dump/admin2Codes.txt" "admin2Codes.txt"
curl_with_retry "https://download.geonames.org/export/dump/countryInfo.txt" "countryInfo.txt"
curl_with_retry "https://download.geonames.org/export/dump/cities500.zip" "cities500.zip"
curl_with_retry "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson/ne_10m_admin_0_countries.geojson" "ne_10m_admin_0_countries.geojson"
unzip -q cities500.zip
date --iso-8601=seconds | tr -d "\n" >geodata-date.txt
rm cities500.zip
cd "$INSTALL_DIR"
ln -s "$GEO_DIR" "$APP_DIR"
msg_ok "Installed GeoNames data"
# MickLesk temporary patch for HEIC thumbnail gen
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
+1
View File
@@ -43,6 +43,7 @@ After=network.target
[Service]
Type=simple
User=root
Environment=ENABLE_TIME_SYNC=true
ExecStart=/usr/bin/node /opt/matter-server/node_modules/matter-server/dist/esm/MatterServer.js --storage-path /var/lib/matterjs-server
Restart=on-failure
RestartSec=5
+4 -1
View File
@@ -23,7 +23,10 @@ msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
msg_info "Installing n8n (Patience)"
$STD npm install -g n8n@latest
# n8n-nodes-base 2.46.0 pins both @napi-rs/canvas musl binaries as hard
# dependencies, which no os/cpu/libc combination can satisfy. Drop --force
# once upstream makes them optional again.
$STD npm install -g n8n@latest --force
msg_ok "Installed n8n"
msg_info "Creating Service"
+1 -1
View File
@@ -15,7 +15,7 @@ update_os
PG_VERSION="16" setup_postgresql
PG_DB_NAME="nametag_db" PG_DB_USER="nametag" setup_postgresql_db
NODE_VERSION="20" setup_nodejs
NODE_VERSION="24" setup_nodejs
fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/opt/nametag"
msg_info "Setting up Application"
+4 -4
View File
@@ -22,8 +22,8 @@ PG_DB_NAME="norish" PG_DB_USER="norish" setup_postgresql_db
PYTHON_VERSION="3.14" setup_uv
fetch_and_deploy_gh_release "norish" "norish-recipes/norish" "tarball"
NODE_VERSION="22" NODE_MODULE="pnpm@$(jq -r '.packageManager | split("@")[1]' /opt/norish/package.json)" setup_nodejs
fetch_and_deploy_gh_release "obscura" "h4ckf0r0day/obscura" "prebuild" "v$(sed -n 's/^OBSCURA_VERSION=//p' /opt/norish/docker/obscura/pin.env)" "/opt/obscura" "obscura-$(arch_resolve "x86_64" "aarch64")-linux-stealth.tar.gz"
NODE_VERSION="26" NODE_MODULE="pnpm@$(jq -r '.packageManager | split("@")[1]' /opt/norish/package.json)" setup_nodejs
fetch_and_deploy_gh_release "obscura" "$(sed -n 's#^OBSCURA_REPO=https://github.com/##p' /opt/norish/docker/obscura/pin.env)" "prebuild" "v$(sed -n 's/^OBSCURA_VERSION=//p' /opt/norish/docker/obscura/pin.env)" "/opt/obscura" "obscura-$(arch_resolve "x86_64" "aarch64")-linux-stealth.tar.gz"
fetch_and_deploy_gh_release "yt-dlp" "yt-dlp/yt-dlp" "singlefile" "latest" "/usr/local/bin" "$(arch_resolve "yt-dlp_linux" "yt-dlp_linux_aarch64")"
msg_info "Building Norish"
@@ -75,9 +75,9 @@ Wants=obscura.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/norish
WorkingDirectory=/opt/norish/apps/web
EnvironmentFile=/opt/norish_data/.env
ExecStart=/usr/bin/node /opt/norish/dist-server/index.mjs
ExecStart=/usr/bin/node --import tsx server/index.ts
Restart=on-failure
RestartSec=5
+6 -4
View File
@@ -23,9 +23,11 @@ NODE_VERSION="24" NODE_MODULE="playactor" setup_nodejs
fetch_and_deploy_gh_release "ps5-mqtt" "FunkeyFlo/ps5-mqtt" "tarball"
msg_info "Configuring PS5-MQTT"
cd /opt/ps5-mqtt/ps5-mqtt/
$STD npm install
$STD npm run build
cd /opt/ps5-mqtt
$STD corepack yarn install --immutable
$STD env PREPARE_ONLY=1 corepack yarn workspace @ps5-mqtt/server run package
cd /opt/ps5-mqtt/ps5-mqtt/server/.packaged/server
$STD npm install --omit=dev --omit=optional
mkdir -p /opt/.config/ps5-mqtt/
mkdir -p /opt/.config/ps5-mqtt/playactor
cat <<EOF >/opt/.config/ps5-mqtt/config.json
@@ -66,7 +68,7 @@ Description=PS5-MQTT Daemon
After=syslog.target network.target
[Service]
WorkingDirectory=/opt/ps5-mqtt/ps5-mqtt
WorkingDirectory=/opt/ps5-mqtt/ps5-mqtt/server/.packaged
Environment="CONFIG_PATH=/opt/.config/ps5-mqtt/config.json"
Environment="DEBUG='@ha:ps5:*'"
Restart=always
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Owen Voke (owenvoke)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/Ralex91/Razzia
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 nginx
msg_ok "Installed Dependencies"
NODE_VERSION="24" NODE_MODULE="pnpm@11" setup_nodejs
fetch_and_deploy_gh_release "razzia" "Ralex91/Razzia" "tarball"
msg_info "Building Razzia"
cd /opt/razzia
$STD pnpm install --frozen-lockfile
$STD pnpm build
msg_ok "Built Razzia"
msg_info "Configuring Razzia"
mkdir -p /opt/razzia-config
cat <<EOF >/opt/razzia/.env
MANAGER_PASSWORD=$(random_password 16)
JWT_SECRET=$(openssl rand -hex 32)
EOF
msg_ok "Configured Razzia"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/razzia.service
[Unit]
Description=Razzia Socket Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/razzia/packages/socket
EnvironmentFile=/opt/razzia/.env
Environment=NODE_ENV=production
Environment=CONFIG_PATH=/opt/razzia-config
ExecStart=/usr/bin/node /opt/razzia/packages/socket/dist/index.cjs
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now razzia
msg_ok "Created Service"
msg_info "Configuring Nginx"
cat <<EOF >/etc/nginx/sites-available/razzia
server {
listen 3000;
server_name _;
root /opt/razzia/packages/web/dist;
index index.html;
location / {
try_files \$uri \$uri/ /index.html;
}
location /branding/ {
alias /opt/razzia-config/branding/;
}
location /media/ {
alias /opt/razzia-config/media/;
types {
image/png png;
image/jpeg jpg;
image/webp webp;
image/gif gif;
audio/mpeg mp3;
audio/ogg ogg;
audio/wav wav;
video/mp4 mp4;
video/webm webm;
}
default_type application/octet-stream;
add_header X-Content-Type-Options nosniff;
}
location /api {
client_max_body_size 101m;
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host \$host;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
location /ws {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
EOF
nginx_enable_site razzia
msg_ok "Configured Nginx"
motd_ssh
customize
cleanup_lxc
+6 -4
View File
@@ -17,7 +17,8 @@ msg_info "Installing Dependencies"
$STD apt install -y \
build-essential \
python3 \
git
git \
redis-server
msg_ok "Installed Dependencies"
NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs
@@ -56,8 +57,8 @@ SECRET_KEY=${SECRET_KEY}
PORT=3000
NODE_ENV=production
NEXT_TELEMETRY_DISABLED=1
BACKEND_CACHE_PROVIDER=sqlite
BACKEND_CACHE_SQLITE_URI=sqlite:///opt/teable/.assets/.cache.db
BACKEND_CACHE_PROVIDER=redis
BACKEND_CACHE_REDIS_URI=redis://127.0.0.1:6379/0
NEXTJS_DIR=apps/nextjs-app
EOF
ln -sf /opt/teable /app
@@ -70,10 +71,11 @@ fi
msg_ok "Configured Teable"
msg_info "Creating Service"
systemctl enable -q --now redis-server
cat <<EOF >/etc/systemd/system/teable.service
[Unit]
Description=Teable
After=network.target postgresql.service
After=network.target postgresql.service redis-server.service
[Service]
Type=simple
+1 -1
View File
@@ -17,7 +17,7 @@ msg_info "Installing Dependencies"
$STD apt install -y redis-server
msg_ok "Installed Dependencies"
NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs
NODE_VERSION="26" NODE_MODULE="corepack" setup_nodejs
PG_VERSION="18" setup_postgresql
msg_info "Installing pnpm"
+1 -1
View File
@@ -21,7 +21,7 @@ msg_ok "Installed Dependencies"
PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql
PG_DB_NAME="twenty_db" PG_DB_USER="twenty" PG_DB_SCHEMA_PERMS="true" PG_DB_EXTENSIONS="vector" setup_postgresql_db
NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs
NODE_VERSION="26" NODE_MODULE="corepack" setup_nodejs
fetch_and_deploy_gh_release "twenty" "twentyhq/twenty" "tarball"
+5 -1
View File
@@ -21,7 +21,11 @@ msg_info "Setup Wazuh"
curl -fsSL https://packages.wazuh.com/$RELEASE/wazuh-install.sh -o wazuh-install.sh
chmod +x wazuh-install.sh
if [ "$STD" = "silent" ]; then
bash wazuh-install.sh -a >>~/wazuh-install.output
bash wazuh-install.sh -a >>~/wazuh-install.output || {
$STD tail -n 25 ~/wazuh-install.output
msg_error "The Wazuh installer failed, see its output above or in ~/wazuh-install.output"
exit 1
}
else
bash wazuh-install.sh -a | tee -a ~/wazuh-install.output
fi
+32 -8
View File
@@ -17,26 +17,44 @@ msg_info "Installing Dependencies"
$STD apt install -y git
msg_ok "Installed Dependencies"
NODE_VERSION="26" NODE_MODULE="yarn,node-gyp" setup_nodejs
NODE_VERSION="26" setup_nodejs
PG_VERSION="17" setup_postgresql
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" PG_DB_EXTENSIONS="pg_trgm" setup_postgresql_db
fetch_and_deploy_gh_release "wikijs" "requarks/wiki-v2" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
PG_DB_NAME="wiki" PG_DB_USER="wikijs_user" setup_postgresql_db
fetch_and_deploy_gh_release "wikijs" "requarks/wiki" "prebuild" "latest" "/opt/wikijs" "wiki-js.tar.gz"
if [[ "$(arch_resolve)" == "arm64" ]]; then
msg_info "Building Wiki.js Dependencies"
cd /opt/wikijs/backend
$STD npm ci --omit=dev
msg_ok "Built Wiki.js Dependencies"
fi
msg_info "Configuring Wiki.js"
mv /opt/wikijs/config.sample.yml /opt/wikijs/config.yml
sed -i -E 's|^( *user: ).*|\1'"$PG_DB_USER"'|' /opt/wikijs/config.yml
sed -i -E 's|^( *pass: ).*|\1'"$PG_DB_PASS"'|' /opt/wikijs/config.yml
mkdir -p /opt/wikijs_data
cp /opt/wikijs/config.sample.yml /opt/wikijs/config.yml
sed -i \
-e "s|^ user: .*| user: wikijs_user|" \
-e "s|^ pass: .*| pass: ${PG_DB_PASS}|" \
-e "s|^ db: .*| db: wiki|" \
-e "s|^dataPath: .*|dataPath: /opt/wikijs_data|" \
/opt/wikijs/config.yml
ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16)
cat <<EOF >~/wikijs.creds
Wiki.js Admin
Email: admin@example.com
Password: ${ADMIN_PASS}
EOF
msg_ok "Configured Wiki.js"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/wikijs.service
[Unit]
Description=Wiki.js
After=network.target
After=network.target postgresql.service
[Service]
Type=simple
ExecStart=/usr/bin/node server
ExecStart=/usr/bin/node --no-experimental-webstorage backend
Restart=always
User=root
Environment=NODE_ENV=production
@@ -45,7 +63,13 @@ WorkingDirectory=/opt/wikijs
[Install]
WantedBy=multi-user.target
EOF
systemctl set-environment ADMIN_PASS="${ADMIN_PASS}"
systemctl enable -q --now wikijs
for _ in $(seq 60); do
curl -fs -o /dev/null http://127.0.0.1:3000 && break
sleep 2
done
systemctl unset-environment ADMIN_PASS
msg_ok "Created Service"
motd_ssh
+5 -5
View File
@@ -36,17 +36,17 @@ cd /tmp
ZABBIX_REPO_DIR="debian"
[[ "$ZABBIX_VERSION" == "7.0" && "$(arch_resolve)" == "arm64" ]] && ZABBIX_REPO_DIR="debian-arm64"
if [[ "$ZABBIX_VERSION" == "7.0" ]]; then
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/${ZABBIX_REPO_DIR}/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb"
ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian13_all.deb"
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/${ZABBIX_REPO_DIR}/pool/main/z/zabbix-release/zabbix-release_latest_${ZABBIX_VERSION}+debian$(get_os_version_major)_all.deb"
ZABBIX_DEB_FILE="zabbix-release_latest_${ZABBIX_VERSION}+debian$(get_os_version_major)_all.deb"
else
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian13_all.deb"
ZABBIX_DEB_FILE="zabbix-release_latest+debian13_all.deb"
ZABBIX_DEB_URL="https://repo.zabbix.com/zabbix/${ZABBIX_VERSION}/release/debian/pool/main/z/zabbix-release/zabbix-release_latest+debian$(get_os_version_major)_all.deb"
ZABBIX_DEB_FILE="zabbix-release_latest+debian$(get_os_version_major)_all.deb"
fi
curl -fsSL "$ZABBIX_DEB_URL" -o /tmp/"$ZABBIX_DEB_FILE"
$STD dpkg -i /tmp/"$ZABBIX_DEB_FILE"
apt_update_safe
$STD apt install -y zabbix-server-pgsql zabbix-frontend-php php8.4-pgsql zabbix-apache-conf zabbix-sql-scripts
$STD apt install -y zabbix-server-pgsql zabbix-frontend-php php-pgsql zabbix-apache-conf zabbix-sql-scripts
if [[ "$ZABBIX_VERSION" == "7.0" ]]; then
ZABBIX_SQL="/usr/share/zabbix-sql-scripts/postgresql/server.sql.gz"
+1
View File
@@ -53,6 +53,7 @@ export VITE_SHOUTRRR_VERSION=$(cat ~/.shoutrrr)
export NODE_OPTIONS="--max-old-space-size=3072"
$STD bun install
$STD node ./node_modules/vite/bin/vite.js build
$STD bun build apps/agent/src/index.ts --outfile .output/agent/index.mjs --target bun
msg_ok "Built Zerobyte"
msg_info "Configuring Zerobyte"
-275
View File
@@ -1,275 +0,0 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster)
# Co-Author: MickLesk
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
if ! command -v curl >/dev/null 2>&1; then
apk update && apk add curl >/dev/null 2>&1
fi
# Mark container context BEFORE error handling starts: error_handler/on_exit
# must write local failure artifacts instead of talking to the telemetry API
# (the host is the single telemetry reporter).
export TELEMETRY_CONTEXT="container"
# A freshly booted container may not have DNS up yet. `source <(curl ...)` hides
# curl's exit code, so a failed download would silently source an empty stream.
_bootstrap_die() {
if declare -f msg_error >/dev/null 2>&1; then
msg_error "$1"
else
echo "FATAL: $1" >&2
fi
exit 115
}
_bootstrap_source() {
local url="$1" probe="$2" content
content=$(curl -fsSL --connect-timeout 10 --retry 5 --retry-connrefused --retry-delay 2 "$url") ||
_bootstrap_die "Failed to download ${url##*/}"
source /dev/stdin <<<"$content"
declare -f "$probe" >/dev/null 2>&1 ||
_bootstrap_die "${url##*/} loaded but incomplete (missing ${probe})"
}
_FUNC_BASE="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc"
_bootstrap_source "$_FUNC_BASE/core.func" load_functions
_bootstrap_source "$_FUNC_BASE/error_handler.func" catch_errors
load_functions
catch_errors
# Persist diagnostics setting inside container (exported from build.func)
# so addon scripts running later can find the user's choice
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics
fi
# Get LXC IP address (must be called INSIDE container, after network is up)
get_lxc_ip
# ------------------------------------------------------------------------------
# post_progress_to_api()
#
# - Lightweight progress ping from inside the container
# - Updates the existing telemetry record status
# - Arguments:
# * $1: status (optional, default: "configuring")
# - Signals that the installation is actively progressing (not stuck)
# - Fire-and-forget: never blocks or fails the script
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
# ------------------------------------------------------------------------------
post_progress_to_api() {
command -v curl &>/dev/null || return 0
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
[[ -z "${RANDOM_UUID:-}" ]] && return 0
local progress_status="${1:-configuring}"
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
-H "Content-Type: application/json" \
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\",\"platform\":\"${TELEMETRY_PLATFORM:-}\",\"repo_source\":\"${REPO_SOURCE:-}\",\"repo_slug\":\"${REPO_SLUG:-}\"}" &>/dev/null || true
}
# This function enables IPv6 if it's not disabled and sets verbose mode
verb_ip6() {
set_std_mode # Set STD mode based on VERBOSE
if [ "${IPV6_METHOD:-}" = "disable" ]; then
msg_info "Disabling IPv6 (this may affect some services)"
$STD sysctl -w net.ipv6.conf.all.disable_ipv6=1
$STD sysctl -w net.ipv6.conf.default.disable_ipv6=1
$STD sysctl -w net.ipv6.conf.lo.disable_ipv6=1
mkdir -p /etc/sysctl.d
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
$STD rc-update add sysctl default
msg_ok "Disabled IPv6"
fi
}
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() {
msg_info "Setting up Container OS"
while [ $i -gt 0 ]; do
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
break
fi
echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep $RETRY_EVERY
i=$((i - 1))
done
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
echo -e "${NETWORK}Check Network Settings"
exit 121
fi
msg_ok "Set up Container OS"
msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}"
post_progress_to_api
}
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
network_check() {
set +e
trap - ERR
ipv4_connected=false
# Check IPv4 connectivity to Cloudflare, Google & Quad9 DNS servers
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
msg_ok "IPv4 Internet Connected"
ipv4_connected=true
else
msg_error "IPv4 Internet Not Connected"
fi
if [[ $ipv4_connected == false ]]; then
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt </dev/tty
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
else
echo -e "${NETWORK}Check Network Settings"
exit 122
fi
fi
# DNS resolution checks for GitHub-related domains
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com")
GIT_STATUS="Git DNS:"
DNS_FAILED=false
for HOST in "${GIT_HOSTS[@]}"; do
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
if [[ -z "$RESOLVEDIP" ]]; then
GIT_STATUS+="$HOST:($DNSFAIL)"
DNS_FAILED=true
else
GIT_STATUS+=" $HOST:($DNSOK)"
fi
done
if [[ "$DNS_FAILED" == true ]]; then
fatal "$GIT_STATUS"
else
msg_ok "$GIT_STATUS"
fi
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
}
# This function updates the Container OS by running apk upgrade with mirror fallback
update_os() {
msg_info "Updating Container OS"
configure_http_proxy
if ! $STD apk -U upgrade; then
msg_warn "apk update failed (dl-cdn.alpinelinux.org), trying alternate mirrors..."
local alpine_mirrors="mirror.init7.net ftp.halifax.rwth-aachen.de mirrors.edge.kernel.org alpine.mirror.wearetriple.com mirror.leaseweb.com uk.alpinelinux.org dl-2.alpinelinux.org dl-4.alpinelinux.org"
local apk_ok=false
for m in $(printf '%s\n' $alpine_mirrors | shuf); do
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${m}"
cat <<EOF >/etc/apk/repositories
http://$m/alpine/latest-stable/main
http://$m/alpine/latest-stable/community
EOF
if $STD apk -U upgrade; then
msg_ok "CDN set to ${m}: tests passed"
apk_ok=true
break
else
msg_warn "Mirror ${m} failed"
fi
fi
done
if [[ "$apk_ok" != true ]]; then
msg_error "All Alpine mirrors failed. Check network or try again later."
exit 1
fi
fi
local tools_content
tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || {
msg_error "Failed to download tools.func"
exit 115
}
source /dev/stdin <<<"$tools_content"
if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then
msg_error "tools.func loaded but incomplete — missing expected functions"
exit 115
fi
msg_ok "Updated Container OS"
post_progress_to_api
}
# This function modifies the message of the day (motd) and SSH settings
motd_ssh() {
echo "export TERM='xterm-256color'" >>/root/.bashrc
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
echo "[ -t 1 ] || return 0" >"$PROFILE_FILE"
echo "echo -e \"\"" >>"$PROFILE_FILE"
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
echo "echo \"\"" >>"$PROFILE_FILE"
cat <<EOF >>"$PROFILE_FILE"
os_display="Unknown OS"
if [ -r /etc/os-release ]; then
. /etc/os-release
os_display="\${PRETTY_NAME:-\${NAME:-Unknown OS}}"
fi
echo -e "${TAB}${OS}${YW} OS: ${GN}\${os_display}${CL}"
EOF
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(ip -4 addr show eth0 | awk '/inet / {print \$2}' | cut -d/ -f1 | head -n 1)${CL}\"" >>"$PROFILE_FILE"
# Configure SSH if enabled
if [[ "${SSH_ROOT}" == "yes" ]]; then
# Enable sshd service
$STD rc-update add sshd
# Allow root login via SSH
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
# Start the sshd service
$STD /etc/init.d/sshd start
fi
post_progress_to_api
}
# Validate Timezone for some LXC's
validate_tz() {
[[ -f "/usr/share/zoneinfo/$1" ]]
}
# This function customizes the container and enables passwordless login for the root user
customize() {
if [[ "$PASSWORD" == "" ]]; then
msg_info "Customizing Container"
passwd -d root >/dev/null 2>&1
# Ensure agetty is available
apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1
# Create persistent autologin boot script
mkdir -p /etc/local.d
cat <<'EOF' >/etc/local.d/autologin.start
#!/bin/sh
sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab
kill -HUP 1
EOF
touch /root/.hushlogin
chmod +x /etc/local.d/autologin.start
rc-update add local >/dev/null 2>&1
# Apply autologin immediately for current session
/etc/local.d/autologin.start
msg_ok "Customized Container"
fi
echo 'set -a; [ -f /etc/profile.d/90-http-proxy.sh ] && . /etc/profile.d/90-http-proxy.sh; set +a; bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/'"${app}"'.sh)"' >/usr/bin/update
chmod +x /usr/bin/update
post_progress_to_api
}
-515
View File
@@ -1,515 +0,0 @@
#!/bin/ash
# shellcheck shell=ash
# Expects existing msg_* functions and optional $STD from the framework.
# ------------------------------
# helpers
# ------------------------------
lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; }
has() { command -v "$1" >/dev/null 2>&1; }
need_tool() {
# usage: need_tool curl jq unzip ...
# setup missing tools via apk
local missing=0 t
for t in "$@"; do
if ! has "$t"; then missing=1; fi
done
if [ "$missing" -eq 1 ]; then
msg_info "Installing tools: $*"
apk add --no-cache "$@" >/dev/null 2>&1 || {
msg_error "apk add failed for: $*"
return 100
}
msg_ok "Tools ready: $*"
fi
}
net_resolves() {
# better handling for missing getent on Alpine
# usage: net_resolves api.github.com
local host="$1"
ping -c1 -W1 "$host" >/dev/null 2>&1 || nslookup "$host" >/dev/null 2>&1
}
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() {
# $1 url, $2 dest
local url="$1" out="$2" cl
need_tool curl pv || return 127
cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r')
if [ -n "$cl" ]; then
curl -fsSL "$url" | pv -s "$cl" >"$out" || {
msg_error "Download failed: $url"
return 250
}
else
curl -fL# -o "$out" "$url" || {
msg_error "Download failed: $url"
return 250
}
fi
}
# ------------------------------
# GitHub: check Release
# ------------------------------
check_for_gh_release() {
# app, repo, [pinned]
local app="$1" source="$2" pinned="${3:-}"
local app_lc
app_lc="$(lower "$app" | tr -d ' ')"
local current_file="$HOME/.${app_lc}"
local current="" release tag
msg_info "Check for update: $app"
net_resolves api.github.com || {
msg_error "DNS/network error: api.github.com"
return 6
}
need_tool curl jq || return 127
tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty')
[ -z "$tag" ] && {
msg_error "Unable to fetch latest tag for $app"
return 22
}
release="${tag#v}"
[ -f "$current_file" ] && current="$(cat "$current_file")"
if [ -n "$pinned" ]; then
if [ "$pinned" = "$release" ]; then
msg_ok "$app pinned to v$pinned (no update)"
return 1
fi
if [ "$current" = "$pinned" ]; then
msg_ok "$app pinned v$pinned installed (upstream v$release)"
return 1
fi
msg_info "$app pinned v$pinned (upstream v$release) → update/downgrade"
CHECK_UPDATE_RELEASE="$pinned"
return 0
fi
if [ "$release" != "$current" ] || [ ! -f "$current_file" ]; then
CHECK_UPDATE_RELEASE="$release"
msg_info "New release available: v$release (current: v${current:-none})"
return 0
fi
msg_ok "$app is up to date (v$release)"
return 1
}
# ------------------------------
# GitHub: get Release & deploy (Alpine)
# modes: tarball | prebuild | singlefile
# ------------------------------
fetch_and_deploy_gh() {
# $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern
local app="$1" repo="$2" mode="${3:-tarball}" version="${4:-latest}" target="${5:-/opt/$1}" pattern="${6:-}"
local app_lc
app_lc="$(lower "$app" | tr -d ' ')"
local vfile="$HOME/.${app_lc}"
local json url filename tmpd unpack
net_resolves api.github.com || {
msg_error "DNS/network error"
return 6
}
need_tool curl jq tar || return 127
[ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true
tmpd="$(mktemp -d)" || return 252
mkdir -p "$target"
# Release JSON
if [ "$version" = "latest" ]; then
json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || {
msg_error "GitHub API failed"
rm -rf "$tmpd"
return 22
}
else
json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || {
msg_error "GitHub API failed"
rm -rf "$tmpd"
return 22
}
fi
# correct Version
version="$(printf '%s' "$json" | jq -r '.tag_name // empty')"
version="${version#v}"
[ -z "$version" ] && {
msg_error "No tag in release json"
rm -rf "$tmpd"
return 65
}
case "$mode" in
tarball | source)
url="$(printf '%s' "$json" | jq -r '.tarball_url // empty')"
[ -z "$url" ] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz"
filename="${app_lc}-${version}.tar.gz"
download_with_progress "$url" "$tmpd/$filename" || {
rm -rf "$tmpd"
return 250
}
tar -xzf "$tmpd/$filename" -C "$tmpd" || {
msg_error "tar extract failed"
rm -rf "$tmpd"
return 251
}
unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)"
# copy content of unpack to target
(cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed"
rm -rf "$tmpd"
return 252
}
;;
prebuild)
[ -n "$pattern" ] || {
msg_error "prebuild requires asset pattern"
rm -rf "$tmpd"
return 65
}
url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" '
BEGIN{IGNORECASE=1}
$0 ~ p {print; exit}
')"
[ -z "$url" ] && {
msg_error "asset not found for pattern: $pattern"
rm -rf "$tmpd"
return 250
}
filename="${url##*/}"
download_with_progress "$url" "$tmpd/$filename" || {
rm -rf "$tmpd"
return 250
}
# unpack archive (Zip or tarball)
case "$filename" in
*.zip)
need_tool unzip || {
rm -rf "$tmpd"
return 127
}
mkdir -p "$tmpd/unp"
unzip -q "$tmpd/$filename" -d "$tmpd/unp"
;;
*.tar.gz | *.tgz | *.tar.xz | *.tar.zst | *.tar.bz2)
mkdir -p "$tmpd/unp"
tar -xf "$tmpd/$filename" -C "$tmpd/unp"
;;
*)
msg_error "unsupported archive: $filename"
rm -rf "$tmpd"
return 251
;;
esac
# top-level folder strippen
if [ "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ] && [ -z "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type f | head -n1)" ]; then
unpack="$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d)"
(cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed"
rm -rf "$tmpd"
return 252
}
else
(cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || {
msg_error "copy failed"
rm -rf "$tmpd"
return 252
}
fi
;;
singlefile)
[ -n "$pattern" ] || {
msg_error "singlefile requires asset pattern"
rm -rf "$tmpd"
return 65
}
url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" '
BEGIN{IGNORECASE=1}
$0 ~ p {print; exit}
')"
[ -z "$url" ] && {
msg_error "asset not found for pattern: $pattern"
rm -rf "$tmpd"
return 250
}
filename="${url##*/}"
download_with_progress "$url" "$target/$app" || {
rm -rf "$tmpd"
return 250
}
chmod +x "$target/$app"
;;
*)
msg_error "Unknown mode: $mode"
rm -rf "$tmpd"
return 65
;;
esac
echo "$version" >"$vfile"
ensure_usr_local_bin_persist
rm -rf "$tmpd"
msg_ok "Deployed $app ($version) → $target"
}
# ------------------------------
# yq (mikefarah) – Alpine
# ------------------------------
setup_yq() {
# prefer apk, unless FORCE_GH=1
if [ "${FORCE_GH:-0}" != "1" ] && apk info -e yq >/dev/null 2>&1; then
msg_info "Updating yq via apk"
apk add --no-cache --upgrade yq >/dev/null 2>&1 || true
msg_ok "yq ready ($(yq --version 2>/dev/null))"
return 0
fi
need_tool curl || return 127
local arch bin url tmp
case "$(uname -m)" in
x86_64) arch="amd64" ;;
aarch64) arch="arm64" ;;
*)
msg_error "Unsupported arch for yq: $(uname -m)"
return 238
;;
esac
url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}"
tmp="$(mktemp)"
download_with_progress "$url" "$tmp" || return 250
/usr/bin/install -m 0755 "$tmp" /usr/local/bin/yq
rm -f "$tmp"
msg_ok "Setup yq ($(yq --version 2>/dev/null))"
}
# ------------------------------
# Adminer – Alpine
# ------------------------------
setup_adminer() {
need_tool curl || return 127
msg_info "Setup Adminer (Alpine)"
mkdir -p /var/www/localhost/htdocs/adminer
curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \
-o /var/www/localhost/htdocs/adminer/index.php || {
msg_error "Adminer download failed"
return 250
}
msg_ok "Adminer at /adminer (served by your webserver)"
}
# ------------------------------
# uv – Alpine (musl tarball)
# optional: PYTHON_VERSION="3.12"
# ------------------------------
setup_uv() {
need_tool curl tar || return 127
local UV_BIN="/usr/local/bin/uv"
local arch tarball url tmpd ver installed
case "$(uname -m)" in
x86_64) arch="x86_64-unknown-linux-musl" ;;
aarch64) arch="aarch64-unknown-linux-musl" ;;
*)
msg_error "Unsupported arch for uv: $(uname -m)"
return 238
;;
esac
ver="$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | jq -r '.tag_name' 2>/dev/null)"
ver="${ver#v}"
[ -z "$ver" ] && {
msg_error "uv: cannot determine latest version"
return 250
}
if has "$UV_BIN"; then
installed="$($UV_BIN -V 2>/dev/null | awk '{print $2}')"
[ "$installed" = "$ver" ] && {
msg_ok "uv $ver already installed"
return 0
}
msg_info "Updating uv $installed → $ver"
else
msg_info "Setup uv $ver"
fi
tmpd="$(mktemp -d)" || return 252
tarball="uv-${arch}.tar.gz"
url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}"
download_with_progress "$url" "$tmpd/uv.tar.gz" || {
rm -rf "$tmpd"
return 250
}
tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || {
msg_error "uv: extract failed"
rm -rf "$tmpd"
return 251
}
# tar contains ./uv
if [ -x "$tmpd/uv" ]; then
/usr/bin/install -m 0755 "$tmpd/uv" "$UV_BIN"
else
# fallback: in subfolder
/usr/bin/install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || {
msg_error "uv binary not found in tar"
rm -rf "$tmpd"
return 252
}
fi
rm -rf "$tmpd"
ensure_usr_local_bin_persist
msg_ok "Setup uv $ver"
if [ -n "${PYTHON_VERSION:-}" ]; then
local match
match="$(uv python list --only-downloads 2>/dev/null | awk -v maj="$PYTHON_VERSION" '
$0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)"
[ -z "$match" ] && {
msg_error "No matching Python for $PYTHON_VERSION"
return 250
}
if ! uv python list | grep -q "cpython-${match}-linux"; then
msg_info "Installing Python $match via uv"
uv python install "$match" || {
msg_error "uv python install failed"
return 150
}
msg_ok "Python $match installed (uv)"
fi
fi
}
# ------------------------------
# Java – Alpine (OpenJDK)
# JAVA_VERSION: 17|21 (Default 21)
# ------------------------------
setup_java() {
local JAVA_VERSION="${JAVA_VERSION:-21}" pkg
case "$JAVA_VERSION" in
17) pkg="openjdk17-jdk" ;;
21 | *) pkg="openjdk21-jdk" ;;
esac
msg_info "Setup Java (OpenJDK $JAVA_VERSION)"
apk add --no-cache "$pkg" >/dev/null 2>&1 || {
msg_error "apk add $pkg failed"
return 100
}
# set JAVA_HOME
local prof="/etc/profile.d/20-java.sh"
if [ ! -f "$prof" ]; then
echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(command -v java))))' >"$prof"
echo 'case ":$PATH:" in *:$JAVA_HOME/bin:*) ;; *) export PATH="$JAVA_HOME/bin:$PATH";; esac' >>"$prof"
chmod +x "$prof"
fi
msg_ok "Java ready: $(java -version 2>&1 | head -n1)"
}
# ------------------------------
# Go – Alpine (apk prefers, else tarball)
# ------------------------------
setup_go() {
if [ -z "${GO_VERSION:-}" ]; then
msg_info "Setup Go (apk)"
apk add --no-cache go >/dev/null 2>&1 || {
msg_error "apk add go failed"
return 100
}
msg_ok "Go ready: $(go version 2>/dev/null)"
return 0
fi
need_tool curl tar || return 127
local ARCH TARBALL URL TMP
case "$(uname -m)" in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
*)
msg_error "Unsupported arch for Go: $(uname -m)"
return 238
;;
esac
TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz"
URL="https://go.dev/dl/${TARBALL}"
msg_info "Setup Go $GO_VERSION (tarball)"
TMP="$(mktemp)"
download_with_progress "$URL" "$TMP" || return 250
rm -rf /usr/local/go
tar -C /usr/local -xzf "$TMP" || {
msg_error "extract go failed"
rm -f "$TMP"
return 251
}
rm -f "$TMP"
ln -sf /usr/local/go/bin/go /usr/local/bin/go
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
ensure_usr_local_bin_persist
msg_ok "Go ready: $(go version 2>/dev/null)"
}
# ------------------------------
# Composer – Alpine
# uses php83-cli + openssl + phar
# ------------------------------
setup_composer() {
local COMPOSER_BIN="/usr/local/bin/composer"
if ! has php; then
# prefers php83
msg_info "Installing PHP CLI for Composer"
apk add --no-cache php83-cli php83-openssl php83-phar php83-iconv >/dev/null 2>&1 || {
# Fallback to generic php if 83 not available
apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || {
msg_error "Failed to install php-cli for composer"
return 100
}
}
msg_ok "PHP CLI ready: $(php -v | head -n1)"
fi
if [ -x "$COMPOSER_BIN" ]; then
msg_info "Updating Composer"
else
msg_info "Setup Composer"
fi
need_tool curl || return 127
curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || {
msg_error "composer installer download failed"
return 250
}
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || {
msg_error "composer install failed"
return 150
}
rm -f /tmp/composer-setup.php
ensure_usr_local_bin_persist
msg_ok "Composer ready: $(composer --version 2>/dev/null)"
}
-1224
View File
File diff suppressed because it is too large Load Diff
-7331
View File
File diff suppressed because it is too large Load Diff
-708
View File
@@ -1,708 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: community-scripts ORG
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/branch/main/LICENSE
# Revision: 1
# ==============================================================================
# CLOUD-INIT.FUNC - VM CLOUD-INIT CONFIGURATION LIBRARY
# ==============================================================================
#
# Universal helper library for Cloud-Init configuration in Proxmox VMs.
# Provides functions for:
#
# - Native Proxmox Cloud-Init setup (user, password, network, SSH keys)
# - Interactive configuration dialogs (whiptail)
# - IP address retrieval via qemu-guest-agent
# - Cloud-Init status monitoring and waiting
#
# Usage:
# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/cloud-init.func)
# setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes"
#
# Compatible with: Debian, Ubuntu, and all Cloud-Init enabled distributions
# ==============================================================================
# ==============================================================================
# SECTION 1: CONFIGURATION DEFAULTS
# ==============================================================================
# These can be overridden before sourcing this library
# Disable 'unbound variable' errors for this library (restored at end)
_OLD_SET_STATE=$(set +o | grep -E 'set -(e|u|o)')
set +u
CLOUDINIT_DEFAULT_USER="${CLOUDINIT_DEFAULT_USER:-root}"
CLOUDINIT_DNS_SERVERS="${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}"
CLOUDINIT_SEARCH_DOMAIN="${CLOUDINIT_SEARCH_DOMAIN:-local}"
CLOUDINIT_SSH_KEYS="${CLOUDINIT_SSH_KEYS:-}" # Empty by default - user must explicitly provide keys
# ==============================================================================
# SECTION 2: SSH KEY DISCOVERY AND SELECTION
# ==============================================================================
# ------------------------------------------------------------------------------
# _ci_ssh_extract_keys_from_file - Extracts valid SSH public keys from a file
# ------------------------------------------------------------------------------
function _ci_ssh_extract_keys_from_file() {
local file="$1"
[[ -f "$file" && -r "$file" ]] || return 0
grep -E '^(ssh-(rsa|ed25519|dss|ecdsa)|ecdsa-sha2-)' "$file" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
# _ci_ssh_discover_files - Scans standard paths for SSH keys
# ------------------------------------------------------------------------------
function _ci_ssh_discover_files() {
local -a cand=()
shopt -s nullglob
cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2)
cand+=(/root/.ssh/*.pub)
cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
shopt -u nullglob
printf '%s\0' "${cand[@]}"
}
# ------------------------------------------------------------------------------
# _ci_ssh_build_choices - Builds whiptail checklist from SSH key files
#
# Sets: CI_SSH_CHOICES (array), CI_SSH_COUNT (int), CI_SSH_MAPFILE (path)
# ------------------------------------------------------------------------------
function _ci_ssh_build_choices() {
local -a files=("$@")
CI_SSH_CHOICES=()
CI_SSH_COUNT=0
CI_SSH_MAPFILE="$(mktemp)"
local id key typ fp cmt base
for f in "${files[@]}"; do
[[ -f "$f" && -r "$f" ]] || continue
base="$(basename -- "$f")"
# Skip known_hosts and private keys
case "$base" in
known_hosts | known_hosts.* | config) continue ;;
id_*) [[ "$f" != *.pub ]] && continue ;;
esac
while IFS= read -r key; do
[[ -n "$key" ]] || continue
typ=""
fp=""
cmt=""
read -r _typ _b64 _cmt <<<"$key"
typ="${_typ:-key}"
cmt="${_cmt:-}"
# Get fingerprint via ssh-keygen if available
if command -v ssh-keygen >/dev/null 2>&1; then
fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')"
fi
# Shorten long comments
[[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..."
CI_SSH_COUNT=$((CI_SSH_COUNT + 1))
id="K${CI_SSH_COUNT}"
echo "${id}|${key}" >>"$CI_SSH_MAPFILE"
CI_SSH_CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF")
done < <(_ci_ssh_extract_keys_from_file "$f")
done
}
# ------------------------------------------------------------------------------
# configure_cloudinit_ssh_keys - Interactive SSH key selection for Cloud-Init
#
# Usage: configure_cloudinit_ssh_keys
# Sets: CLOUDINIT_SSH_KEYS (path to temporary file with selected keys)
# ------------------------------------------------------------------------------
function configure_cloudinit_ssh_keys() {
local backtitle="Proxmox VE Helper Scripts"
local ssh_key_mode
# Create temp file for selected keys
CLOUDINIT_SSH_KEYS_TEMP="$(mktemp)"
: >"$CLOUDINIT_SSH_KEYS_TEMP"
# Discover keys and build choices
IFS=$'\0' read -r -d '' -a _def_files < <(_ci_ssh_discover_files && printf '\0')
_ci_ssh_build_choices "${_def_files[@]}"
local default_key_count="$CI_SSH_COUNT"
if [[ "$default_key_count" -gt 0 ]]; then
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"Provision SSH keys for Cloud-Init VM:" 14 72 4 \
"found" "Select from detected keys (${default_key_count})" \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
else
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
"No host keys detected. Choose:" 12 72 3 \
"manual" "Paste a single public key" \
"folder" "Scan another folder (path or glob)" \
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
fi
case "$ssh_key_mode" in
found)
# Show checklist with individual keys
local selection
selection=$(whiptail --backtitle "$backtitle" --title "SELECT SSH KEYS" \
--checklist "Select one or more keys to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) selected${CL}"
;;
manual)
local pubkey
pubkey=$(whiptail --backtitle "$backtitle" --title "PASTE SSH PUBLIC KEY" \
--inputbox "Paste your SSH public key (ssh-rsa, ssh-ed25519, etc.):" 10 76 3>&1 1>&2 2>&3) || return 1
if [[ -n "$pubkey" ]]; then
echo "$pubkey" >"$CLOUDINIT_SSH_KEYS_TEMP"
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}1 key added manually${CL}"
else
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (empty input)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
fi
;;
folder)
local glob_path
glob_path=$(whiptail --backtitle "$backtitle" --title "SCAN FOLDER/GLOB" \
--inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub):" 10 72 3>&1 1>&2 2>&3) || return 1
if [[ -n "$glob_path" ]]; then
shopt -s nullglob
local -a _scan_files=($glob_path)
shopt -u nullglob
if [[ "${#_scan_files[@]}" -gt 0 ]]; then
_ci_ssh_build_choices "${_scan_files[@]}"
if [[ "$CI_SSH_COUNT" -gt 0 ]]; then
local folder_selection
folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \
--checklist "Select key(s) to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
for tag in $folder_selection; do
tag="${tag%\"}"
tag="${tag#\"}"
local line
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
done
local imported
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) from folder${CL}"
else
whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60
fi
else
whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60
fi
fi
;;
none | *)
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (password auth only)${CL}"
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
return 0
;;
esac
# Cleanup mapfile
rm -f "$CI_SSH_MAPFILE" 2>/dev/null
# Set the variable for setup_cloud_init to use
if [[ -s "$CLOUDINIT_SSH_KEYS_TEMP" ]]; then
CLOUDINIT_SSH_KEYS="$CLOUDINIT_SSH_KEYS_TEMP"
else
CLOUDINIT_SSH_KEYS=""
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
fi
return 0
}
# ==============================================================================
# SECTION 3: HELPER FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# _ci_msg - Internal message helper with fallback
# ------------------------------------------------------------------------------
function _ci_msg_info() { msg_info "$1" 2>/dev/null || echo "[INFO] $1"; }
function _ci_msg_ok() { msg_ok "$1" 2>/dev/null || echo "[OK] $1"; }
function _ci_msg_warn() { msg_warn "$1" 2>/dev/null || echo "[WARN] $1"; }
function _ci_msg_error() { msg_error "$1" 2>/dev/null || echo "[ERROR] $1"; }
# ------------------------------------------------------------------------------
# validate_ip_cidr - Validate IP address in CIDR format
# Usage: validate_ip_cidr "192.168.1.100/24" && echo "Valid"
# Returns: 0 if valid, 1 if invalid
# ------------------------------------------------------------------------------
function validate_ip_cidr() {
local ip_cidr="$1"
# Match: 0-255.0-255.0-255.0-255/0-32
if [[ "$ip_cidr" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
# Validate each octet is 0-255
local ip="${ip_cidr%/*}"
IFS='.' read -ra octets <<<"$ip"
for octet in "${octets[@]}"; do
((octet > 255)) && return 1
done
return 0
fi
return 1
}
# ------------------------------------------------------------------------------
# validate_ip - Validate plain IP address (no CIDR)
# Usage: validate_ip "192.168.1.1" && echo "Valid"
# ------------------------------------------------------------------------------
function validate_ip() {
local ip="$1"
if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
IFS='.' read -ra octets <<<"$ip"
for octet in "${octets[@]}"; do
((octet > 255)) && return 1
done
return 0
fi
return 1
}
# ==============================================================================
# SECTION 3: MAIN CLOUD-INIT FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# setup_cloud_init - Configures Proxmox Native Cloud-Init
# ------------------------------------------------------------------------------
# Parameters:
# $1 - VMID (required)
# $2 - Storage name (required)
# $3 - Hostname (optional, default: vm-<vmid>)
# $4 - Enable Cloud-Init (yes/no, default: no)
# $5 - User (optional, default: root)
# $6 - Network mode (dhcp/static, default: dhcp)
# $7 - Static IP (optional, format: 192.168.1.100/24)
# $8 - Gateway (optional)
# $9 - Nameservers (optional, default: 1.1.1.1 8.8.8.8)
#
# Returns: 0 on success, 1 on failure
# Exports: CLOUDINIT_USER, CLOUDINIT_PASSWORD, CLOUDINIT_CRED_FILE
# ==============================================================================
function setup_cloud_init() {
local vmid="$1"
local storage="$2"
local hostname="${3:-vm-${vmid}}"
local enable="${4:-no}"
local ciuser="${5:-$CLOUDINIT_DEFAULT_USER}"
local network_mode="${6:-dhcp}"
local static_ip="${7:-}"
local gateway="${8:-}"
local nameservers="${9:-$CLOUDINIT_DNS_SERVERS}"
# Skip if not enabled
if [ "$enable" != "yes" ]; then
return 0
fi
# Validate static IP if provided
if [ "$network_mode" = "static" ]; then
if [ -n "$static_ip" ] && ! validate_ip_cidr "$static_ip"; then
_ci_msg_error "Invalid static IP format: $static_ip (expected: x.x.x.x/xx)"
return 65
fi
if [ -n "$gateway" ] && ! validate_ip "$gateway"; then
_ci_msg_error "Invalid gateway IP format: $gateway"
return 65
fi
fi
_ci_msg_info "Configuring Cloud-Init"
# Create Cloud-Init drive (try ide2 first, then scsi1 as fallback)
if ! qm set "$vmid" --ide2 "${storage}:cloudinit" >/dev/null 2>&1; then
qm set "$vmid" --scsi1 "${storage}:cloudinit" >/dev/null 2>&1
fi
# Set user
qm set "$vmid" --ciuser "$ciuser" >/dev/null
# Generate and set secure random password
local cipassword=$(openssl rand -base64 16)
qm set "$vmid" --cipassword "$cipassword" >/dev/null
# Add SSH keys only if explicitly provided (not auto-imported from host)
if [ -n "${CLOUDINIT_SSH_KEYS:-}" ] && [ -f "$CLOUDINIT_SSH_KEYS" ]; then
qm set "$vmid" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null 2>&1 || true
_ci_msg_info "SSH keys imported from: $CLOUDINIT_SSH_KEYS"
fi
# Configure network
if [ "$network_mode" = "static" ] && [ -n "$static_ip" ] && [ -n "$gateway" ]; then
qm set "$vmid" --ipconfig0 "ip=${static_ip},gw=${gateway}" >/dev/null
else
qm set "$vmid" --ipconfig0 "ip=dhcp" >/dev/null
fi
# Set DNS servers
qm set "$vmid" --nameserver "$nameservers" >/dev/null
# Set search domain
qm set "$vmid" --searchdomain "$CLOUDINIT_SEARCH_DOMAIN" >/dev/null
# Enable package upgrades on first boot (if supported by Proxmox version)
qm set "$vmid" --ciupgrade 1 >/dev/null 2>&1 || true
# Save credentials to file (with restrictive permissions)
local cred_file="/tmp/${hostname}-${vmid}-cloud-init-credentials.txt"
umask 077
cat >"$cred_file" <<EOF
╔══════════════════════════════════════════════════════════════════╗
║ ⚠️ SECURITY WARNING: DELETE THIS FILE AFTER NOTING CREDENTIALS ║
╚══════════════════════════════════════════════════════════════════╝
Cloud-Init Credentials
────────────────────────────────────────
VM ID: ${vmid}
Hostname: ${hostname}
Created: $(date)
Username: ${ciuser}
Password: ${cipassword}
Network: ${network_mode}$([ "$network_mode" = "static" ] && echo " (IP: ${static_ip}, GW: ${gateway})" || echo " (DHCP)")
DNS: ${nameservers}
────────────────────────────────────────
SSH Access (if keys configured):
ssh ${ciuser}@<vm-ip>
Proxmox UI Configuration:
VM ${vmid} > Cloud-Init > Edit
- User, Password, SSH Keys
- Network (IP Config)
- DNS, Search Domain
────────────────────────────────────────
🗑️ To delete this file:
rm -f ${cred_file}
────────────────────────────────────────
EOF
chmod 600 "$cred_file"
_ci_msg_ok "Cloud-Init configured (User: ${ciuser})"
# Export for use in calling script (DO NOT display password here - will be shown in summary)
export CLOUDINIT_USER="$ciuser"
export CLOUDINIT_PASSWORD="$cipassword"
export CLOUDINIT_CRED_FILE="$cred_file"
return 0
}
# ==============================================================================
# SECTION 4: INTERACTIVE CONFIGURATION
# ==============================================================================
# ------------------------------------------------------------------------------
# configure_cloud_init_interactive - Whiptail dialog for Cloud-Init setup
# ------------------------------------------------------------------------------
# Prompts user for Cloud-Init configuration choices
# Returns configuration via exported variables:
# - CLOUDINIT_ENABLE (yes/no)
# - CLOUDINIT_USER
# - CLOUDINIT_NETWORK_MODE (dhcp/static)
# - CLOUDINIT_IP (if static)
# - CLOUDINIT_GW (if static)
# - CLOUDINIT_DNS
# ------------------------------------------------------------------------------
function configure_cloud_init_interactive() {
local default_user="${1:-root}"
# Check if whiptail is available
if ! command -v whiptail >/dev/null 2>&1; then
echo "Warning: whiptail not available, skipping interactive configuration"
export CLOUDINIT_ENABLE="no"
return 127
fi
# Ask if user wants to enable Cloud-Init
if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \
--yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n• User accounts and passwords\n• SSH keys\n• Network settings (DHCP/Static)\n• DNS configuration\n\nYou can also configure these settings later in Proxmox UI." 16 68); then
export CLOUDINIT_ENABLE="no"
return 0
fi
export CLOUDINIT_ENABLE="yes"
# Username
if CLOUDINIT_USER=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
"Cloud-Init Username" 8 58 "$default_user" --title "USERNAME" 3>&1 1>&2 2>&3); then
export CLOUDINIT_USER="${CLOUDINIT_USER:-$default_user}"
else
export CLOUDINIT_USER="$default_user"
fi
# Network configuration
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK MODE" \
--yesno "Use DHCP for network configuration?\n\nSelect 'No' for static IP configuration." 10 58); then
export CLOUDINIT_NETWORK_MODE="dhcp"
else
export CLOUDINIT_NETWORK_MODE="static"
# Static IP with validation
while true; do
if CLOUDINIT_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
"Static IP Address (CIDR format)\nExample: 192.168.1.100/24" 9 58 "" --title "IP ADDRESS" 3>&1 1>&2 2>&3); then
if validate_ip_cidr "$CLOUDINIT_IP"; then
export CLOUDINIT_IP
break
else
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID IP" \
--msgbox "Invalid IP format: $CLOUDINIT_IP\n\nPlease use CIDR format: x.x.x.x/xx\nExample: 192.168.1.100/24" 10 50
fi
else
_ci_msg_warn "Static IP required, falling back to DHCP"
export CLOUDINIT_NETWORK_MODE="dhcp"
break
fi
done
# Gateway with validation
if [ "$CLOUDINIT_NETWORK_MODE" = "static" ]; then
while true; do
if CLOUDINIT_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
"Gateway IP Address\nExample: 192.168.1.1" 8 58 "" --title "GATEWAY" 3>&1 1>&2 2>&3); then
if validate_ip "$CLOUDINIT_GW"; then
export CLOUDINIT_GW
break
else
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID GATEWAY" \
--msgbox "Invalid gateway format: $CLOUDINIT_GW\n\nPlease use format: x.x.x.x\nExample: 192.168.1.1" 10 50
fi
else
_ci_msg_warn "Gateway required, falling back to DHCP"
export CLOUDINIT_NETWORK_MODE="dhcp"
break
fi
done
fi
fi
# DNS Servers
if CLOUDINIT_DNS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
"DNS Servers (space-separated)" 8 58 "1.1.1.1 8.8.8.8" --title "DNS SERVERS" 3>&1 1>&2 2>&3); then
export CLOUDINIT_DNS="${CLOUDINIT_DNS:-1.1.1.1 8.8.8.8}"
else
export CLOUDINIT_DNS="1.1.1.1 8.8.8.8"
fi
return 0
}
# ==============================================================================
# SECTION 5: UTILITY FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# display_cloud_init_info - Show Cloud-Init summary after setup
# ------------------------------------------------------------------------------
function display_cloud_init_info() {
local vmid="$1"
local hostname="${2:-}"
if [ -n "$CLOUDINIT_CRED_FILE" ] && [ -f "$CLOUDINIT_CRED_FILE" ]; then
if [ -n "${INFO:-}" ]; then
echo -e "\n${INFO}${BOLD:-}${GN:-} Cloud-Init Configuration:${CL:-}"
echo -e "${TAB:- }${DGN:-}User: ${BGN:-}${CLOUDINIT_USER:-root}${CL:-}"
echo -e "${TAB:- }${DGN:-}Password: ${BGN:-}${CLOUDINIT_PASSWORD}${CL:-}"
echo -e "${TAB:- }${DGN:-}Credentials: ${BL:-}${CLOUDINIT_CRED_FILE}${CL:-}"
echo -e "${TAB:- }${RD:-}⚠️ Delete credentials file after noting password!${CL:-}"
echo -e "${TAB:- }${YW:-}💡 Configure in Proxmox UI: VM ${vmid} > Cloud-Init${CL:-}"
else
echo ""
echo "[INFO] Cloud-Init Configuration:"
echo " User: ${CLOUDINIT_USER:-root}"
echo " Password: ${CLOUDINIT_PASSWORD}"
echo " Credentials: ${CLOUDINIT_CRED_FILE}"
echo " ⚠️ Delete credentials file after noting password!"
echo " Configure in Proxmox UI: VM ${vmid} > Cloud-Init"
fi
fi
}
# ------------------------------------------------------------------------------
# cleanup_cloud_init_credentials - Remove credentials file
# ------------------------------------------------------------------------------
# Usage: cleanup_cloud_init_credentials
# Call this after user has noted/saved the credentials
# ------------------------------------------------------------------------------
function cleanup_cloud_init_credentials() {
if [ -n "$CLOUDINIT_CRED_FILE" ] && [ -f "$CLOUDINIT_CRED_FILE" ]; then
rm -f "$CLOUDINIT_CRED_FILE"
_ci_msg_ok "Credentials file removed: $CLOUDINIT_CRED_FILE"
unset CLOUDINIT_CRED_FILE
return 0
fi
return 1
}
# ------------------------------------------------------------------------------
# has_cloud_init - Check if VM has Cloud-Init configured
# ------------------------------------------------------------------------------
function has_cloud_init() {
local vmid="$1"
qm config "$vmid" 2>/dev/null | grep -qE "(ide2|scsi1):.*cloudinit"
}
# ------------------------------------------------------------------------------
# regenerate_cloud_init - Regenerate Cloud-Init configuration
# ------------------------------------------------------------------------------
function regenerate_cloud_init() {
local vmid="$1"
if has_cloud_init "$vmid"; then
_ci_msg_info "Regenerating Cloud-Init configuration"
qm cloudinit update "$vmid" >/dev/null 2>&1 || true
_ci_msg_ok "Cloud-Init configuration regenerated"
return 0
else
_ci_msg_warn "VM $vmid does not have Cloud-Init configured"
return 1
fi
}
# ------------------------------------------------------------------------------
# get_vm_ip - Get VM IP address via qemu-guest-agent
# ------------------------------------------------------------------------------
function get_vm_ip() {
local vmid="$1"
local timeout="${2:-30}"
local elapsed=0
while [ $elapsed -lt $timeout ]; do
local vm_ip=$(qm guest cmd "$vmid" network-get-interfaces 2>/dev/null |
jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"' 2>/dev/null | head -1)
if [ -n "$vm_ip" ]; then
echo "$vm_ip"
return 0
fi
sleep 2
elapsed=$((elapsed + 2))
done
return 7
}
# ------------------------------------------------------------------------------
# wait_for_cloud_init - Wait for Cloud-Init to complete (requires SSH access)
# ------------------------------------------------------------------------------
function wait_for_cloud_init() {
local vmid="$1"
local timeout="${2:-300}"
local vm_ip="${3:-}"
# Get IP if not provided
if [ -z "$vm_ip" ]; then
vm_ip=$(get_vm_ip "$vmid" 60)
fi
if [ -z "$vm_ip" ]; then
_ci_msg_warn "Unable to determine VM IP address"
return 7
fi
_ci_msg_info "Waiting for Cloud-Init to complete on ${vm_ip}"
local elapsed=0
while [ $elapsed -lt $timeout ]; do
if timeout 10 ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
"${CLOUDINIT_USER:-root}@${vm_ip}" "cloud-init status --wait" 2>/dev/null; then
_ci_msg_ok "Cloud-Init completed successfully"
return 0
fi
sleep 10
elapsed=$((elapsed + 10))
done
_ci_msg_warn "Cloud-Init did not complete within ${timeout}s"
return 150
}
# ==============================================================================
# SECTION 6: EXPORTS
# ==============================================================================
# Export all functions for use in other scripts
export -f setup_cloud_init 2>/dev/null || true
export -f configure_cloud_init_interactive 2>/dev/null || true
export -f display_cloud_init_info 2>/dev/null || true
export -f cleanup_cloud_init_credentials 2>/dev/null || true
export -f has_cloud_init 2>/dev/null || true
export -f regenerate_cloud_init 2>/dev/null || true
export -f get_vm_ip 2>/dev/null || true
export -f wait_for_cloud_init 2>/dev/null || true
export -f validate_ip_cidr 2>/dev/null || true
export -f validate_ip 2>/dev/null || true
# Restore previous shell options if they were saved
if [ -n "${_OLD_SET_STATE:-}" ]; then
eval "$_OLD_SET_STATE"
fi
# ==============================================================================
# SECTION 7: EXAMPLES & DOCUMENTATION
# ==============================================================================
: <<'EXAMPLES'
# Example 1: Simple DHCP setup (most common)
setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes"
# Example 2: Static IP setup
setup_cloud_init "$VMID" "$STORAGE" "myserver" "yes" "root" "static" "192.168.1.100/24" "192.168.1.1"
# Example 3: Interactive configuration in advanced_settings()
configure_cloud_init_interactive "admin"
if [ "$CLOUDINIT_ENABLE" = "yes" ]; then
setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" "$CLOUDINIT_USER" \
"$CLOUDINIT_NETWORK_MODE" "$CLOUDINIT_IP" "$CLOUDINIT_GW" "$CLOUDINIT_DNS"
fi
# Example 4: Display info after VM creation
display_cloud_init_info "$VMID" "$HN"
# Example 5: Check if VM has Cloud-Init
if has_cloud_init "$VMID"; then
echo "Cloud-Init is configured"
fi
# Example 6: Wait for Cloud-Init to complete after VM start
if [ "$START_VM" = "yes" ]; then
qm start "$VMID"
sleep 30
wait_for_cloud_init "$VMID" 300
fi
# Example 7: Cleanup credentials file after user has noted password
display_cloud_init_info "$VMID" "$HN"
read -p "Have you saved the credentials? (y/N): " -r
[[ $REPLY =~ ^[Yy]$ ]] && cleanup_cloud_init_credentials
# Example 8: Validate IP before using
if validate_ip_cidr "192.168.1.100/24"; then
echo "Valid IP/CIDR"
fi
EXAMPLES
-1968
View File
File diff suppressed because it is too large Load Diff
-733
View File
@@ -1,733 +0,0 @@
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# ERROR HANDLER - ERROR & SIGNAL MANAGEMENT
# ------------------------------------------------------------------------------
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# ------------------------------------------------------------------------------
#
# Provides error handling and signal management for all scripts.
#
# TELEMETRY CONTRACT:
# - HOST context: this file reports terminal statuses via post_update_to_api
# (full metadata + focused error trace).
# - CONTAINER context: this file NEVER talks to the telemetry API. It writes
# two local artifacts that the host picks up after lxc-attach returns:
# /root/.install-<SESSION_ID>.failed → exit code (flag file)
# /root/.install-<SESSION_ID>.log.errinfo → structured error capture
# This guarantees the server only ever sees ONE terminal event per
# execution - the host's complete one.
#
# Usage:
# source <(curl -fsSL .../error_handler.func)
# catch_errors
#
# ------------------------------------------------------------------------------
# ==============================================================================
# SECTION 1: EXIT CODE EXPLANATIONS (fallback)
# ==============================================================================
# ------------------------------------------------------------------------------
# explain_exit_code()
#
# - Canonical version lives in api.func (sourced before this file on the host)
# - This fallback covers the container context where api.func is not sourced
# ------------------------------------------------------------------------------
if ! declare -f explain_exit_code &>/dev/null; then
explain_exit_code() {
local code="$1"
case "$code" in
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)" ;;
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" ;;
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)" ;;
103) echo "Validation: Shell is not Bash" ;;
104) echo "Validation: Not running as root (or invoked via sudo)" ;;
105) echo "Validation: Proxmox VE version not supported" ;;
106) echo "Validation: Unsupported architecture (requires amd64 or arm64)" ;;
107) echo "Validation: Kernel key parameters unreadable" ;;
108) echo "Validation: Kernel key limits exceeded" ;;
109) echo "Proxmox: No available container ID after max attempts" ;;
110) echo "Proxmox: Failed to apply default.vars" ;;
111) echo "Proxmox: App defaults file not available" ;;
112) echo "Proxmox: Invalid install menu option" ;;
113) echo "LXC: Under-provisioned — user aborted update" ;;
114) echo "LXC: Storage too low — user aborted update" ;;
115) echo "Download: install.func download failed or incomplete" ;;
116) echo "Proxmox: Default bridge vmbr0 not found" ;;
117) echo "LXC: Container did not reach running state" ;;
118) echo "LXC: No IP assigned to container after timeout" ;;
119) echo "Proxmox: No valid storage for rootdir content" ;;
120) echo "Proxmox: No valid storage for vztmpl content" ;;
121) echo "LXC: Container network not ready (no IP after retries)" ;;
122) echo "LXC: No internet connectivity — user declined to continue" ;;
123) echo "LXC: Local IP detection failed" ;;
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)" ;;
150) echo "Systemd: Service failed to start" ;;
151) echo "Systemd: Service unit not found" ;;
152) echo "Permission denied (EACCES)" ;;
153) echo "Build/compile failed (make/gcc/cmake)" ;;
154) echo "Node.js: Native addon build failed (node-gyp)" ;;
160) echo "Python: Virtualenv / uv environment missing or broken" ;;
161) echo "Python: Dependency resolution failed" ;;
162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;;
170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;;
171) echo "PostgreSQL: Authentication failed (bad user/password)" ;;
172) echo "PostgreSQL: Database does not exist" ;;
173) echo "PostgreSQL: Fatal error in query / syntax" ;;
180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;;
181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;;
182) echo "MySQL/MariaDB: Database does not exist" ;;
183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;;
190) echo "MongoDB: Connection failed (server not running)" ;;
191) echo "MongoDB: Authentication failed (bad user/password)" ;;
192) echo "MongoDB: Database not found" ;;
193) echo "MongoDB: Fatal query error" ;;
200) echo "Proxmox: Failed to create lock file" ;;
203) echo "Proxmox: Missing CTID variable" ;;
204) echo "Proxmox: Missing PCT_OSTYPE variable" ;;
205) echo "Proxmox: Invalid CTID (<100)" ;;
206) echo "Proxmox: CTID already in use" ;;
207) echo "Proxmox: Password contains unescaped special characters" ;;
208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;;
209) echo "Proxmox: Container creation failed" ;;
210) echo "Proxmox: Cluster not quorate" ;;
211) echo "Proxmox: Timeout waiting for template lock" ;;
212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;;
213) echo "Proxmox: Storage type does not support 'rootdir' content" ;;
214) echo "Proxmox: Not enough storage space" ;;
215) echo "Proxmox: Container created but not listed (ghost state)" ;;
216) echo "Proxmox: RootFS entry missing in config" ;;
217) echo "Proxmox: Storage not accessible" ;;
218) echo "Proxmox: Template file corrupted or incomplete" ;;
219) echo "Proxmox: CephFS does not support containers - use RBD" ;;
220) echo "Proxmox: Unable to resolve template path" ;;
221) echo "Proxmox: Template file not readable" ;;
222) echo "Proxmox: Template download failed" ;;
223) echo "Proxmox: Template not available after download" ;;
224) echo "Proxmox: PBS storage is for backups only" ;;
225) echo "Proxmox: No template available for OS/Version" ;;
226) echo "Proxmox: VM disk import or post-creation setup failed" ;;
231) echo "Proxmox: LXC stack upgrade failed" ;;
232) echo "Tools: Wrong execution environment (run on PVE host, not inside LXC)" ;;
233) echo "Tools: Application not installed (update prerequisite missing)" ;;
234) echo "Tools: No LXC containers found or available" ;;
235) echo "Tools: Backup or restore operation failed" ;;
236) echo "Tools: Required hardware not detected" ;;
237) echo "Tools: Dependency package installation failed" ;;
238) echo "Tools: OS or distribution not supported for this addon" ;;
239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;;
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
245) echo "Node.js: Invalid command-line option" ;;
246) echo "Node.js: Internal JavaScript Parse Error" ;;
247) echo "Node.js: Fatal internal error" ;;
248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
249) echo "npm/pnpm/yarn: Unknown fatal error" ;;
250) echo "App: Download failed or version not determined" ;;
251) echo "App: File extraction failed (corrupt or incomplete archive)" ;;
252) echo "App: Required file or resource not found" ;;
253) echo "App: Data migration required — update aborted" ;;
254) echo "App: User declined prompt or input timed out" ;;
255) echo "DPKG: Fatal internal error" ;;
*) echo "Unknown error" ;;
esac
}
fi
# ==============================================================================
# SECTION 2: CONTEXT DETECTION & CONTAINER ARTIFACTS
# ==============================================================================
# ------------------------------------------------------------------------------
# _is_container_context()
#
# - Returns 0 (true) when running INSIDE the LXC container being installed
# - TELEMETRY_CONTEXT can override the heuristic ("host" / "container");
# install.func sets TELEMETRY_CONTEXT=container during bootstrap
# ------------------------------------------------------------------------------
_is_container_context() {
case "${TELEMETRY_CONTEXT:-}" in
container) return 0 ;;
host) return 1 ;;
esac
# Proxmox/Incus tooling exists only on the host
command -v pveversion &>/dev/null && return 1
command -v pct &>/dev/null && return 1
command -v incus &>/dev/null && return 1
# systemd-detect-virt reports lxc inside containers
if command -v systemd-detect-virt &>/dev/null; then
case "$(systemd-detect-virt -c 2>/dev/null)" in
lxc | lxc-libvirt | openvz) return 0 ;;
esac
fi
# PCT_OSTYPE is exported into the install environment by the host
[[ -n "${PCT_OSTYPE:-}" ]] && return 0
return 1
}
# ------------------------------------------------------------------------------
# _container_write_failure()
#
# - Writes the failure artifacts inside the container for the host to pick up:
# * flag file with the exit code
# * .errinfo capture (if silent() has not already written a better one)
# * copy of the install log
# - This REPLACES any direct telemetry send from the container
# - Arguments: $1 = exit_code, $2 = command (optional), $3 = line (optional)
# ------------------------------------------------------------------------------
_container_write_failure() {
local exit_code="${1:-1}"
local command="${2:-}"
local line="${3:-}"
local sid="${SESSION_ID:-error}"
# Flag file with exit code (host reads this after lxc-attach returns)
echo "$exit_code" >"/root/.install-${sid}.failed" 2>/dev/null || true
# Keep the install log where the host expects it
if [[ -n "${INSTALL_LOG:-}" && -f "${INSTALL_LOG}" && "${INSTALL_LOG}" != "/root/.install-${sid}.log" ]]; then
cp "${INSTALL_LOG}" "/root/.install-${sid}.log" 2>/dev/null || true
fi
# Structured error capture (skip if silent() already wrote the exact
# output segment of the failing command - that one is always better).
# Self-contained: api.func is NOT sourced inside containers.
local errinfo="${INSTALL_LOG:-/root/.install-${sid}.log}.errinfo"
if [[ ! -s "$errinfo" ]]; then
local flat_cmd
flat_cmd=$(printf '%s' "${command:-unknown}" | tr '\n' ' ' | head -c 300)
{
echo "EXIT_CODE=${exit_code}"
echo "LINE=${line:-0}"
echo "COMMAND=${flat_cmd}"
echo "--- OUTPUT ---"
if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then
tail -n 80 "${INSTALL_LOG}" 2>/dev/null |
sed 's/\r$//' |
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' |
grep -avE '^(Get:|Hit:|Ign:|Fetched |Reading package lists|Reading state information|Building dependency tree|Selecting previously|Preparing to unpack|Unpacking |Processing triggers for|\(Reading database|[0-9]+%[[:space:]]*\[)' |
grep -avE '^[[:space:]]*$' |
tail -n 60 | head -c 10240
fi
} >"$errinfo" 2>/dev/null || true
fi
}
# ==============================================================================
# SECTION 3: ERROR HANDLER
# ==============================================================================
# ------------------------------------------------------------------------------
# error_handler()
#
# - Main error handler triggered by ERR trap
# - Displays error message with line number, exit code, explanation, command
# - Shows last 20 lines of the active log
# - Emits actionable hints for common failure patterns (OOM, APT, network...)
# - HOST: reports "failed" to telemetry (full payload via api.func)
# - CONTAINER: writes failure artifacts, sends nothing
# - Exits with original exit code
# ------------------------------------------------------------------------------
error_handler() {
local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}}
local line_number=${BASH_LINENO[0]:-unknown}
command="${command//\$STD/}"
# If error originated from silent(), use its captured metadata
if [[ -n "${_SILENT_FAILED_RC:-}" ]]; then
exit_code="$_SILENT_FAILED_RC"
command="$_SILENT_FAILED_CMD"
line_number="$_SILENT_FAILED_LINE"
unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE
fi
if [[ "$exit_code" -eq 0 ]]; then
return 0
fi
# Export the failure location so telemetry can include the "where"
FAILED_COMMAND="$command"
FAILED_LINE="$line_number"
export FAILED_COMMAND FAILED_LINE
# Stop spinner and restore cursor FIRST - before any output
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
printf "\e[?25h"
local explanation
explanation="$(explain_exit_code "$exit_code")"
if [[ "$explanation" == curl:* && ! "$command" =~ (^|[[:space:]])([^[:space:]]*/)?curl([[:space:]]|$) ]]; then
explanation="Command failed with exit status ${exit_code}"
fi
# ── Telemetry / failure artifacts ──
if _is_container_context; then
_container_write_failure "$exit_code" "$command" "$line_number"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
fi
# ── Display ──
if declare -f msg_error >/dev/null 2>&1; then
msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}"
else
echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n"
fi
if [[ -n "${DEBUG_LOGFILE:-}" ]]; then
{
echo "------ ERROR ------"
echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')"
echo "Exit Code : $exit_code ($explanation)"
echo "Line : $line_number"
echo "Command : $command"
echo "-------------------"
} >>"$DEBUG_LOGFILE"
fi
# Get active log file (prefer silent()'s logfile when available)
local active_log=""
if [[ -n "${_SILENT_FAILED_LOG:-}" && -s "${_SILENT_FAILED_LOG}" ]]; then
active_log="$_SILENT_FAILED_LOG"
unset _SILENT_FAILED_LOG
elif declare -f get_active_logfile >/dev/null 2>&1; then
active_log="$(get_active_logfile)"
elif [[ -n "${SILENT_LOGFILE:-}" ]]; then
active_log="$SILENT_LOGFILE"
fi
if [[ -n "$active_log" && ! -s "$active_log" && -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then
active_log="$BUILD_LOG"
fi
# stderr: the trap can fire inside a $(...) capture that would eat this as data
if [[ -n "$active_log" && -s "$active_log" ]]; then
echo -e "\n${TAB}--- Last 20 lines of log ---" >&2
tail -n 20 "$active_log" >&2
echo -e "${TAB}-----------------------------------\n" >&2
fi
# ── Node.js heap OOM detection with actionable guidance ──
local node_oom_detected="false"
local node_build_context="false"
if [[ "$command" =~ (npm|pnpm|yarn|node|vite|turbo) ]]; then
node_build_context="true"
fi
if [[ "$exit_code" == "243" ]]; then
node_oom_detected="true"
elif [[ -n "$active_log" && -s "$active_log" ]]; then
if tail -n 200 "$active_log" 2>/dev/null | grep -Eqi 'Reached heap limit|JavaScript heap out of memory|Allocation failed - JavaScript heap out of memory|FATAL ERROR: Reached heap limit'; then
node_oom_detected="true"
fi
fi
if [[ "$node_oom_detected" == "true" ]] || { [[ "$node_build_context" == "true" ]] && [[ "$exit_code" =~ ^(134|137)$ ]]; }; then
local heap_hint_mb=""
if [[ -n "${NODE_OPTIONS:-}" ]] && [[ "${NODE_OPTIONS}" =~ max-old-space-size=([0-9]+) ]]; then
heap_hint_mb="${BASH_REMATCH[1]}"
elif [[ -n "${var_ram:-}" ]] && [[ "${var_ram}" =~ ^[0-9]+$ ]]; then
heap_hint_mb=$((var_ram * 75 / 100))
else
local mem_kb=""
mem_kb=$(awk '/^MemTotal:/ {print $2; exit}' /proc/meminfo 2>/dev/null || echo "")
if [[ "$mem_kb" =~ ^[0-9]+$ ]]; then
local mem_mb=$((mem_kb / 1024))
heap_hint_mb=$((mem_mb * 75 / 100))
fi
fi
if [[ -z "$heap_hint_mb" ]] || ((heap_hint_mb < 1024)); then
heap_hint_mb=1024
elif ((heap_hint_mb > 12288)); then
heap_hint_mb=12288
fi
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "Possible Node.js heap OOM. Try: export NODE_OPTIONS=\"--max-old-space-size=${heap_hint_mb}\" and rerun the build."
else
echo -e "${YW}Possible Node.js heap OOM. Try: export NODE_OPTIONS=\"--max-old-space-size=${heap_hint_mb}\" and rerun the build.${CL}"
fi
fi
# ── Log-pattern analysis: actionable hints for common failure causes ──
if [[ -n "$active_log" && -s "$active_log" ]]; then
local _log_tail
_log_tail=$(tail -n 60 "$active_log" 2>/dev/null || true)
# 1. APT/dpkg dependency conflict
if echo "$_log_tail" | grep -qE "Depends:|depends on.*but.*not installed|broken packages|unmet dep|dependency problems"; then
local _pg_conflict
_pg_conflict=$(echo "$_log_tail" | grep -oE 'postgresql-[0-9]+ but.*installed' | head -1 || true)
if [[ -n "$_pg_conflict" ]]; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "PostgreSQL version conflict: ${_pg_conflict}"
msg_warn "Hint: A package requires a specific PostgreSQL version that is not installed. Your distribution may have installed a different PG version than expected."
fi
else
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "APT dependency conflict detected. A required package is not available or is the wrong version for this system."
msg_warn "Hint: Run 'apt-get install -f' inside the container or check that all required repositories are configured for your distribution."
fi
fi
# 2. APT/GPG signature verification failure
elif echo "$_log_tail" | grep -qE "sqv|KEYEXPIRED|NO_PUBKEY|key is not certified|signature verification failed|is not signed|Sub-process.*sqv"; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "APT repository signature error detected."
msg_warn "Hint: A repository GPG key may be missing, expired, or the keyring file is not yet present (/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc etc.)."
msg_warn "Hint: Install the 'postgresql-common' package first, or re-add the repository with its correct signing key."
fi
# 3. Network / DNS failure
elif echo "$_log_tail" | grep -qE "Could not resolve|Failed to fetch|Unable to connect|Name or service not known|Network is unreachable|curl.*resolve"; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "Network or DNS failure detected."
msg_warn "Hint: Check the container's network connectivity, DNS settings, and whether any firewall or ad-blocker is intercepting traffic."
fi
# 4. APT lock held by another process
elif echo "$_log_tail" | grep -qE "Could not get lock|dpkg frontend lock|waiting for it to exit|E: Unable to lock"; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "APT or dpkg lock conflict detected."
msg_warn "Hint: Another package manager process may be running. Try 'rm /var/lib/dpkg/lock-frontend && dpkg --configure -a' inside the container."
fi
# 5. Disk space exhaustion
elif echo "$_log_tail" | grep -qE "No space left on device|disk quota exceeded|ENOSPC"; then
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "Disk space exhausted during installation."
msg_warn "Hint: Increase the container's disk size (pct resize <ctid> rootfs +2G) or clean up space first."
fi
fi
fi
# ── Context-specific cleanup ──
if _is_container_context; then
# Container: artifacts already written above; nothing more to do
:
else
# HOST: show log path and offer container cleanup
if [[ -n "$active_log" && -s "$active_log" ]]; then
if declare -f msg_custom >/dev/null 2>&1; then
msg_custom "📋" "${YW}" "Full log: ${active_log}"
else
echo -e "${YW}Full log:${CL} ${BL}${active_log}${CL}"
fi
fi
# Offer to remove container if it exists (build errors after container creation)
if [[ -n "${CTID:-}" ]] && command -v pct &>/dev/null && pct status "$CTID" &>/dev/null; then
echo ""
if declare -f msg_custom >/dev/null 2>&1; then
echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
else
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
fi
local response=""
local read_rc
declare -f reclaim_tty >/dev/null 2>&1 && reclaim_tty
read -t 60 -r response </dev/tty
read_rc=$?
if [[ $read_rc -eq 0 ]]; then
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
echo ""
if declare -f msg_info >/dev/null 2>&1; then
msg_info "Removing container ${CTID}"
else
echo -e "${YW}Removing container ${CTID}${CL}"
fi
pct stop "$CTID" &>/dev/null || true
pct destroy "$CTID" &>/dev/null || true
if declare -f msg_ok >/dev/null 2>&1; then
msg_ok "Container ${CTID} removed"
else
echo -e "${GN}✔${CL} Container ${CTID} removed"
fi
elif [[ "$response" =~ ^[Nn]$ ]]; then
echo ""
if declare -f msg_warn >/dev/null 2>&1; then
msg_warn "Container ${CTID} kept for debugging"
else
echo -e "${YW}Container ${CTID} kept for debugging${CL}"
fi
fi
elif [[ $read_rc -gt 128 ]]; then
# Timeout - auto-remove
echo ""
if declare -f msg_info >/dev/null 2>&1; then
msg_info "No response - removing container ${CTID}"
else
echo -e "${YW}No response - removing container ${CTID}${CL}"
fi
pct stop "$CTID" &>/dev/null || true
pct destroy "$CTID" &>/dev/null || true
if declare -f msg_ok >/dev/null 2>&1; then
msg_ok "Container ${CTID} removed"
else
echo -e "${GN}✔${CL} Container ${CTID} removed"
fi
else
# read itself failed (e.g. broken/closed stdin, I/O error) rather than
# timing out - don't guess and destroy the container on a read we
# couldn't actually capture; keep it since that's the reversible choice.
while read -r -t 0.1 -n 1 _ </dev/tty 2>/dev/null; do :; done
echo ""
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Could not read your response (stdin error) - keeping container ${CTID} for safety."
msg_error "Remove it manually if not needed: pct destroy ${CTID}"
else
echo -e "${YW}Could not read your response (stdin error) - keeping container ${CTID} for safety.${CL}"
echo -e "${YW}Remove it manually if not needed: pct destroy ${CTID}${CL}"
fi
fi
fi
fi
exit "$exit_code"
}
# ==============================================================================
# SECTION 4: TELEMETRY & CLEANUP HELPERS FOR SIGNAL HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# _send_abort_telemetry() (compatibility name)
#
# - HOST: reports via post_update_to_api (signals map to "aborted" there)
# - CONTAINER: writes failure artifacts instead of sending anything
# - Arguments: $1 = exit_code
# ------------------------------------------------------------------------------
_send_abort_telemetry() {
local exit_code="${1:-1}"
if _is_container_context; then
_container_write_failure "$exit_code"
return 0
fi
if declare -f post_update_to_api &>/dev/null; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
fi
return 0
}
# ------------------------------------------------------------------------------
# _stop_container_if_installing()
#
# - Stops the LXC container if we're in the install phase (host only)
# - Prevents orphaned container processes when the host exits due to a signal
# ------------------------------------------------------------------------------
_stop_container_if_installing() {
[[ "${CONTAINER_INSTALLING:-}" == "true" ]] || return 0
[[ -n "${CTID:-}" ]] || return 0
command -v pct &>/dev/null || return 0
pct stop "$CTID" 2>/dev/null || true
}
# ==============================================================================
# SECTION 5: SIGNAL HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# on_exit()
#
# - EXIT trap handler — runs on EVERY script termination
# - CONTAINER: ensures failure artifacts exist on non-zero exit (this also
# covers silent()'s direct `exit $rc`, which bypasses the ERR trap)
# - HOST: catches executions that never sent a final status:
# * non-zero exit → "failed" (signal codes map to "aborted")
# * zero exit with an "installing" record but no final → "aborted"
# (e.g. user cancelled a whiptail dialog, script exited cleanly)
# ------------------------------------------------------------------------------
on_exit() {
local exit_code=$?
if _is_container_context; then
if [[ $exit_code -ne 0 ]]; then
_container_write_failure "$exit_code" "${FAILED_COMMAND:-}" "${FAILED_LINE:-}"
fi
else
if [[ "${POST_UPDATE_DONE:-}" != "true" ]] && declare -f post_update_to_api >/dev/null 2>&1; then
if [[ $exit_code -ne 0 ]]; then
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
elif [[ "${POST_TO_API_DONE:-}" == "true" ]]; then
# Clean exit but no success was ever reported: the user backed out
# somewhere. Report as aborted so the record doesn't stay "installing".
post_update_to_api "aborted" "0" 2>/dev/null || true
fi
fi
# Best-effort log collection on failure
if [[ $exit_code -ne 0 ]] && declare -f ensure_log_on_host >/dev/null 2>&1; then
ensure_log_on_host 2>/dev/null || true
fi
# Stop orphaned container if we're in the install phase
if [[ $exit_code -ne 0 ]]; then
_stop_container_if_installing
fi
fi
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
exit "$exit_code"
}
# ------------------------------------------------------------------------------
# on_interrupt() - SIGINT (Ctrl+C)
# ------------------------------------------------------------------------------
on_interrupt() {
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
printf "\e[?25h" 2>/dev/null || true
if _is_container_context; then
_container_write_failure "130"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "130" 2>/dev/null || true
fi
_stop_container_if_installing
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Interrupted by user (SIGINT)" 2>/dev/null || true
else
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" 2>/dev/null || true
fi
exit 130
}
# ------------------------------------------------------------------------------
# on_terminate() - SIGTERM
# ------------------------------------------------------------------------------
on_terminate() {
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
printf "\e[?25h" 2>/dev/null || true
if _is_container_context; then
_container_write_failure "143"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "143" 2>/dev/null || true
fi
_stop_container_if_installing
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Terminated by signal (SIGTERM)" 2>/dev/null || true
else
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" 2>/dev/null || true
fi
exit 143
}
# ------------------------------------------------------------------------------
# on_hangup() - SIGHUP (SSH disconnect, terminal closed)
# ------------------------------------------------------------------------------
on_hangup() {
if declare -f stop_spinner >/dev/null 2>&1; then
stop_spinner 2>/dev/null || true
fi
if _is_container_context; then
_container_write_failure "129"
elif declare -f post_update_to_api &>/dev/null; then
post_update_to_api "aborted" "129" 2>/dev/null || true
fi
_stop_container_if_installing
exit 129
}
# ==============================================================================
# SECTION 6: INITIALIZATION
# ==============================================================================
# ------------------------------------------------------------------------------
# catch_errors()
#
# - Initializes error handling and signal traps
# - set -Ee -o pipefail (+ set -u when STRICT_UNSET=1)
# - Traps: ERR → error_handler, EXIT → on_exit, INT/TERM/HUP → signal handlers
# - Call this function early in every script
# ------------------------------------------------------------------------------
catch_errors() {
set -Ee -o pipefail
if [ "${STRICT_UNSET:-0}" = "1" ]; then
set -u
fi
trap 'error_handler' ERR
trap on_exit EXIT
trap on_interrupt INT
trap on_terminate TERM
trap on_hangup HUP
}
-552
View File
@@ -1,552 +0,0 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster)
# Co-Author: MickLesk
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# ==============================================================================
# INSTALL.FUNC - CONTAINER INSTALLATION & SETUP
# ==============================================================================
#
# This file provides installation functions executed inside LXC containers
# after creation. Handles:
#
# - Network connectivity verification (IPv4/IPv6)
# - OS updates and package installation
# - DNS resolution checks
# - MOTD and SSH configuration
# - Container customization and auto-login
#
# Usage:
# - Sourced by <app>-install.sh scripts
# - Executes via pct exec inside container
# - Requires internet connectivity
#
# ==============================================================================
# ==============================================================================
# SECTION 1: INITIALIZATION
# ==============================================================================
if ! command -v curl >/dev/null 2>&1; then
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
apt update >/dev/null 2>&1
apt install -y curl >/dev/null 2>&1
fi
# Mark container context BEFORE error handling starts: error_handler/on_exit
# must write local failure artifacts instead of talking to the telemetry API
# (the host is the single telemetry reporter).
export TELEMETRY_CONTEXT="container"
# A freshly booted container may not have DNS up yet. `source <(curl ...)` hides
# curl's exit code, so a failed download would silently source an empty stream.
_bootstrap_die() {
if declare -f msg_error >/dev/null 2>&1; then
msg_error "$1"
else
echo "FATAL: $1" >&2
fi
exit 115
}
_bootstrap_source() {
local url="$1" probe="$2" content
content=$(curl -fsSL --connect-timeout 10 --retry 5 --retry-connrefused --retry-delay 2 "$url") ||
_bootstrap_die "Failed to download ${url##*/}"
source /dev/stdin <<<"$content"
declare -f "$probe" >/dev/null 2>&1 ||
_bootstrap_die "${url##*/} loaded but incomplete (missing ${probe})"
}
_FUNC_BASE="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc"
_bootstrap_source "$_FUNC_BASE/core.func" load_functions
_bootstrap_source "$_FUNC_BASE/error_handler.func" catch_errors
load_functions
catch_errors
# Persist diagnostics setting inside container (exported from build.func)
# so addon scripts running later can find the user's choice
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
mkdir -p /usr/local/community-scripts
echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics
fi
# Get LXC IP address (must be called INSIDE container, after network is up)
get_lxc_ip
# ------------------------------------------------------------------------------
# post_progress_to_api()
#
# - Lightweight progress ping from inside the container
# - Updates the existing telemetry record status
# - Arguments:
# * $1: status (optional, default: "configuring")
# - Signals that the installation is actively progressing (not stuck)
# - Fire-and-forget: never blocks or fails the script
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
# ------------------------------------------------------------------------------
post_progress_to_api() {
command -v curl &>/dev/null || return 0
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
[[ -z "${RANDOM_UUID:-}" ]] && return 0
local progress_status="${1:-configuring}"
# Progress pings are the ONLY telemetry a container sends (terminal statuses
# are reported by the host). Include execution_id + platform + repo
# attribution (exported by the host) so the server can correlate and filter.
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
-H "Content-Type: application/json" \
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\",\"platform\":\"${TELEMETRY_PLATFORM:-}\",\"repo_source\":\"${REPO_SOURCE:-}\",\"repo_slug\":\"${REPO_SLUG:-}\"}" &>/dev/null || true
}
# ==============================================================================
# SECTION 2: NETWORK & CONNECTIVITY
# ==============================================================================
# ------------------------------------------------------------------------------
# verb_ip6()
#
# - Configures IPv6 based on DISABLEIPV6 variable
# - If DISABLEIPV6=yes: disables IPv6 via sysctl
# - Sets verbose mode via set_std_mode()
# ------------------------------------------------------------------------------
verb_ip6() {
set_std_mode # Set STD mode based on VERBOSE
if [ "${IPV6_METHOD:-}" = "disable" ]; then
msg_info "Disabling IPv6 (this may affect some services)"
mkdir -p /etc/sysctl.d
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
# Disable IPv6 (set by community-scripts)
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
$STD sysctl -p /etc/sysctl.d/99-disable-ipv6.conf
msg_ok "Disabled IPv6"
fi
}
# ------------------------------------------------------------------------------
# setting_up_container()
#
# - Verifies network connectivity via hostname -I
# - Retries up to RETRY_NUM times with RETRY_EVERY seconds delay
# - Removes Python EXTERNALLY-MANAGED restrictions
# - Disables systemd-networkd-wait-online.service for faster boot
# - Exits with error if network unavailable after retries
# ------------------------------------------------------------------------------
setting_up_container() {
msg_info "Setting up Container OS"
# Fix Debian 13 LXC template bug where / is owned by nobody
# Only attempt in privileged containers (unprivileged cannot chown /)
if [[ "$(stat -c '%U' /)" != "root" ]]; then
(chown root:root / 2>/dev/null) || true
fi
for ((i = RETRY_NUM; i > 0; i--)); do
if [ "$(hostname -I)" != "" ]; then
break
fi
echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep $RETRY_EVERY
done
if [ "$(hostname -I)" = "" ]; then
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
echo -e "${NETWORK}Check Network Settings"
exit 121
fi
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
systemctl disable -q --now systemd-networkd-wait-online.service
msg_ok "Set up Container OS"
#msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
msg_ok "Network Connected: ${BL}$(hostname -I)"
post_progress_to_api
}
# ------------------------------------------------------------------------------
# network_check()
#
# - Comprehensive network connectivity check for IPv4 and IPv6
# - Tests connectivity to multiple DNS servers:
# * IPv4: 1.1.1.1 (Cloudflare), 8.8.8.8 (Google), 9.9.9.9 (Quad9)
# * IPv6: 2606:4700:4700::1111, 2001:4860:4860::8888, 2620:fe::fe
# - Verifies DNS resolution for GitHub and Community-Scripts domains
# - Prompts user to continue if no internet detected
# - Uses fatal() on DNS resolution failure for critical hosts
# ------------------------------------------------------------------------------
network_check() {
set +e
trap - ERR
ipv4_connected=false
ipv6_connected=false
sleep 1
# Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers.
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
msg_ok "IPv4 Internet Connected"
ipv4_connected=true
else
msg_error "IPv4 Internet Not Connected"
fi
# Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers.
if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then
msg_ok "IPv6 Internet Connected"
ipv6_connected=true
else
msg_error "IPv6 Internet Not Connected"
fi
# If both IPv4 and IPv6 checks fail, prompt the user
if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt </dev/tty
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
else
echo -e "${NETWORK}Check Network Settings"
exit 122
fi
fi
# DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com")
GIT_STATUS="Git DNS:"
DNS_FAILED=false
for HOST in "${GIT_HOSTS[@]}"; do
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
if [[ -z "$RESOLVEDIP" ]]; then
GIT_STATUS+="$HOST:($DNSFAIL)"
DNS_FAILED=true
else
GIT_STATUS+=" $HOST:($DNSOK)"
fi
done
if [[ "$DNS_FAILED" == true ]]; then
fatal "$GIT_STATUS"
else
msg_ok "$GIT_STATUS"
fi
set -e
trap 'error_handler' ERR
}
# ==============================================================================
# SECTION 3: OS UPDATE & PACKAGE MANAGEMENT
# ==============================================================================
# ------------------------------------------------------------------------------
# apt_update_safe()
#
# - Runs apt-get update with CDN mirror fallback
# - On failure, detects distro (Debian/Ubuntu) and tries alternate mirrors
# - Three-phase approach: global mirrors → primary mirror → regional mirrors
# - Falls back to manual user prompt if all auto mirrors fail
# - Detects hash mismatch, SSL errors, and generic apt failures
# ------------------------------------------------------------------------------
apt_update_safe() {
if $STD apt-get update; then
return 0
fi
local failed_mirror
failed_mirror=$(grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null || echo "unknown")
msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..."
local distro
distro=$(. /etc/os-release 2>/dev/null && echo "$ID" || echo "debian")
local eu_mirrors us_mirrors ap_mirrors
if [[ "$distro" == "ubuntu" ]]; then
eu_mirrors="de.archive.ubuntu.com fr.archive.ubuntu.com se.archive.ubuntu.com nl.archive.ubuntu.com it.archive.ubuntu.com ch.archive.ubuntu.com mirrors.xtom.de"
us_mirrors="us.archive.ubuntu.com archive.ubuntu.com mirrors.edge.kernel.org mirror.csclub.uwaterloo.ca mirrors.ocf.berkeley.edu mirror.math.princeton.edu"
ap_mirrors="au.archive.ubuntu.com jp.archive.ubuntu.com kr.archive.ubuntu.com tw.archive.ubuntu.com mirror.aarnet.edu.au"
else
eu_mirrors="ftp.de.debian.org ftp.fr.debian.org ftp.nl.debian.org ftp.uk.debian.org ftp.ch.debian.org ftp.se.debian.org ftp.it.debian.org ftp.fau.de ftp.halifax.rwth-aachen.de debian.mirror.lrz.de mirror.init7.net debian.ethz.ch mirrors.dotsrc.org debian.mirrors.ovh.net"
us_mirrors="ftp.us.debian.org ftp.ca.debian.org debian.csail.mit.edu mirrors.ocf.berkeley.edu mirrors.wikimedia.org debian.osuosl.org mirror.cogentco.com"
ap_mirrors="ftp.au.debian.org ftp.jp.debian.org ftp.tw.debian.org ftp.kr.debian.org ftp.hk.debian.org ftp.sg.debian.org mirror.aarnet.edu.au mirror.nitc.ac.in"
fi
local tz regional others
tz=$(cat /etc/timezone 2>/dev/null || echo "UTC")
case "$tz" in
Europe/* | Arctic/*)
regional="$eu_mirrors"
others="$us_mirrors $ap_mirrors"
;;
America/*)
regional="$us_mirrors"
others="$eu_mirrors $ap_mirrors"
;;
Asia/* | Australia/* | Pacific/*)
regional="$ap_mirrors"
others="$eu_mirrors $us_mirrors"
;;
*)
regional=""
others="$eu_mirrors $us_mirrors $ap_mirrors"
;;
esac
echo 'Acquire::By-Hash "no";' >/etc/apt/apt.conf.d/99no-by-hash
_try_apt_mirror() {
local m=$1
for src in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do
[[ -f "$src" ]] && sed -i "s|URIs: http[s]*://[^/]*/|URIs: http://${m}/|g; s|deb http[s]*://[^/]*/|deb http://${m}/|g" "$src"
done
rm -rf /var/lib/apt/lists/*
local out
out=$(apt-get update 2>&1)
if echo "$out" | grep -qi "hashsum\|hash sum"; then
msg_warn "Mirror ${m} failed (hash mismatch)"
return 1
elif echo "$out" | grep -qi "SSL\|certificate"; then
msg_warn "Mirror ${m} failed (SSL/certificate error)"
return 1
elif echo "$out" | grep -q "^E:"; then
msg_warn "Mirror ${m} failed (apt-get update error)"
return 1
else
msg_ok "CDN set to ${m}: tests passed"
return 0
fi
}
_scan_reachable() {
local result=""
for m in $1; do
if timeout 2 bash -c "echo >/dev/tcp/$m/80" 2>/dev/null; then
result="$result $m"
fi
done
echo "$result" | xargs
}
local apt_ok=false
# Phase 1: Scan global mirrors first (independent of local CDN issues)
local others_ok
others_ok=$(_scan_reachable "$others")
local others_pick
others_pick=$(printf '%s\n' $others_ok | shuf | head -3 | xargs)
for mirror in $others_pick; do
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}"
if _try_apt_mirror "$mirror"; then
apt_ok=true
break
fi
done
# Phase 2: Try primary mirror
if [[ "$apt_ok" != true ]]; then
local primary
if [[ "$distro" == "ubuntu" ]]; then
primary="archive.ubuntu.com"
else
primary="ftp.debian.org"
fi
if timeout 2 bash -c "echo >/dev/tcp/$primary/80" 2>/dev/null; then
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${primary}"
if _try_apt_mirror "$primary"; then
apt_ok=true
fi
fi
fi
# Phase 3: Fall back to regional mirrors
if [[ "$apt_ok" != true ]]; then
local regional_ok
regional_ok=$(_scan_reachable "$regional")
local regional_pick
regional_pick=$(printf '%s\n' $regional_ok | shuf | head -3 | xargs)
for mirror in $regional_pick; do
msg_custom "${INFO}" "${YW}" "Attempting mirror: ${mirror}"
if _try_apt_mirror "$mirror"; then
apt_ok=true
break
fi
done
fi
# Phase 4: All auto mirrors failed, prompt user
if [[ "$apt_ok" != true ]]; then
msg_warn "Multiple mirrors failed (possible CDN synchronization issue)."
if [[ "$distro" == "ubuntu" ]]; then
msg_warn "Find Ubuntu mirrors at: https://launchpad.net/ubuntu/+archivemirrors"
else
msg_warn "Find Debian mirrors at: https://www.debian.org/mirror/list"
fi
local custom_mirror
while true; do
read -rp " Enter a mirror hostname (or 'skip' to abort): " custom_mirror </dev/tty
[[ -z "$custom_mirror" ]] && continue
[[ "$custom_mirror" == "skip" ]] && break
[[ ! "$custom_mirror" =~ ^[a-zA-Z0-9._-]+$ ]] && {
msg_warn "Invalid hostname format."
continue
}
if _try_apt_mirror "$custom_mirror"; then
apt_ok=true
break
fi
msg_warn "Mirror '${custom_mirror}' also failed. Try another or type 'skip'."
done
fi
if [[ "$apt_ok" != true ]]; then
msg_error "All mirrors failed. Check network or try again later."
return 1
fi
}
# ------------------------------------------------------------------------------
# update_os()
#
# - Updates container OS via apt-get update and dist-upgrade
# - Configures APT cacher proxy if CACHER=yes (accelerates package downloads)
# - Removes Python EXTERNALLY-MANAGED restrictions for pip
# - Sources tools.func for additional setup functions after update
# - Uses $STD wrapper to suppress output unless VERBOSE=yes
# ------------------------------------------------------------------------------
update_os() {
msg_info "Updating Container OS"
configure_http_proxy
if [[ "$CACHER" == "yes" && -z "${HTTP_PROXY:-${http_proxy:-}}" ]]; then
echo 'Acquire::http::Proxy-Auto-Detect "/usr/local/bin/apt-proxy-detect.sh";' >/etc/apt/apt.conf.d/00aptproxy
local _proxy_raw="${CACHER_IP}"
local _proxy_host _proxy_port _proxy_url
# Parse host and port from URL or plain IP/hostname
_proxy_host=$(echo "$_proxy_raw" | sed -e 's|https\?://||' -e 's|/.*||' | cut -d: -f1)
_proxy_port=$(echo "$_proxy_raw" | sed -e 's|https\?://||' -e 's|/.*||' | cut -s -d: -f2)
if [[ "$_proxy_raw" =~ ^https?:// ]]; then
# Full URL provided — use as-is for proxy output, extract port for nc check
_proxy_url="$_proxy_raw"
_proxy_port="${_proxy_port:-80}"
else
# Legacy: plain IP or hostname — default to http + port 3142
_proxy_port="${_proxy_port:-3142}"
_proxy_url="http://${_proxy_raw}:${_proxy_port}"
fi
cat <<EOF >/usr/local/bin/apt-proxy-detect.sh
#!/bin/bash
if nc -w1 -z "${_proxy_host}" ${_proxy_port}; then
echo -n "${_proxy_url}"
else
echo -n "DIRECT"
fi
EOF
chmod +x /usr/local/bin/apt-proxy-detect.sh
fi
apt_update_safe
$STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
msg_ok "Updated Container OS"
post_progress_to_api
local tools_content
tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || {
msg_error "Failed to download tools.func"
exit 115
}
source /dev/stdin <<<"$tools_content"
if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then
msg_error "tools.func loaded but incomplete — missing expected functions"
exit 115
fi
}
# ==============================================================================
# SECTION 4: MOTD & SSH CONFIGURATION
# ==============================================================================
# ------------------------------------------------------------------------------
# motd_ssh()
#
# - Configures Message of the Day (MOTD) with container information
# - Creates /etc/profile.d/00_lxc-details.sh with:
# * Application name
# * Warning banner (DEV repository)
# * OS name and version
# * Hostname and IP address
# * GitHub repository link
# - Disables executable flag on /etc/update-motd.d/* scripts
# - Enables root SSH access if SSH_ROOT=yes
# - Configures TERM environment variable for better terminal support
# ------------------------------------------------------------------------------
motd_ssh() {
# Set terminal to 256-color mode
grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
echo "[ -t 1 ] || return 0" >"$PROFILE_FILE"
echo "echo -e \"\"" >>"$PROFILE_FILE"
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
echo "echo \"\"" >>"$PROFILE_FILE"
cat <<EOF >>"$PROFILE_FILE"
os_display="Unknown OS"
if [ -r /etc/os-release ]; then
. /etc/os-release
os_display="\${PRETTY_NAME:-\${NAME:-Unknown OS}}"
fi
echo -e "${TAB}${OS}${YW} OS: ${GN}\${os_display}${CL}"
EOF
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE"
# Disable default MOTD scripts
chmod -x /etc/update-motd.d/*
if [[ "${SSH_ROOT}" == "yes" ]]; then
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
systemctl restart sshd
fi
post_progress_to_api
}
# ==============================================================================
# SECTION 5: CONTAINER CUSTOMIZATION
# ==============================================================================
# ------------------------------------------------------------------------------
# customize()
#
# - Customizes container for passwordless root login if PASSWORD is empty
# - Configures getty for auto-login via /etc/systemd/system/container-getty@1.service.d/override.conf
# - Creates /usr/bin/update script for easy application updates
# - Injects SSH authorized keys if SSH_AUTHORIZED_KEY variable is set
# - Sets proper permissions on SSH directories and key files
# ------------------------------------------------------------------------------
customize() {
if [[ "$PASSWORD" == "" ]]; then
msg_info "Customizing Container"
GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf"
mkdir -p "$(dirname "$GETTY_OVERRIDE")"
cat <<EOF >"$GETTY_OVERRIDE"
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
EOF
systemctl daemon-reload
systemctl restart "$(basename "$(dirname "$GETTY_OVERRIDE")" | sed 's/\.d//')"
msg_ok "Customized Container"
fi
echo 'set -a; [ -f /etc/profile.d/90-http-proxy.sh ] && . /etc/profile.d/90-http-proxy.sh; set +a; bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/'"${app}"'.sh)"' >/usr/bin/update
chmod +x /usr/bin/update
if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then
mkdir -p /root/.ssh
echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys
fi
post_progress_to_api
}
-10293
View File
File diff suppressed because it is too large Load Diff
-1138
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -65,6 +65,28 @@ while [[ -z "${CTID}" ]]; do
"${CTID_MENU[@]}" 3>&1 1>&2 2>&3) || exit 0
done
if [[ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]]; then
msg_info "Starting CT $CTID"
pct start "$CTID"
for _ in {1..30}; do
pct exec "$CTID" -- sh -c 'ip route 2>/dev/null | grep -q "^default"' && break
sleep 1
done
msg_ok "Started CT $CTID"
fi
CT_OS=$(pct exec "$CTID" -- sh -c '. /etc/os-release 2>/dev/null; echo "${ID:-}"')
case "$CT_OS" in
debian | ubuntu) ;;
alpine)
pct exec "$CTID" -- sh -c 'command -v bash >/dev/null 2>&1 || apk add --no-cache bash >/dev/null'
;;
*)
msg_error "CT $CTID runs ${CT_OS:-an unknown OS}. This add-on supports Debian, Ubuntu and Alpine containers."
exit 238
;;
esac
CTID_CONFIG_PATH="/etc/pve/lxc/${CTID}.conf"
# Skip if already configured
+7 -6
View File
@@ -70,14 +70,10 @@ function cleanup_ctid() {
header_info
require_pve_host
# Stop Proxmox VE Monitor-All if running
if systemctl is-active -q ping-instances.service; then
systemctl stop ping-instances.service
fi
msg_info "Updating template list"
pveam update >/dev/null 2>&1
msg_ok "Updated template list"
whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templates" --yesno "This will allow for the creation of one of the many Template LXC Containers. Proceed?" 10 68
whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templates" --yesno "This will allow for the creation of one of the many Template LXC Containers. Proceed?" 10 68 || exit 0
TEMPLATE_MENU=()
MSG_MAX_LENGTH=0
HOST_ARCH=$(dpkg --print-architecture)
@@ -87,7 +83,7 @@ while read -r TAG ITEM ARCH; do
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
TEMPLATE_MENU+=("$ITEM" "$TAG " "OFF")
done < <(pveam available)
TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Template LXCs" --radiolist "\nSelect a Template LXC to create:\n" 16 $((MSG_MAX_LENGTH + 58)) 10 "${TEMPLATE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Template LXCs" --radiolist "\nSelect a Template LXC to create:\n" 16 $((MSG_MAX_LENGTH + 58)) 10 "${TEMPLATE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit 0
[ -z "$TEMPLATE" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Template LXC Selected" --msgbox "It appears that no Template LXC container was selected" 10 68
echo "Done"
@@ -185,6 +181,11 @@ msg_ok "Using '$TEMPLATE_STORAGE' for template storage."
CONTAINER_STORAGE=$(select_storage container)
msg_ok "Using '$CONTAINER_STORAGE' for container storage."
# Stop Proxmox VE Monitor-All if running
if systemctl is-active -q ping-instances.service; then
systemctl stop ping-instances.service
fi
# Download template
msg_info "Downloading LXC template (Patience)"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || {
+3 -3
View File
@@ -42,7 +42,7 @@ require_debian_like
function uninstall() {
msg_info "Uninstalling Homebrew"
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd)
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /(nologin|false)$/ { print $1; exit }' /etc/passwd)
if [[ -n "$BREW_USER" ]]; then
BREW_USER_HOME=$(getent passwd "$BREW_USER" | cut -d: -f6)
for rc_file in "$BREW_USER_HOME/.bashrc" "$BREW_USER_HOME/.profile"; do
@@ -64,7 +64,7 @@ function uninstall() {
# ==============================================================================
function install() {
msg_info "Detecting Non-Root User"
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd)
BREW_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /(nologin|false)$/ { print $1; exit }' /etc/passwd)
if [[ -z "$BREW_USER" ]]; then
msg_warn "No non-root user found (uid >= 1000). Homebrew cannot run as root."
read -r -p "${TAB}Create a 'brew' user automatically? (y/N): " create_user_prompt
@@ -81,7 +81,7 @@ function install() {
msg_ok "Detected User: $BREW_USER"
msg_info "Installing Dependencies"
$STD apt update
apt_update_safe
$STD apt install -y build-essential git file procps
msg_ok "Installed Dependencies"
+2 -2
View File
@@ -80,7 +80,7 @@ install() {
msg_ok "Set up repository"
msg_info "Installing ${APP}"
$STD apt update
apt_update_safe
$STD apt install -y netdata
msg_ok "Installed ${APP}"
msg_ok "Completed successfully!\n"
@@ -108,7 +108,7 @@ OPTIONS=(Install "Install ${APP} on Proxmox VE"
Uninstall "Uninstall ${APP} from Proxmox VE")
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP}" \
--menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
--menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3) || exit 0
case $CHOICE in
"Install") install ;;
+1 -1
View File
@@ -5,7 +5,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
________ __ _ ________
/ ____/ /__ ____ _____ / / | |/ / ____/
+1 -1
View File
@@ -19,7 +19,7 @@ CONF_FILE="/etc/update-lxcs.conf"
LOG_FILE="/var/log/update-lxcs-cron.log"
CRON_ENTRY="0 0 * * 0 ${LOCAL_SCRIPT} >>${LOG_FILE} 2>&1"
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
______ __ __ __ __ __ _ ________
/ ____/________ ____ / / / /___ ____/ /___ _/ /____ / / | |/ / ____/____
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
______ __ __ _ ________
/ ____/ _____ _______ __/ /____ / / | |/ / ____/
@@ -67,7 +67,7 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do
else
os=$(pct config "$container" | awk '/^ostype/ {print $2}')
if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then
echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL}"
echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian or Ubuntu ${CL}"
continue
fi
+3 -2
View File
@@ -3,7 +3,7 @@
set -eEuo pipefail
function header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
_______ __ __ ______ _
/ ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___
@@ -118,7 +118,8 @@ function trim_container() {
fi
local fstrim_output
fstrim_output=$(pct fstrim "$container" 2>&1)
# pct fstrim fails on a locked CT (e.g. a running backup); report it and go on.
fstrim_output=$(pct fstrim "$container" 2>&1) || true
if echo "$fstrim_output" | grep -qi "not supported"; then
echo -e "${RD}fstrim isnt supported on this storage!${CL}"
elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then
+1 -1
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ ______ __ ____ __ __
/ __ \_________ _ ______ ___ ____ _ __ / ____/_ _____ _____/ /_ / __ \___ / /__ / /____
+1 -1
View File
@@ -9,7 +9,7 @@
set -e
function header_info {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
__ ___ __ ___ __ __ _
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ ____ __________ __ ______ __________ ___ ____ ______
/ __ \/ __ ) ___/__ / / / / / __ \/ ____/ __ \/ | / __ \/ ____/
@@ -168,7 +168,7 @@ while true; do
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
+7 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ ____ _____ __ __ __ __ __
/ __ \/ __ ) ___// // / / / / /___ ____ __________ _____/ /__
@@ -192,13 +192,18 @@ while true; do
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
esac
done
if command -v pveversion >/dev/null 2>&1; then
echo -e "\n🛑 PVE Detected, Wrong Script!\n"
exit 232
fi
if [ "$(dpkg --print-architecture 2>/dev/null)" = "arm64" ]; then
header_info
msg_error "This upgrade script targets the amd64 Proxmox repositories and is not supported on ARM64."
+3 -3
View File
@@ -4,7 +4,7 @@
# ----------------------------------------------------------------------------
# This file is NOT meant to be executed as-is.
# It is a collection of complete, copy-pasteable example hooks for the
# optional `var_post_install` feature in build.func.
# optional `var_post_install` feature in the core engine.
#
# HOW IT WORKS
# ------------
@@ -118,7 +118,7 @@ TS="$(date -Iseconds)"
read -r -d '' DISCORD_PAYLOAD <<JSON || true
{
"username": "Proxmox - ${NODE}",
"avatar_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png",
"avatar_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/.github/assets/logo-81x112.png",
"embeds": [{
"title": "✅ ${APP} LXC created",
"description": "A new community-script LXC has been provisioned on **${NODE}**.",
@@ -173,7 +173,7 @@ set -euo pipefail
# --- CONFIG (edit me) -------------------------------------------------------
TARGET_POOL="auto-lxc"
EXTRA_TAGS=("auto-provisioned" "${NSAPP}") # community-script tag is set by build.func
EXTRA_TAGS=("auto-provisioned" "${NSAPP}") # community-script tag is set by the engine
BACKUP_STORAGE="pbs-main" # set to "" to skip initial backup
PIHOLE_HOST="192.168.1.5"
PIHOLE_PASSWORD="changeme" # web-UI password
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ ____ _____ ____ __ ____ __ ____
/ __ \/ __ ) ___/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -89,7 +89,7 @@ main() {
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ ____ __ ___ ____ __ ____ __ ____
/ __ \/ __ \/ |/ / / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -412,7 +412,7 @@ while true; do
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
+2 -2
View File
@@ -5,7 +5,7 @@
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ __ _________ ____ __ ____ __ ____
/ __ \/ |/ / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -410,7 +410,7 @@ while true; do
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
+4 -5
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ _ ________ ____ __ ____ __ ____
/ __ \ | / / ____/ / __ \____ _____/ /_ / _/___ _____/ /_____ _/ / /
@@ -81,7 +81,7 @@ main() {
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
@@ -226,9 +226,8 @@ start_routines_9() {
[[ -n "$list_files" ]] && MSG+="$(echo "$list_files" | sed 's|^| - |')\n"
MSG+="\nDo you want to disable (comment out/rename) all legacy sources and use ONLY deb822 .sources format?\n\nRecommended for Proxmox VE 9."
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \
--yesno "$MSG" 18 80
if [[ $? -eq 0 ]]; then
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \
--yesno "$MSG" 18 80; then
# Backup and disable sources.list
if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then
cp "$listfile" "$listfile.bak"
+1 -1
View File
@@ -31,7 +31,7 @@ check_root() {
select_target_storage_and_container_id() {
echo -e "\nSelect target storage for restored container:\n"
mapfile -t target_storages < <(pvesm status --content images | awk 'NR > 1 {print $1}')
mapfile -t target_storages < <(pvesm status --content rootdir | awk 'NR > 1 {print $1}')
for i in "${!target_storages[@]}"; do
printf "%s) %s\n" "$((i + 1))" "${target_storages[$i]}"
done
+2 -2
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
____ _ ____________ __ ______ __________ ___ ____ ______
/ __ \ | / / ____( __ ) / / / / __ \/ ____/ __ \/ | / __ \/ ____/
@@ -118,7 +118,7 @@ while true; do
case $yn in
[Yy]*) break ;;
[Nn]*)
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
;;
*) echo "Please answer yes or no." ;;
+3 -3
View File
@@ -11,7 +11,7 @@ source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "scaling-governor" "pve"
header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<EOF
________ __ __ _____
/ ___/ _ \/ / / / / ___/__ _ _____ _______ ___ _______
@@ -26,7 +26,7 @@ GOV_BASE="/sys/devices/system/cpu/cpu0/cpufreq"
if [[ ! -r "$GOV_BASE/scaling_governor" || ! -r "$GOV_BASE/scaling_available_governors" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Not Available" \
--msgbox "CPU frequency scaling is not available on this system.\n\nThis is normal when no cpufreq driver is active (e.g. CPU power management handled by the BIOS, or certain virtualized hosts)." 12 70
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit 0
fi
@@ -42,7 +42,7 @@ done < <(tr ' ' '\n' <"$GOV_BASE/scaling_available_governors" | sed '/^$/d' | gr
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --radiolist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$scaling_governor" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
exit
}
echo "${scaling_governor}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null
+1 -1
View File
@@ -11,7 +11,7 @@ declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "storage-share
set -eEuo pipefail
function header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
_____ __ ___ _________
/ ___// /_____ _________ _____ ____ / | / _/ __ \
+20 -13
View File
@@ -271,15 +271,22 @@ function dry_run_container() {
fi
}
# The line of vzdump or pct restore output that says why it failed.
function failure_reason() {
local reason
reason=$(grep -m1 '^ERROR:' <<<"$1" | sed 's/^ERROR: //')
[[ -n "$reason" ]] || reason=$(grep -v '^[[:space:]]*$' <<<"$1" | tail -n1)
printf '%s' "$reason"
}
function backup_container() {
msg_info "Creating backup for container $1"
vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" >/dev/null 2>&1
status=$?
if [ $status -eq 0 ]; then
local output
if output=$(vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "{{guestname}} - community-scripts backup updater" 2>&1); then
msg_ok "Backup created"
else
msg_error "Backup failed for container $1"
log_write "Container $1: vzdump failed:"$'\n'"$output"
msg_error "Backup failed for container $1: $(failure_reason "$output")"
exit 235
fi
}
@@ -337,7 +344,7 @@ function log_write() {
# Skip confirmation if var_skip_confirm is set to yes
if [[ "$var_skip_confirm" != "yes" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit
whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC App Update" --yesno "This will update apps in LXCs installed by Helper-Scripts. Proceed?" 10 58 || exit 0
fi
tags_formatted="${var_tags//|/, }"
@@ -460,12 +467,11 @@ if [ "$BACKUP_CHOICE" == "yes" ]; then
# Determine storage based on var_backup_storage
if [[ -n "$var_backup_storage" ]]; then
# Validate that the specified storage exists and supports backups
if echo "$STORAGES" | grep -qw "$var_backup_storage"; then
if grep -qxF -- "$var_backup_storage" <<<"$STORAGES"; then
STORAGE_CHOICE="$var_backup_storage"
msg_ok "Using backup storage: $STORAGE_CHOICE"
else
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups!"
msg_info "Available storages: $(echo $STORAGES | tr '\n' ' ')"
msg_error "Specified backup storage '$var_backup_storage' not found or doesn't support backups! Available: $(echo $STORAGES)"
exit 119
fi
else
@@ -642,7 +648,7 @@ for container in $CHOICE; do
log_result "$container" "$service" "SKIPPED" "Storage critically low on /boot (>80%)"
log_write "Container $container ($service): SKIPPED — storage critically low on /boot"
elif [ "$BACKUP_CHOICE" == "yes" ]; then
msg_error "Update failed for container $container (exit code: $exit_code) — attempting restore"
msg_error "Update failed for container $container ($service, exit code: $exit_code) — attempting restore"
log_write "Container $container ($service): FAILED (exit $exit_code) — attempting restore"
msg_info "Restoring LXC $container from backup ($STORAGE_CHOICE)"
pct stop $container
@@ -654,7 +660,7 @@ for container in $CHOICE; do
log_write "Container $container ($service): FAILED — no backup found for restore"
exit 235
fi
pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force >/dev/null 2>&1
restore_output=$(pct restore $container "$BACKUP_ENTRY" --storage $LXC_STORAGE --force 2>&1)
restorestatus=$?
if [ $restorestatus -eq 0 ]; then
pct start $container
@@ -662,13 +668,14 @@ for container in $CHOICE; do
log_result "$container" "$service" "RESTORED" "Update failed (exit $exit_code) — restored from backup"
log_write "Container $container ($service): RESTORED from $BACKUP_ENTRY"
else
msg_error "Restore failed for container $container"
log_write "Container $container: pct restore failed:"$'\n'"$restore_output"
msg_error "Restore failed for container $container: $(failure_reason "$restore_output")"
log_result "$container" "$service" "FAILED" "Update failed (exit $exit_code) — restore also failed"
log_write "Container $container ($service): FAILED — restore also failed"
exit 235
fi
else
msg_error "Update failed for container $container (exit code: $exit_code)"
msg_error "Update failed for container $container ($service, exit code: $exit_code)"
log_result "$container" "$service" "FAILED" "Exit code $exit_code"
log_write "Container $container ($service): FAILED (exit $exit_code)"
if [[ "$var_continue_on_error" == "yes" ]]; then
+1 -1
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info() {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
__ __ __ __ __ _ ________
/ / / /___ ____/ /___ _/ /____ / / | |/ / ____/
+1 -1
View File
@@ -6,7 +6,7 @@
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
function header_info {
clear
clear 2>/dev/null || printf '\033[H\033[2J\033[3J'
cat <<"EOF"
__ __ __ __ ____
/ / / /___ ____/ /___ _/ /____ / __ \___ ____ ____
+3 -22
View File
@@ -24,18 +24,9 @@ THIN="discard=on,ssd=1,"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${command}"
echo -e "\n$error_message\n"
cleanup_vmid
}
trap 'post_update_to_api "failed" "130"' SIGINT
trap 'post_update_to_api "failed" "143"' SIGTERM
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
TEMP_DIR=$(mktemp -d)
pushd "$TEMP_DIR" >/dev/null
@@ -126,16 +117,6 @@ post_to_api_vm
vm_select_storage "$HN"
# ==============================================================================
# PREREQUISITES
# ==============================================================================
if ! command -v virt-customize &>/dev/null; then
msg_info "Installing libguestfs-tools"
$STD apt-get update
$STD apt-get install -y libguestfs-tools
msg_ok "Installed libguestfs-tools"
fi
msg_info "Retrieving the URL for the ${APP} Qcow2 Disk Image"
ALMA_ARCH="$(vm_arch_resolve x86_64 aarch64)"
URL="https://repo.almalinux.org/almalinux/${var_version}/cloud/${ALMA_ARCH}/images/AlmaLinux-${var_version}-GenericCloud-latest.${ALMA_ARCH}.qcow2"

Some files were not shown because too many files have changed in this diff Show More