mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-08-07 05:13:27 +02:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a9aa287b1 | |||
| a92cba7525 |
Generated
+2
-18
@@ -314,17 +314,12 @@ jobs:
|
|||||||
// ── Allow-lists (mirror the slash bot) ─────────────────────────────
|
// ── Allow-lists (mirror the slash bot) ─────────────────────────────
|
||||||
const ALLOWED_FIELDS = {
|
const ALLOWED_FIELDS = {
|
||||||
name: 'string', description: 'string', logo: 'string', documentation: 'string',
|
name: 'string', description: 'string', logo: 'string', documentation: 'string',
|
||||||
website: 'string', project_url: 'string', repository: 'string', config_path: 'string',
|
website: 'string', project_url: 'string', github: 'string', config_path: 'string',
|
||||||
tags: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string',
|
tags: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string',
|
||||||
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean',
|
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean', has_arm: 'boolean',
|
||||||
architectures: 'select_list', platforms: 'select_list',
|
|
||||||
is_dev: 'boolean', is_disabled: 'boolean', disable_message: 'string',
|
is_dev: 'boolean', is_disabled: 'boolean', disable_message: 'string',
|
||||||
is_deleted: 'boolean', deleted_message: 'string'
|
is_deleted: 'boolean', deleted_message: 'string'
|
||||||
};
|
};
|
||||||
const SELECT_VALUES = {
|
|
||||||
architectures: ['amd64', 'arm64'],
|
|
||||||
platforms: ['pve', 'incus'],
|
|
||||||
};
|
|
||||||
const FIELD_TO_CT_VAR = { tags: 'var_tags', unprivileged: 'var_unprivileged' };
|
const FIELD_TO_CT_VAR = { tags: 'var_tags', unprivileged: 'var_unprivileged' };
|
||||||
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' };
|
||||||
const METHOD_KEYS = { config_path: 'string', script: 'string' };
|
const METHOD_KEYS = { config_path: 'string', script: 'string' };
|
||||||
@@ -344,17 +339,6 @@ jobs:
|
|||||||
if (isNaN(n)) return { error: '`' + key + '` must be a number' };
|
if (isNaN(n)) return { error: '`' + key + '` must be a number' };
|
||||||
return { value: n };
|
return { value: n };
|
||||||
}
|
}
|
||||||
if (type === 'select_list') {
|
|
||||||
// architectures/platforms are multi-selects over a closed set,
|
|
||||||
// so an unknown value is rejected rather than stored. The model
|
|
||||||
// may hand back an array or a comma-separated string.
|
|
||||||
const allowed = SELECT_VALUES[key] || [];
|
|
||||||
const raw = Array.isArray(rawVal) ? rawVal : String(rawVal == null ? '' : rawVal).split(',');
|
|
||||||
const values = raw.map(function (v) { return String(v).trim(); }).filter(Boolean);
|
|
||||||
const bad = values.filter(function (v) { return allowed.indexOf(v) === -1; });
|
|
||||||
if (bad.length > 0) return { error: '`' + key + '` accepts ' + allowed.join(', ') + ' — got: ' + bad.join(', ') };
|
|
||||||
return { value: values };
|
|
||||||
}
|
|
||||||
if (type === 'nullable_string') return { value: rawVal === '' || rawVal == null ? null : String(rawVal) };
|
if (type === 'nullable_string') return { value: rawVal === '' || rawVal == null ? null : String(rawVal) };
|
||||||
return { value: String(rawVal) };
|
return { value: String(rawVal) };
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+6
-25
@@ -323,10 +323,9 @@ jobs:
|
|||||||
'/pocketbase <slug> method remove <type>\n' +
|
'/pocketbase <slug> method remove <type>\n' +
|
||||||
'```\n' +
|
'```\n' +
|
||||||
'Method fields: `cpu` `ram` `hdd` `os` `version` `config_path` `script`\n\n' +
|
'Method fields: `cpu` `ram` `hdd` `os` `version` `config_path` `script`\n\n' +
|
||||||
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `repository` ' +
|
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' +
|
||||||
'`config_path` `port` `default_user` `default_passwd` ' +
|
'`config_path` `port` `default_user` `default_passwd` ' +
|
||||||
'`updateable` `privileged` `is_dev` ' +
|
'`updateable` `privileged` `has_arm` `is_dev` ' +
|
||||||
'`architectures` (amd64,arm64) `platforms` (pve,incus) ' +
|
|
||||||
'`is_disabled` `disable_message` `is_deleted` `deleted_message`';
|
'`is_disabled` `disable_message` `is_deleted` `deleted_message`';
|
||||||
|
|
||||||
if (!withoutCmd) {
|
if (!withoutCmd) {
|
||||||
@@ -505,8 +504,7 @@ jobs:
|
|||||||
out.push('- **Port:** ' + (record.port != null ? '`' + record.port + '`' : '—'));
|
out.push('- **Port:** ' + (record.port != null ? '`' + record.port + '`' : '—'));
|
||||||
out.push('- **Updateable:** ' + (record.updateable ? 'Yes' : 'No'));
|
out.push('- **Updateable:** ' + (record.updateable ? 'Yes' : 'No'));
|
||||||
out.push('- **Privileged:** ' + (record.privileged ? 'Yes' : 'No'));
|
out.push('- **Privileged:** ' + (record.privileged ? 'Yes' : 'No'));
|
||||||
out.push('- **Architectures:** ' + ((record.architectures || []).join(', ') || 'amd64'));
|
out.push('- **ARM:** ' + (record.has_arm ? 'Yes' : 'No'));
|
||||||
out.push('- **Platforms:** ' + ((record.platforms || []).join(', ') || 'pve'));
|
|
||||||
if (record.is_dev) out.push('- **Dev:** Yes');
|
if (record.is_dev) out.push('- **Dev:** Yes');
|
||||||
if (record.is_disabled) out.push('- **Disabled:** Yes' + (record.disable_message ? ' — ' + record.disable_message : ''));
|
if (record.is_disabled) out.push('- **Disabled:** Yes' + (record.disable_message ? ' — ' + record.disable_message : ''));
|
||||||
if (record.is_deleted) out.push('- **Deleted:** Yes' + (record.deleted_message ? ' — ' + record.deleted_message : ''));
|
if (record.is_deleted) out.push('- **Deleted:** Yes' + (record.deleted_message ? ' — ' + record.deleted_message : ''));
|
||||||
@@ -840,7 +838,7 @@ jobs:
|
|||||||
const fieldName = setMatch[1].toLowerCase();
|
const fieldName = setMatch[1].toLowerCase();
|
||||||
const SET_ALLOWED = {
|
const SET_ALLOWED = {
|
||||||
name: 'string', description: 'string', logo: 'string',
|
name: 'string', description: 'string', logo: 'string',
|
||||||
documentation: 'string', website: 'string', project_url: 'string', repository: 'string',
|
documentation: 'string', website: 'string', project_url: 'string', github: 'string',
|
||||||
config_path: 'string', disable_message: 'string', deleted_message: 'string'
|
config_path: 'string', disable_message: 'string', deleted_message: 'string'
|
||||||
};
|
};
|
||||||
if (!SET_ALLOWED[fieldName]) {
|
if (!SET_ALLOWED[fieldName]) {
|
||||||
@@ -890,7 +888,7 @@ jobs:
|
|||||||
documentation: 'string',
|
documentation: 'string',
|
||||||
website: 'string',
|
website: 'string',
|
||||||
project_url: 'string',
|
project_url: 'string',
|
||||||
repository: 'string',
|
github: 'string',
|
||||||
config_path: 'string',
|
config_path: 'string',
|
||||||
tags: 'string',
|
tags: 'string',
|
||||||
port: 'number',
|
port: 'number',
|
||||||
@@ -899,8 +897,7 @@ jobs:
|
|||||||
unprivileged: 'number',
|
unprivileged: 'number',
|
||||||
updateable: 'boolean',
|
updateable: 'boolean',
|
||||||
privileged: 'boolean',
|
privileged: 'boolean',
|
||||||
architectures: 'select_list',
|
has_arm: 'boolean',
|
||||||
platforms: 'select_list',
|
|
||||||
is_dev: 'boolean',
|
is_dev: 'boolean',
|
||||||
is_disabled: 'boolean',
|
is_disabled: 'boolean',
|
||||||
disable_message: 'string',
|
disable_message: 'string',
|
||||||
@@ -927,10 +924,6 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cast values to correct types
|
// Cast values to correct types
|
||||||
const SELECT_VALUES = {
|
|
||||||
architectures: ['amd64', 'arm64'],
|
|
||||||
platforms: ['pve', 'incus'],
|
|
||||||
};
|
|
||||||
const payload = {};
|
const payload = {};
|
||||||
for (const [key, rawVal] of Object.entries(parsedFields)) {
|
for (const [key, rawVal] of Object.entries(parsedFields)) {
|
||||||
const type = ALLOWED_FIELDS[key];
|
const type = ALLOWED_FIELDS[key];
|
||||||
@@ -950,18 +943,6 @@ jobs:
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
payload[key] = n;
|
payload[key] = n;
|
||||||
} else if (type === 'select_list') {
|
|
||||||
// architectures/platforms are multi-selects over a closed set,
|
|
||||||
// so an unknown value is rejected rather than stored.
|
|
||||||
const allowed = SELECT_VALUES[key] || [];
|
|
||||||
const values = rawVal.split(',').map(function (v) { return v.trim(); }).filter(Boolean);
|
|
||||||
const bad = values.filter(function (v) { return allowed.indexOf(v) === -1; });
|
|
||||||
if (bad.length > 0) {
|
|
||||||
await addReaction('-1');
|
|
||||||
await postComment('❌ **PocketBase Bot**: `' + key + '` accepts ' + allowed.join(', ') + ' — got: `' + bad.join(', ') + '`');
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
payload[key] = values;
|
|
||||||
} else if (type === 'nullable_string') {
|
} else if (type === 'nullable_string') {
|
||||||
payload[key] = rawVal === '' ? null : rawVal;
|
payload[key] = rawVal === '' ? null : rawVal;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-13
@@ -179,19 +179,7 @@ jobs:
|
|||||||
if (resolvedType) payload.type = resolvedType;
|
if (resolvedType) payload.type = resolvedType;
|
||||||
var resolvedCats = (data.categories || []).map(function(n) { return categoryNameToPbId[categoryIdToName[n]]; }).filter(Boolean);
|
var resolvedCats = (data.categories || []).map(function(n) { return categoryNameToPbId[categoryIdToName[n]]; }).filter(Boolean);
|
||||||
if (resolvedCats.length) payload.categories = resolvedCats;
|
if (resolvedCats.length) payload.categories = resolvedCats;
|
||||||
// architectures replaces has_arm: a boolean could say "also ARM" but
|
if (data.has_arm !== undefined) payload.has_arm = data.has_arm === true || data.has_arm === 'true';
|
||||||
// not "ARM only" or "amd64 only". A record written before the field
|
|
||||||
// existed is read through the old key so nothing silently drops.
|
|
||||||
if (Array.isArray(data.architectures)) {
|
|
||||||
payload.architectures = data.architectures.filter(function (a) { return a === 'amd64' || a === 'arm64'; });
|
|
||||||
} else if (data.has_arm !== undefined) {
|
|
||||||
payload.architectures = (data.has_arm === true || data.has_arm === 'true') ? ['amd64', 'arm64'] : ['amd64'];
|
|
||||||
}
|
|
||||||
if (Array.isArray(data.platforms)) {
|
|
||||||
payload.platforms = data.platforms.filter(function (p) { return p === 'pve' || p === 'incus'; });
|
|
||||||
}
|
|
||||||
if (Array.isArray(data.app_vars)) payload.app_vars = data.app_vars;
|
|
||||||
if (data.repository !== undefined) payload.repository = data.repository;
|
|
||||||
if (data.version !== undefined) payload.version = data.version;
|
if (data.version !== undefined) payload.version = data.version;
|
||||||
if (data.changelog !== undefined) payload.changelog = data.changelog;
|
if (data.changelog !== undefined) payload.changelog = data.changelog;
|
||||||
if (data.screenshots !== undefined) payload.screenshots = data.screenshots;
|
if (data.screenshots !== undefined) payload.screenshots = data.screenshots;
|
||||||
|
|||||||
@@ -518,32 +518,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## 2026-08-07
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- core: extend new vars and pocketbase values [@MickLesk](https://github.com/MickLesk) ([#16313](https://github.com/community-scripts/ProxmoxVE/pull/16313))
|
|
||||||
|
|
||||||
## 2026-08-06
|
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- paperless: change update abort message (endless spinner) [@MickLesk](https://github.com/MickLesk) ([#16299](https://github.com/community-scripts/ProxmoxVE/pull/16299))
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- Vikunja: remove version pin, v2.5.0 fixes the systemd syscall filter crash [@MickLesk](https://github.com/MickLesk) ([#16300](https://github.com/community-scripts/ProxmoxVE/pull/16300))
|
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- build.func: filter templates by host architecture during search [@MickLesk](https://github.com/MickLesk) ([#16302](https://github.com/community-scripts/ProxmoxVE/pull/16302))
|
|
||||||
|
|
||||||
## 2026-08-05
|
## 2026-08-05
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
### 🚀 Updated Scripts
|
||||||
|
|||||||
@@ -103,55 +103,6 @@ Key rules at a glance:
|
|||||||
- Quote all variables: `"$VAR"` not `$VAR`
|
- Quote all variables: `"$VAR"` not `$VAR`
|
||||||
- Use lowercase variable names
|
- Use lowercase variable names
|
||||||
- Do not hardcode credentials or sensitive values
|
- Do not hardcode credentials or sensitive values
|
||||||
- Never prompt without an escape hatch — see below
|
|
||||||
|
|
||||||
### Answering a prompt up front
|
|
||||||
|
|
||||||
An install script that only asks cannot be deployed unattended. Read the
|
|
||||||
variable first and prompt only when it is unset:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [[ -z "${var_admin_user:-}" ]]; then
|
|
||||||
read -r -p "${TAB3}Admin username: " var_admin_user
|
|
||||||
fi
|
|
||||||
var_admin_user="${var_admin_user:-admin}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Name them `var_<something>`, the same namespace the container variables use.
|
|
||||||
`install/forgejo-runner-install.sh`, `install/pangolin-install.sh`, and `install/docker-install.sh` all
|
|
||||||
follow this.
|
|
||||||
|
|
||||||
The variable also has to be exported from `ct/<app>.sh`, or it never reaches
|
|
||||||
the container — `lxc-attach` carries the caller's environment, but only for
|
|
||||||
what was actually exported:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export var_admin_user="${var_admin_user:-}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Declare them on the script's PocketBase record in `app_vars` so the website's
|
|
||||||
generator can offer them as fields:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"app_vars": [
|
|
||||||
{
|
|
||||||
"name": "var_admin_user",
|
|
||||||
"label": "Admin Username",
|
|
||||||
"type": "text",
|
|
||||||
"default": "admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "var_admin_pass",
|
|
||||||
"label": "Admin Password",
|
|
||||||
"type": "password",
|
|
||||||
"secret": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
`type` is one of `text`, `password`, `number`, `boolean` (`yes`/`no`) or
|
|
||||||
`select` (with `options`). Mark anything credential-like `secret` — the
|
|
||||||
generator keeps those out of shareable links and out of the on-screen summary.
|
|
||||||
|
|
||||||
Full standards and examples: **[community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution)**
|
Full standards and examples: **[community-scripts.org/docs/contribution](https://community-scripts.org/docs/contribution)**
|
||||||
|
|
||||||
|
|||||||
@@ -42,20 +42,6 @@ function update_script() {
|
|||||||
echo -e "${TAB}${TAB}${GN}bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)\"${CL}"
|
echo -e "${TAB}${TAB}${GN}bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)\"${CL}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if docker ps -a --format '{{.Names}}' | grep -q '^portainer_agent$'; then
|
|
||||||
msg_info "Updating Portainer Agent"
|
|
||||||
$STD docker pull portainer/agent:latest
|
|
||||||
$STD docker stop portainer_agent
|
|
||||||
$STD docker rm portainer_agent
|
|
||||||
$STD docker run -d \
|
|
||||||
-p 9001:9001 \
|
|
||||||
--name=portainer_agent \
|
|
||||||
--restart=always \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
|
|
||||||
portainer/agent
|
|
||||||
msg_ok "Updated Portainer Agent"
|
|
||||||
fi
|
|
||||||
msg_ok "Updated successfully!"
|
msg_ok "Updated successfully!"
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ var_nesting="${var_nesting:-1}"
|
|||||||
var_keyctl="${var_keyctl:-1}"
|
var_keyctl="${var_keyctl:-1}"
|
||||||
|
|
||||||
export var_forgejo_instance="${var_forgejo_instance:-}"
|
export var_forgejo_instance="${var_forgejo_instance:-}"
|
||||||
export var_forgejo_runner_uuid="${var_forgejo_runner_uuid:-}"
|
|
||||||
export var_forgejo_runner_token="${var_forgejo_runner_token:-}"
|
export var_forgejo_runner_token="${var_forgejo_runner_token:-}"
|
||||||
export var_runner_labels="${var_runner_labels:-}"
|
export var_runner_labels="${var_runner_labels:-}"
|
||||||
|
|
||||||
@@ -65,10 +64,6 @@ if [[ -n "${mode:-}" ]]; then
|
|||||||
msg_error "var_forgejo_instance is required for unattended installs."
|
msg_error "var_forgejo_instance is required for unattended installs."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ -z "${var_forgejo_runner_uuid:-}" ]]; then
|
|
||||||
msg_error "var_forgejo_runner_uuid is required for unattended installs."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ -z "${var_forgejo_runner_token:-}" ]]; then
|
if [[ -z "${var_forgejo_runner_token:-}" ]]; then
|
||||||
msg_error "var_forgejo_runner_token is required for unattended installs."
|
msg_error "var_forgejo_runner_token is required for unattended installs."
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ var_arm64="${var_arm64:-yes}"
|
|||||||
var_unprivileged="${var_unprivileged:-1}"
|
var_unprivileged="${var_unprivileged:-1}"
|
||||||
var_tun="${var_tun:-1}"
|
var_tun="${var_tun:-1}"
|
||||||
|
|
||||||
export var_pangolin_url="${var_pangolin_url:-}"
|
|
||||||
export var_pangolin_email="${var_pangolin_email:-}"
|
|
||||||
|
|
||||||
header_info "$APP"
|
header_info "$APP"
|
||||||
variables
|
variables
|
||||||
color
|
color
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ function update_script() {
|
|||||||
read -rp "Do you want to continue with the update? (y/N): " MIGRATE
|
read -rp "Do you want to continue with the update? (y/N): " MIGRATE
|
||||||
echo
|
echo
|
||||||
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
|
if [[ ! "$MIGRATE" =~ ^[Yy]$ ]]; then
|
||||||
msg_custom "⚠️" "Update aborted. Decrypt all documents before upgrading to v3."
|
msg_info "Update aborted. Decrypt all documents before upgrading to v3."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
+3
-2
@@ -46,7 +46,8 @@ function update_script() {
|
|||||||
[[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0
|
[[ "$CONFIRM2" =~ ^[yY]$ ]] || exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if check_for_gh_release "vikunja" "go-vikunja/vikunja"; then
|
RELEASE="v2.3.0"
|
||||||
|
if check_for_gh_release "vikunja" "go-vikunja/vikunja" "${RELEASE}" "v2.4.0 is killed at startup by the systemd SystemCallFilter shipped in the .deb (upstream go-vikunja/vikunja#3252); pinned until a fixed release is out"; then
|
||||||
echo
|
echo
|
||||||
msg_warn "The package update may include config file changes."
|
msg_warn "The package update may include config file changes."
|
||||||
echo -e "${TAB}${YW}How do you want to handle /etc/vikunja/config.yml?${CL}"
|
echo -e "${TAB}${YW}How do you want to handle /etc/vikunja/config.yml?${CL}"
|
||||||
@@ -65,7 +66,7 @@ function update_script() {
|
|||||||
systemctl stop vikunja
|
systemctl stop vikunja
|
||||||
msg_ok "Stopped Service"
|
msg_ok "Stopped Service"
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
|
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "${RELEASE}" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
|
||||||
$STD systemctl daemon-reload
|
$STD systemctl daemon-reload
|
||||||
|
|
||||||
msg_info "Starting Service"
|
msg_info "Starting Service"
|
||||||
|
|||||||
@@ -27,18 +27,9 @@ fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VER
|
|||||||
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
|
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
|
||||||
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
|
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
|
||||||
|
|
||||||
# Read the variable first and prompt only when it is unset, so the install can
|
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
|
||||||
# be supplied up front. Same convention as install/forgejo-runner-install.sh.
|
|
||||||
pango_url="${var_pangolin_url:-}"
|
|
||||||
if [[ -z "$pango_url" ]]; then
|
|
||||||
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
|
|
||||||
fi
|
|
||||||
[[ "$pango_url" != https://* && "$pango_url" != http://* ]] && pango_url="https://${pango_url}"
|
[[ "$pango_url" != https://* && "$pango_url" != http://* ]] && pango_url="https://${pango_url}"
|
||||||
|
read -rp "${TAB3}Enter your email address: " pango_email
|
||||||
pango_email="${var_pangolin_email:-}"
|
|
||||||
if [[ -z "$pango_email" ]]; then
|
|
||||||
read -rp "${TAB3}Enter your email address: " pango_email
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg_info "Setup Pangolin"
|
msg_info "Setup Pangolin"
|
||||||
SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)
|
SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "latest" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
|
RELEASE="v2.3.0"
|
||||||
|
fetch_and_deploy_gh_release "vikunja" "go-vikunja/vikunja" "binary" "${RELEASE}" "" "vikunja-*-$(arch_resolve "x86_64" "aarch64").deb"
|
||||||
|
|
||||||
msg_info "Setting up Vikunja"
|
msg_info "Setting up Vikunja"
|
||||||
sed -i 's|^# \(service:\)|\1|' /etc/vikunja/config.yml
|
sed -i 's|^# \(service:\)|\1|' /etc/vikunja/config.yml
|
||||||
|
|||||||
+3
-8
@@ -922,10 +922,8 @@ choose_and_set_storage_for_file() {
|
|||||||
_list_os_versions() {
|
_list_os_versions() {
|
||||||
local ostype="${1:-}"
|
local ostype="${1:-}"
|
||||||
[[ -n "$ostype" ]] || return 0
|
[[ -n "$ostype" ]] || return 0
|
||||||
local arch
|
|
||||||
arch="$(dpkg --print-architecture 2>/dev/null || echo amd64)"
|
|
||||||
pveam available -section system 2>/dev/null |
|
pveam available -section system 2>/dev/null |
|
||||||
grep -E "_${arch}\.(tar\.zst|tar\.xz|tar\.gz)([[:space:]]|$)" |
|
grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
|
||||||
awk '{print $2}' |
|
awk '{print $2}' |
|
||||||
sed -nE "s/^${ostype}-([0-9]+(\.[0-9]+)?).*/\1/p" |
|
sed -nE "s/^${ostype}-([0-9]+(\.[0-9]+)?).*/\1/p" |
|
||||||
sort -u -V || true
|
sort -u -V || true
|
||||||
@@ -938,10 +936,8 @@ _list_os_versions() {
|
|||||||
_list_templates() {
|
_list_templates() {
|
||||||
local search="${1:-}" pattern="${2:-}"
|
local search="${1:-}" pattern="${2:-}"
|
||||||
[[ -n "$search" ]] || return 0
|
[[ -n "$search" ]] || return 0
|
||||||
local arch
|
|
||||||
arch="$(dpkg --print-architecture 2>/dev/null || echo amd64)"
|
|
||||||
pveam available -section system 2>/dev/null |
|
pveam available -section system 2>/dev/null |
|
||||||
grep -E "_${arch}\.(tar\.zst|tar\.xz|tar\.gz)([[:space:]]|$)" |
|
grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
|
||||||
awk '{print $2}' |
|
awk '{print $2}' |
|
||||||
grep -E "^${search}.*${pattern}" |
|
grep -E "^${search}.*${pattern}" |
|
||||||
sort -t - -k 2 -V || true
|
sort -t - -k 2 -V || true
|
||||||
@@ -6725,8 +6721,7 @@ create_lxc_container() {
|
|||||||
# Step 1: Check local templates first (instant)
|
# Step 1: Check local templates first (instant)
|
||||||
mapfile -t LOCAL_TEMPLATES < <(
|
mapfile -t LOCAL_TEMPLATES < <(
|
||||||
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
|
pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
|
||||||
awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" -v arch="${ARCH}" \
|
awk -v search="${TEMPLATE_SEARCH}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' |
|
||||||
'$1 ~ search && $1 ~ pattern && $1 ~ ("_" arch "\\.(tar\\.zst|tar\\.xz|tar\\.gz)$") {print $1}' |
|
|
||||||
sed 's|.*/||' | sort -t - -k 2 -V
|
sed 's|.*/||' | sort -t - -k 2 -V
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+33
-61
@@ -2576,6 +2576,11 @@ _deploy_unpacked_archive() {
|
|||||||
local archive="$1" target="$2" workdir="$3"
|
local archive="$1" target="$2" workdir="$3"
|
||||||
local filename="${archive##*/}"
|
local filename="${archive##*/}"
|
||||||
|
|
||||||
|
mkdir -p "$target"
|
||||||
|
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
|
||||||
|
find "${target:?}" -mindepth 1 -delete
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$filename" == *.zip ]]; then
|
if [[ "$filename" == *.zip ]]; then
|
||||||
ensure_dependencies unzip
|
ensure_dependencies unzip
|
||||||
unzip -q "$archive" -d "$workdir" || {
|
unzip -q "$archive" -d "$workdir" || {
|
||||||
@@ -2602,44 +2607,38 @@ _deploy_unpacked_archive() {
|
|||||||
return 65
|
return 65
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local top_entries inner_dir source_dir
|
local top_entries inner_dir
|
||||||
top_entries=$(find "$workdir" -mindepth 1 -maxdepth 1)
|
top_entries=$(find "$workdir" -mindepth 1 -maxdepth 1)
|
||||||
if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then
|
if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then
|
||||||
inner_dir="$top_entries"
|
inner_dir="$top_entries"
|
||||||
source_dir="$inner_dir"
|
shopt -s dotglob nullglob
|
||||||
else
|
if compgen -G "$inner_dir/*" >/dev/null; then
|
||||||
source_dir="$workdir"
|
cp -r "$inner_dir"/* "$target/" || {
|
||||||
fi
|
msg_error "Failed to copy contents from $inner_dir to $target"
|
||||||
|
shopt -u dotglob nullglob
|
||||||
shopt -s dotglob nullglob
|
return 252
|
||||||
if ! compgen -G "$source_dir/*" >/dev/null; then
|
}
|
||||||
if [[ -n "$inner_dir" ]]; then
|
else
|
||||||
msg_error "Inner directory is empty: $inner_dir"
|
msg_error "Inner directory is empty: $inner_dir"
|
||||||
|
shopt -u dotglob nullglob
|
||||||
|
return 252
|
||||||
|
fi
|
||||||
|
shopt -u dotglob nullglob
|
||||||
|
else
|
||||||
|
shopt -s dotglob nullglob
|
||||||
|
if compgen -G "$workdir/*" >/dev/null; then
|
||||||
|
cp -r "$workdir"/* "$target/" || {
|
||||||
|
msg_error "Failed to copy contents to $target"
|
||||||
|
shopt -u dotglob nullglob
|
||||||
|
return 252
|
||||||
|
}
|
||||||
else
|
else
|
||||||
msg_error "Unpacked archive is empty"
|
msg_error "Unpacked archive is empty"
|
||||||
|
shopt -u dotglob nullglob
|
||||||
|
return 252
|
||||||
fi
|
fi
|
||||||
shopt -u dotglob nullglob
|
shopt -u dotglob nullglob
|
||||||
return 252
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# <target> is only touched once the payload is known good. Wiping earlier left
|
|
||||||
# CLEAN_INSTALL callers with an empty target directory whenever the download
|
|
||||||
# was truncated, the archive was unreadable, or it unpacked to nothing.
|
|
||||||
mkdir -p "$target"
|
|
||||||
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
|
|
||||||
find "${target:?}" -mindepth 1 -delete
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! cp -r "$source_dir"/* "$target/"; then
|
|
||||||
if [[ -n "$inner_dir" ]]; then
|
|
||||||
msg_error "Failed to copy contents from $inner_dir to $target"
|
|
||||||
else
|
|
||||||
msg_error "Failed to copy contents to $target"
|
|
||||||
fi
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
return 252
|
|
||||||
fi
|
|
||||||
shopt -u dotglob nullglob
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4802,8 +4801,8 @@ EOF
|
|||||||
|
|
||||||
# Container Update Check
|
# Container Update Check
|
||||||
# - Skipped entirely when running unattended / non-interactive
|
# - Skipped entirely when running unattended / non-interactive
|
||||||
# - Compose-managed containers: offered via Y/N → docker compose pull + up -d
|
# - Lists containers with a newer image available and the command to update
|
||||||
# - Standalone containers: offered via Y/N → image pull only (no stop/rm)
|
# them; does not pull/recreate anything itself
|
||||||
if [ "$docker_installed" = true ] && ! _docker_is_noninteractive; then
|
if [ "$docker_installed" = true ] && ! _docker_is_noninteractive; then
|
||||||
msg_info "Checking for container updates"
|
msg_info "Checking for container updates"
|
||||||
|
|
||||||
@@ -4841,41 +4840,14 @@ EOF
|
|||||||
if [[ ${#compose_updates[@]} -eq 0 && ${#standalone_updates[@]} -eq 0 ]]; then
|
if [[ ${#compose_updates[@]} -eq 0 && ${#standalone_updates[@]} -eq 0 ]]; then
|
||||||
msg_ok "All containers are up-to-date"
|
msg_ok "All containers are up-to-date"
|
||||||
else
|
else
|
||||||
local reply
|
msg_warn "Updates available (not applied automatically):"
|
||||||
for entry in "${compose_updates[@]}"; do
|
for entry in "${compose_updates[@]}"; do
|
||||||
IFS='|' read -r name image compose_workdir compose_service <<<"$entry"
|
IFS='|' read -r name image compose_workdir compose_service <<<"$entry"
|
||||||
reply=""
|
echo -e "${TAB3}${name} (${image}) — update via: cd ${compose_workdir} && docker compose pull ${compose_service} && docker compose up -d ${compose_service}"
|
||||||
if read -r -t 60 -p "${TAB3}Update ${name} (${image}) via Compose? <y/N> (auto-no in 60s): " reply </dev/tty; then
|
|
||||||
echo ""
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
if [[ "${reply,,}" =~ ^(y|yes)$ ]]; then
|
|
||||||
msg_info "Updating $name"
|
|
||||||
if (cd "$compose_workdir" && $STD docker compose pull "$compose_service" && $STD docker compose up -d "$compose_service"); then
|
|
||||||
msg_ok "Updated $name"
|
|
||||||
else
|
|
||||||
msg_warn "Could not update $name — try manually in $compose_workdir"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
for entry in "${standalone_updates[@]}"; do
|
for entry in "${standalone_updates[@]}"; do
|
||||||
IFS='|' read -r name image <<<"$entry"
|
IFS='|' read -r name image <<<"$entry"
|
||||||
reply=""
|
echo -e "${TAB3}${name} (${image}) — update via: docker pull ${image} && docker restart ${name}"
|
||||||
if read -r -t 60 -p "${TAB3}Pull new image for ${name} (${image})? <y/N> (auto-no in 60s): " reply </dev/tty; then
|
|
||||||
echo ""
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
if [[ "${reply,,}" =~ ^(y|yes)$ ]]; then
|
|
||||||
msg_info "Pulling new image for $name"
|
|
||||||
if $STD docker pull "$image"; then
|
|
||||||
msg_ok "New image available for $name — recreate the container to apply the update"
|
|
||||||
else
|
|
||||||
msg_warn "Failed to pull image for $name"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -49,6 +49,22 @@ function uninstall() {
|
|||||||
msg_ok "${APP} has been uninstalled"
|
msg_ok "${APP} has been uninstalled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# UPDATE SCRIPT HELPER
|
||||||
|
# ==============================================================================
|
||||||
|
function ensure_update_script() {
|
||||||
|
[[ -f /usr/local/bin/update_portainer ]] && return 0
|
||||||
|
|
||||||
|
msg_info "Creating update script"
|
||||||
|
cat <<'UPDATEEOF' >/usr/local/bin/update_portainer
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Portainer Update Script
|
||||||
|
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)"
|
||||||
|
UPDATEEOF
|
||||||
|
chmod +x /usr/local/bin/update_portainer
|
||||||
|
msg_ok "Created update script (/usr/local/bin/update_portainer)"
|
||||||
|
}
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# UPDATE
|
# UPDATE
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -62,6 +78,8 @@ function update() {
|
|||||||
$STD docker compose up -d --remove-orphans
|
$STD docker compose up -d --remove-orphans
|
||||||
msg_ok "Restarted ${APP}"
|
msg_ok "Restarted ${APP}"
|
||||||
|
|
||||||
|
ensure_update_script
|
||||||
|
|
||||||
msg_ok "Updated successfully"
|
msg_ok "Updated successfully"
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
@@ -115,15 +133,7 @@ function install() {
|
|||||||
$STD docker compose up -d
|
$STD docker compose up -d
|
||||||
msg_ok "Started ${APP}"
|
msg_ok "Started ${APP}"
|
||||||
|
|
||||||
# Create update script
|
ensure_update_script
|
||||||
msg_info "Creating update script"
|
|
||||||
cat <<'UPDATEEOF' >/usr/local/bin/update_portainer
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Portainer Update Script
|
|
||||||
type=update bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/portainer.sh)"
|
|
||||||
UPDATEEOF
|
|
||||||
chmod +x /usr/local/bin/update_portainer
|
|
||||||
msg_ok "Created update script (/usr/local/bin/update_portainer)"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
msg_ok "${APP} is reachable at: ${BL}https://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
|
msg_ok "${APP} is reachable at: ${BL}https://${LOCAL_IP}:${DEFAULT_PORT}${CL}"
|
||||||
@@ -150,6 +160,9 @@ fi
|
|||||||
header_info
|
header_info
|
||||||
get_lxc_ip
|
get_lxc_ip
|
||||||
|
|
||||||
|
check_docker
|
||||||
|
check_existing_container
|
||||||
|
|
||||||
# Check if already installed
|
# Check if already installed
|
||||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||||
msg_warn "${APP} is already installed."
|
msg_warn "${APP} is already installed."
|
||||||
|
|||||||
@@ -125,6 +125,12 @@ EOF
|
|||||||
no) msg_error "Selected no to Correcting Proxmox VE Sources" ;;
|
no) msg_error "Selected no to Correcting Proxmox VE Sources" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
if [[ "$(dpkg --print-architecture 2>/dev/null)" == "arm64" ]]; then
|
||||||
|
msg_ok "ARM64 detected - skipping Proxmox repository setup"
|
||||||
|
post_routines_common
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pve-enterprise' repository?" 14 58 2 \
|
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pve-enterprise' repository?" 14 58 2 \
|
||||||
"yes" " " \
|
"yes" " " \
|
||||||
"no" " " 3>&2 2>&1 1>&3)
|
"no" " " 3>&2 2>&1 1>&3)
|
||||||
@@ -282,6 +288,12 @@ EOF
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$(dpkg --print-architecture 2>/dev/null)" == "arm64" ]]; then
|
||||||
|
msg_ok "ARM64 detected - skipping Proxmox repository setup"
|
||||||
|
post_routines_common
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
# ---- PVE-ENTERPRISE ----
|
# ---- PVE-ENTERPRISE ----
|
||||||
if component_exists_in_sources "pve-enterprise"; then
|
if component_exists_in_sources "pve-enterprise"; then
|
||||||
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
|
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
|
||||||
|
|||||||
Reference in New Issue
Block a user