mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-08-06 21:03:25 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 78977d2d11 | |||
| ff8fdc853e | |||
| 35735daae6 | |||
| 2a560f909a | |||
| 16195a29fb | |||
| 5ddc4a2a41 | |||
| 43305ae097 |
Generated
+18
-2
@@ -314,12 +314,17 @@ jobs:
|
||||
// ── Allow-lists (mirror the slash bot) ─────────────────────────────
|
||||
const ALLOWED_FIELDS = {
|
||||
name: 'string', description: 'string', logo: 'string', documentation: 'string',
|
||||
website: 'string', project_url: 'string', github: 'string', config_path: 'string',
|
||||
website: 'string', project_url: 'string', repository: 'string', config_path: 'string',
|
||||
tags: 'string', port: 'number', default_user: 'nullable_string', default_passwd: 'nullable_string',
|
||||
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean', has_arm: 'boolean',
|
||||
unprivileged: 'number', updateable: 'boolean', privileged: 'boolean',
|
||||
architectures: 'select_list', platforms: 'select_list',
|
||||
is_dev: 'boolean', is_disabled: 'boolean', disable_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 RESOURCE_KEYS = { cpu: 'number', ram: 'number', hdd: 'number', os: 'string', version: 'string' };
|
||||
const METHOD_KEYS = { config_path: 'string', script: 'string' };
|
||||
@@ -339,6 +344,17 @@ jobs:
|
||||
if (isNaN(n)) return { error: '`' + key + '` must be a number' };
|
||||
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) };
|
||||
return { value: String(rawVal) };
|
||||
}
|
||||
|
||||
Generated
+25
-6
@@ -323,9 +323,10 @@ jobs:
|
||||
'/pocketbase <slug> method remove <type>\n' +
|
||||
'```\n' +
|
||||
'Method fields: `cpu` `ram` `hdd` `os` `version` `config_path` `script`\n\n' +
|
||||
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' +
|
||||
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `repository` ' +
|
||||
'`config_path` `port` `default_user` `default_passwd` ' +
|
||||
'`updateable` `privileged` `has_arm` `is_dev` ' +
|
||||
'`updateable` `privileged` `is_dev` ' +
|
||||
'`architectures` (amd64,arm64) `platforms` (pve,incus) ' +
|
||||
'`is_disabled` `disable_message` `is_deleted` `deleted_message`';
|
||||
|
||||
if (!withoutCmd) {
|
||||
@@ -504,7 +505,8 @@ jobs:
|
||||
out.push('- **Port:** ' + (record.port != null ? '`' + record.port + '`' : '—'));
|
||||
out.push('- **Updateable:** ' + (record.updateable ? 'Yes' : 'No'));
|
||||
out.push('- **Privileged:** ' + (record.privileged ? 'Yes' : 'No'));
|
||||
out.push('- **ARM:** ' + (record.has_arm ? 'Yes' : 'No'));
|
||||
out.push('- **Architectures:** ' + ((record.architectures || []).join(', ') || 'amd64'));
|
||||
out.push('- **Platforms:** ' + ((record.platforms || []).join(', ') || 'pve'));
|
||||
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_deleted) out.push('- **Deleted:** Yes' + (record.deleted_message ? ' — ' + record.deleted_message : ''));
|
||||
@@ -838,7 +840,7 @@ jobs:
|
||||
const fieldName = setMatch[1].toLowerCase();
|
||||
const SET_ALLOWED = {
|
||||
name: 'string', description: 'string', logo: 'string',
|
||||
documentation: 'string', website: 'string', project_url: 'string', github: 'string',
|
||||
documentation: 'string', website: 'string', project_url: 'string', repository: 'string',
|
||||
config_path: 'string', disable_message: 'string', deleted_message: 'string'
|
||||
};
|
||||
if (!SET_ALLOWED[fieldName]) {
|
||||
@@ -888,7 +890,7 @@ jobs:
|
||||
documentation: 'string',
|
||||
website: 'string',
|
||||
project_url: 'string',
|
||||
github: 'string',
|
||||
repository: 'string',
|
||||
config_path: 'string',
|
||||
tags: 'string',
|
||||
port: 'number',
|
||||
@@ -897,7 +899,8 @@ jobs:
|
||||
unprivileged: 'number',
|
||||
updateable: 'boolean',
|
||||
privileged: 'boolean',
|
||||
has_arm: 'boolean',
|
||||
architectures: 'select_list',
|
||||
platforms: 'select_list',
|
||||
is_dev: 'boolean',
|
||||
is_disabled: 'boolean',
|
||||
disable_message: 'string',
|
||||
@@ -924,6 +927,10 @@ jobs:
|
||||
}
|
||||
|
||||
// Cast values to correct types
|
||||
const SELECT_VALUES = {
|
||||
architectures: ['amd64', 'arm64'],
|
||||
platforms: ['pve', 'incus'],
|
||||
};
|
||||
const payload = {};
|
||||
for (const [key, rawVal] of Object.entries(parsedFields)) {
|
||||
const type = ALLOWED_FIELDS[key];
|
||||
@@ -943,6 +950,18 @@ jobs:
|
||||
process.exit(0);
|
||||
}
|
||||
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') {
|
||||
payload[key] = rawVal === '' ? null : rawVal;
|
||||
} else {
|
||||
|
||||
+13
-1
@@ -179,7 +179,19 @@ jobs:
|
||||
if (resolvedType) payload.type = resolvedType;
|
||||
var resolvedCats = (data.categories || []).map(function(n) { return categoryNameToPbId[categoryIdToName[n]]; }).filter(Boolean);
|
||||
if (resolvedCats.length) payload.categories = resolvedCats;
|
||||
if (data.has_arm !== undefined) payload.has_arm = data.has_arm === true || data.has_arm === 'true';
|
||||
// architectures replaces has_arm: a boolean could say "also ARM" but
|
||||
// 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.changelog !== undefined) payload.changelog = data.changelog;
|
||||
if (data.screenshots !== undefined) payload.screenshots = data.screenshots;
|
||||
|
||||
@@ -103,6 +103,55 @@ Key rules at a glance:
|
||||
- Quote all variables: `"$VAR"` not `$VAR`
|
||||
- Use lowercase variable names
|
||||
- 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)**
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var_nesting="${var_nesting:-1}"
|
||||
var_keyctl="${var_keyctl:-1}"
|
||||
|
||||
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_runner_labels="${var_runner_labels:-}"
|
||||
|
||||
@@ -64,6 +65,10 @@ if [[ -n "${mode:-}" ]]; then
|
||||
msg_error "var_forgejo_instance is required for unattended installs."
|
||||
exit 1
|
||||
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
|
||||
msg_error "var_forgejo_runner_token is required for unattended installs."
|
||||
exit 1
|
||||
|
||||
@@ -17,6 +17,9 @@ var_arm64="${var_arm64:-yes}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
var_tun="${var_tun:-1}"
|
||||
|
||||
export var_pangolin_url="${var_pangolin_url:-}"
|
||||
export var_pangolin_email="${var_pangolin_email:-}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
|
||||
@@ -27,9 +27,18 @@ 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 "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
|
||||
|
||||
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
|
||||
# Read the variable first and prompt only when it is unset, so the install can
|
||||
# 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}"
|
||||
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"
|
||||
SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32)
|
||||
|
||||
Reference in New Issue
Block a user