Compare commits

...

15 Commits

Author SHA1 Message Date
CanbiZ (MickLesk) 72bbe22f4e fix(pangolin): create migration tables before data transfer to prevent role loss
The previous migration fix attempted to INSERT INTO 'userOrgRoles' before
that table existed (it is new in 1.17.1). The error was silently ignored,
so no role data was migrated. When drizzle-kit then dropped roleId from
userOrgs, all user-role associations were permanently lost.

- CREATE TABLE IF NOT EXISTS for userOrgRoles before migrating data
- Same treatment for userInviteRoles (also new in 1.17.1)

Fixes community-scripts/ProxmoxVE#13857
2026-04-20 10:42:26 +02:00
community-scripts-pr-app[bot] 7e5e5be161 Update CHANGELOG.md (#13873)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 08:19:24 +00:00
CanbiZ (MickLesk) 55c7549c3e Refactor: PMG Post Install (#13693)
* PMG Post Install: Detect gateway via /etc/os-release when /etc/issue is generic

* PMG Post Install: detect gateway via dpkg or PMG service units

* PMG: migrate post-install to deb822 format, fix APT source conflicts

- Simplify PMG detection to dpkg-based check only
- Extend repo_state() to scan .sources files (deb822 format)
- Add toggle_repo() helper for enable/disable on both formats
- Migrate Debian sources correction to deb822 (debian.sources)
- Migrate pmg-enterprise, pmg-no-subscription, pmgtest repo
  creation to deb822 .sources files
- Install script: clean up duplicate APT sources created by
  proxmox-mailgateway-container package (enterprise.list,
  pmg-install-repo.list, legacy sources.list)

* fix: use official Signed-By path & revert install script cleanup

- Change Signed-By from /etc/apt/keyrings/pmg.gpg to
  /usr/share/keyrings/proxmox-archive-keyring.gpg in all three
  PMG repo creation blocks (enterprise, no-subscription, test),
  matching official PMG docs and PVE post-install convention
- Remove APT source cleanup from install script (handled by
  post-pmg-install instead)

* remove empty line
2026-04-20 10:18:55 +02:00
community-scripts-pr-app[bot] 4089fed9c9 Update CHANGELOG.md (#13869)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 07:24:30 +00:00
CanbiZ (MickLesk) 325668d5c9 Wanderer: add pocketbase CLI wrapper with env (#13863) 2026-04-20 09:24:08 +02:00
community-scripts-pr-app[bot] 63296fc3e7 Update CHANGELOG.md (#13868)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 07:22:25 +00:00
community-scripts-pr-app[bot] e98fae3c54 Update CHANGELOG.md (#13867)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 07:22:13 +00:00
CanbiZ (MickLesk) bf97029f57 Pangolin: pre-apply schema migrations to prevent data loss (#13861) 2026-04-20 09:21:54 +02:00
community-scripts-pr-app[bot] 5859c06715 Update CHANGELOG.md (#13866)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 07:21:49 +00:00
CanbiZ (MickLesk) 9b4fc56de6 Change migration messages to warnings (#13860) 2026-04-20 09:21:25 +02:00
community-scripts-pr-app[bot] 3a5244e285 Update CHANGELOG.md (#13865)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 07:20:48 +00:00
CanbiZ (MickLesk) 1025715a74 slskd: migrate config keys for 0.25.0 breaking change (#13862) 2026-04-20 09:20:20 +02:00
CanbiZ (MickLesk) ec3b79bb61 PocketBase Bot: fix field names notes_json -> notes, install_methods_json -> install_methods
Collection fields were renamed but bot still used old _json suffixed names.
2026-04-20 09:02:27 +02:00
community-scripts-pr-app[bot] 37dfb58d29 Update CHANGELOG.md (#13864)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-20 06:54:03 +00:00
CanbiZ (MickLesk) d95073f505 PocketBase Bot: fix double-stringify on notes_json patch
notes_json was sent as JSON.stringify(arr) inside JSON.stringify(),
causing PocketBase to receive a string instead of a JSON array.
patchMethods already does it correctly — align patchNotes.
2026-04-20 08:53:34 +02:00
7 changed files with 143 additions and 30 deletions
+6 -6
View File
@@ -337,8 +337,8 @@ jobs:
if (infoMatch) { if (infoMatch) {
// ── INFO SUBCOMMAND ────────────────────────────────────────────── // ── INFO SUBCOMMAND ──────────────────────────────────────────────
const notesArr = readJsonBlob(record.notes_json); const notesArr = readJsonBlob(record.notes);
const methodsArr = readJsonBlob(record.install_methods_json); const methodsArr = readJsonBlob(record.install_methods);
const out = []; const out = [];
out.push('️ **PocketBase Bot**: Info for **`' + slug + '`**\n'); out.push('️ **PocketBase Bot**: Info for **`' + slug + '`**\n');
@@ -382,13 +382,13 @@ jobs:
// ── NOTE SUBCOMMAND ────────────────────────────────────────────── // ── NOTE SUBCOMMAND ──────────────────────────────────────────────
const noteAction = noteMatch[1].toLowerCase(); const noteAction = noteMatch[1].toLowerCase();
const noteArgsStr = rest.substring(noteMatch[0].length).trim(); const noteArgsStr = rest.substring(noteMatch[0].length).trim();
let notesArr = readJsonBlob(record.notes_json); let notesArr = readJsonBlob(record.notes);
async function patchNotes(arr) { async function patchNotes(arr) {
const res = await request(recordsUrl + '/' + record.id, { const res = await request(recordsUrl + '/' + record.id, {
method: 'PATCH', method: 'PATCH',
headers: { 'Authorization': token, 'Content-Type': 'application/json' }, headers: { 'Authorization': token, 'Content-Type': 'application/json' },
body: JSON.stringify({ notes_json: JSON.stringify(arr) }) body: JSON.stringify({ notes: arr })
}); });
if (!res.ok) { if (!res.ok) {
await addReaction('-1'); await addReaction('-1');
@@ -504,7 +504,7 @@ jobs:
// ── METHOD SUBCOMMAND ──────────────────────────────────────────── // ── METHOD SUBCOMMAND ────────────────────────────────────────────
const methodArgs = rest.replace(/^method\s*/i, '').trim(); const methodArgs = rest.replace(/^method\s*/i, '').trim();
const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list'; const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list';
let methodsArr = readJsonBlob(record.install_methods_json); let methodsArr = readJsonBlob(record.install_methods);
// Method field classification // Method field classification
const RESOURCE_KEYS = { cpu: 'number', ram: 'number', hdd: 'number', os: 'string', version: 'string' }; const RESOURCE_KEYS = { cpu: 'number', ram: 'number', hdd: 'number', os: 'string', version: 'string' };
@@ -526,7 +526,7 @@ jobs:
const res = await request(recordsUrl + '/' + record.id, { const res = await request(recordsUrl + '/' + record.id, {
method: 'PATCH', method: 'PATCH',
headers: { 'Authorization': token, 'Content-Type': 'application/json' }, headers: { 'Authorization': token, 'Content-Type': 'application/json' },
body: JSON.stringify({ install_methods_json: arr }) body: JSON.stringify({ install_methods: arr })
}); });
if (!res.ok) { if (!res.ok) {
await addReaction('-1'); await addReaction('-1');
+18
View File
@@ -445,6 +445,24 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-04-20
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- Pangolin: pre-apply schema migrations to prevent data loss [@MickLesk](https://github.com/MickLesk) ([#13861](https://github.com/community-scripts/ProxmoxVE/pull/13861))
- ActualBudget: change migration messages to warnings [@MickLesk](https://github.com/MickLesk) ([#13860](https://github.com/community-scripts/ProxmoxVE/pull/13860))
- slskd: migrate config keys for 0.25.0 breaking change [@MickLesk](https://github.com/MickLesk) ([#13862](https://github.com/community-scripts/ProxmoxVE/pull/13862))
- #### ✨ New Features
- Wanderer: add pocketbase CLI wrapper with env [@MickLesk](https://github.com/MickLesk) ([#13863](https://github.com/community-scripts/ProxmoxVE/pull/13863))
- #### 🔧 Refactor
- Refactor: PMG Post Install [@MickLesk](https://github.com/MickLesk) ([#13693](https://github.com/community-scripts/ProxmoxVE/pull/13693))
## 2026-04-19 ## 2026-04-19
### 🆕 New Scripts ### 🆕 New Scripts
+3 -3
View File
@@ -48,9 +48,9 @@ function update_script() {
msg_ok "Updated successfully!" msg_ok "Updated successfully!"
fi fi
else else
msg_info "Old Installation Found, you need to migrate your data and recreate to a new container" msg_warn "Old Installation Found, you need to migrate your data and recreate to a new container"
msg_info "Please follow the instructions on the Actual Budget website to migrate your data" msg_warn "Please follow the instructions on the Actual Budget website to migrate your data"
msg_info "https://actualbudget.org/docs/backup-restore/backup" msg_warn "https://actualbudget.org/docs/backup-restore/backup"
exit exit
fi fi
exit exit
+27 -1
View File
@@ -69,7 +69,33 @@ function update_script() {
msg_info "Running database migrations" msg_info "Running database migrations"
cd /opt/pangolin cd /opt/pangolin
ENVIRONMENT=prod $STD npx drizzle-kit push --config drizzle.sqlite.config.ts
# Pre-apply potentially destructive schema changes safely so drizzle-kit
# does not recreate tables (which would delete all rows).
local DB="/opt/pangolin/config/db/db.sqlite"
if [[ -f "$DB" ]]; then
sqlite3 "$DB" "ALTER TABLE 'orgs' ADD COLUMN 'settingsLogRetentionDaysConnection' integer DEFAULT 0 NOT NULL;" 2>/dev/null || true
sqlite3 "$DB" "ALTER TABLE 'clientSitesAssociationsCache' ADD COLUMN 'isJitMode' integer DEFAULT 0 NOT NULL;" 2>/dev/null || true
# Create new role-mapping tables and migrate data before drizzle-kit
# drops the roleId columns from userOrgs and userInvites.
sqlite3 "$DB" "CREATE TABLE IF NOT EXISTS 'userOrgRoles' (
'userId' text NOT NULL REFERENCES 'user'('id') ON DELETE CASCADE,
'orgId' text NOT NULL REFERENCES 'orgs'('orgId') ON DELETE CASCADE,
'roleId' integer NOT NULL REFERENCES 'roles'('roleId') ON DELETE CASCADE,
UNIQUE('userId', 'orgId', 'roleId')
);" 2>/dev/null || true
sqlite3 "$DB" "INSERT OR IGNORE INTO 'userOrgRoles' (userId, orgId, roleId) SELECT userId, orgId, roleId FROM 'userOrgs' WHERE roleId IS NOT NULL;" 2>/dev/null || true
sqlite3 "$DB" "CREATE TABLE IF NOT EXISTS 'userInviteRoles' (
'inviteId' text NOT NULL REFERENCES 'userInvites'('inviteId') ON DELETE CASCADE,
'roleId' integer NOT NULL REFERENCES 'roles'('roleId') ON DELETE CASCADE,
PRIMARY KEY('inviteId', 'roleId')
);" 2>/dev/null || true
sqlite3 "$DB" "INSERT OR IGNORE INTO 'userInviteRoles' (inviteId, roleId) SELECT inviteId, roleId FROM 'userInvites' WHERE roleId IS NOT NULL;" 2>/dev/null || true
fi
ENVIRONMENT=prod $STD npx drizzle-kit push --force --config drizzle.sqlite.config.ts
msg_ok "Ran database migrations" msg_ok "Ran database migrations"
msg_info "Updating Badger plugin version" msg_info "Updating Badger plugin version"
+4
View File
@@ -43,6 +43,10 @@ function update_script() {
msg_info "Restoring config" msg_info "Restoring config"
mv /opt/slskd.yml.bak /opt/slskd/config/slskd.yml mv /opt/slskd.yml.bak /opt/slskd/config/slskd.yml
# Migrate 0.25.0 breaking config key renames
sed -i 's/^global:/transfers:/' /opt/slskd/config/slskd.yml
sed -i 's/^integration:/integrations:/' /opt/slskd/config/slskd.yml
msg_ok "Restored config" msg_ok "Restored config"
msg_info "Starting Service(s)" msg_info "Starting Service(s)"
+10
View File
@@ -60,6 +60,16 @@ wait -n
EOF EOF
chmod +x /opt/wanderer/start.sh chmod +x /opt/wanderer/start.sh
cat <<'EOF' >/usr/local/bin/wanderer-pb
#!/usr/bin/env bash
set -a
source /opt/wanderer/.env
set +a
cd /opt/wanderer/source/db
exec ./pocketbase "$@" --dir="$PB_DB_LOCATION"
EOF
chmod +x /usr/local/bin/wanderer-pb
cat <<EOF >/etc/systemd/system/wanderer-web.service cat <<EOF >/etc/systemd/system/wanderer-web.service
[Unit] [Unit]
Description=wanderer Description=wanderer
+75 -20
View File
@@ -47,7 +47,8 @@ msg_error() {
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pmg-install" "pve" declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "post-pmg-install" "pve"
if ! grep -q "Proxmox Mail Gateway" /etc/issue 2>/dev/null; then if ! dpkg -s proxmox-mailgateway-container >/dev/null 2>&1 &&
! dpkg -s proxmox-mailgateway >/dev/null 2>&1; then
msg_error "This script is only intended for Proxmox Mail Gateway" msg_error "This script is only intended for Proxmox Mail Gateway"
exit 232 exit 232
fi fi
@@ -57,14 +58,24 @@ repo_state() {
local repo="$1" local repo="$1"
local file="" local file=""
local state="missing" local state="missing"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
[[ -f "$f" ]] || continue [[ -f "$f" ]] || continue
if grep -q "$repo" "$f"; then if grep -q "$repo" "$f"; then
file="$f" file="$f"
if grep -qE "^[^#].*${repo}" "$f"; then if [[ "$f" == *.sources ]]; then
state="active" # deb822 format: check Enabled field
elif grep -qE "^#.*${repo}" "$f"; then if grep -qiE '^Enabled:\s*no' "$f"; then
state="disabled" state="disabled"
else
state="active"
fi
else
# legacy format
if grep -qE "^[^#].*${repo}" "$f"; then
state="active"
elif grep -qE "^#.*${repo}" "$f"; then
state="disabled"
fi
fi fi
break break
fi fi
@@ -72,6 +83,28 @@ repo_state() {
echo "$state $file" echo "$state $file"
} }
toggle_repo() {
# $1 = file, $2 = action (enable|disable)
local file="$1" action="$2"
if [[ "$file" == *.sources ]]; then
if [[ "$action" == "disable" ]]; then
if grep -qiE '^Enabled:' "$file"; then
sed -i 's/^Enabled:.*/Enabled: no/' "$file"
else
echo "Enabled: no" >>"$file"
fi
else
sed -i 's/^Enabled:.*/Enabled: yes/' "$file"
fi
else
if [[ "$action" == "disable" ]]; then
sed -i '/^[^#]/s/^/# /' "$file"
else
sed -i 's/^# *//' "$file"
fi
fi
}
start_routines() { start_routines() {
header_info header_info
VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)"
@@ -84,11 +117,20 @@ start_routines() {
case $CHOICE in case $CHOICE in
yes) yes)
msg_info "Correcting Debian Sources" msg_info "Correcting Debian Sources"
cat <<EOF >/etc/apt/sources.list cat <<EOF >/etc/apt/sources.list.d/debian.sources
deb http://deb.debian.org/debian ${VERSION} main contrib Types: deb
deb http://deb.debian.org/debian ${VERSION}-updates main contrib URIs: http://deb.debian.org/debian
deb http://security.debian.org/debian-security ${VERSION}-security main contrib Suites: ${VERSION} ${VERSION}-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security
Suites: ${VERSION}-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF EOF
rm -f /etc/apt/sources.list
msg_ok "Corrected Debian Sources" msg_ok "Corrected Debian Sources"
;; ;;
no) msg_error "Selected no to Correcting Debian Sources" ;; no) msg_error "Selected no to Correcting Debian Sources" ;;
@@ -108,7 +150,7 @@ EOF
keep) msg_ok "Kept 'pmg-enterprise' repository" ;; keep) msg_ok "Kept 'pmg-enterprise' repository" ;;
disable) disable)
msg_info "Disabling 'pmg-enterprise' repository" msg_info "Disabling 'pmg-enterprise' repository"
sed -i "s/^[^#].*pmg-enterprise/# &/" "$file" toggle_repo "$file" disable
msg_ok "Disabled 'pmg-enterprise' repository" msg_ok "Disabled 'pmg-enterprise' repository"
;; ;;
delete) delete)
@@ -128,7 +170,7 @@ EOF
case $CHOICE in case $CHOICE in
enable) enable)
msg_info "Enabling 'pmg-enterprise' repository" msg_info "Enabling 'pmg-enterprise' repository"
sed -i "s/^#.*pmg-enterprise/deb/" "$file" toggle_repo "$file" enable
msg_ok "Enabled 'pmg-enterprise' repository" msg_ok "Enabled 'pmg-enterprise' repository"
;; ;;
keep) msg_ok "Kept 'pmg-enterprise' repository disabled" ;; keep) msg_ok "Kept 'pmg-enterprise' repository disabled" ;;
@@ -149,8 +191,12 @@ EOF
case $CHOICE in case $CHOICE in
yes) yes)
msg_info "Adding 'pmg-enterprise' repository" msg_info "Adding 'pmg-enterprise' repository"
cat >/etc/apt/sources.list.d/pmg-enterprise.list <<EOF cat >/etc/apt/sources.list.d/pmg-enterprise.sources <<EOF
deb https://enterprise.proxmox.com/debian/pmg ${VERSION} pmg-enterprise Types: deb
URIs: https://enterprise.proxmox.com/debian/pmg
Suites: ${VERSION}
Components: pmg-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF EOF
msg_ok "Added 'pmg-enterprise' repository" msg_ok "Added 'pmg-enterprise' repository"
;; ;;
@@ -173,7 +219,7 @@ EOF
keep) msg_ok "Kept 'pmg-no-subscription' repository" ;; keep) msg_ok "Kept 'pmg-no-subscription' repository" ;;
disable) disable)
msg_info "Disabling 'pmg-no-subscription' repository" msg_info "Disabling 'pmg-no-subscription' repository"
sed -i "s/^[^#].*pmg-no-subscription/# &/" "$file" toggle_repo "$file" disable
msg_ok "Disabled 'pmg-no-subscription' repository" msg_ok "Disabled 'pmg-no-subscription' repository"
;; ;;
delete) delete)
@@ -193,7 +239,7 @@ EOF
case $CHOICE in case $CHOICE in
enable) enable)
msg_info "Enabling 'pmg-no-subscription' repository" msg_info "Enabling 'pmg-no-subscription' repository"
sed -i "s/^#.*pmg-no-subscription/deb/" "$file" toggle_repo "$file" enable
msg_ok "Enabled 'pmg-no-subscription' repository" msg_ok "Enabled 'pmg-no-subscription' repository"
;; ;;
keep) msg_ok "Kept 'pmg-no-subscription' repository disabled" ;; keep) msg_ok "Kept 'pmg-no-subscription' repository disabled" ;;
@@ -213,8 +259,12 @@ EOF
case $CHOICE in case $CHOICE in
yes) yes)
msg_info "Adding 'pmg-no-subscription' repository" msg_info "Adding 'pmg-no-subscription' repository"
cat >/etc/apt/sources.list.d/pmg-install-repo.list <<EOF cat >/etc/apt/sources.list.d/pmg-no-subscription.sources <<EOF
deb http://download.proxmox.com/debian/pmg ${VERSION} pmg-no-subscription Types: deb
URIs: http://download.proxmox.com/debian/pmg
Suites: ${VERSION}
Components: pmg-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF EOF
msg_ok "Added 'pmg-no-subscription' repository" msg_ok "Added 'pmg-no-subscription' repository"
;; ;;
@@ -236,8 +286,13 @@ EOF
case $CHOICE in case $CHOICE in
yes) yes)
msg_info "Adding 'pmgtest' repository (disabled)" msg_info "Adding 'pmgtest' repository (disabled)"
cat >/etc/apt/sources.list.d/pmgtest-for-beta.list <<EOF cat >/etc/apt/sources.list.d/pmgtest.sources <<EOF
# deb http://download.proxmox.com/debian/pmg ${VERSION} pmgtest Types: deb
URIs: http://download.proxmox.com/debian/pmg
Suites: ${VERSION}
Components: pmgtest
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: no
EOF EOF
msg_ok "Added 'pmgtest' repository" msg_ok "Added 'pmgtest' repository"
;; ;;