Compare commits

..

8 Commits

Author SHA1 Message Date
Michel Roegl-Brunner 07f8147568 add configurable host CA inheritance for LXC bootstrap
Introduce host CA certificate propagation in the shared LXC build flow so containers can trust enterprise/private PKI roots during early package bootstrap. Add an advanced-install toggle with default auto behavior so unattended installs remain seamless while interactive users can explicitly opt out.
2026-07-17 09:56:36 +02:00
community-scripts-pr-app[bot] 3f90af2a21 Update CHANGELOG.md (#15839)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-17 07:00:56 +00:00
Chris 55002839fb Pin Opencloud to v7.3.0 (#15826) 2026-07-17 09:00:32 +02:00
community-scripts-pr-app[bot] a51e1f37f5 Update CHANGELOG.md (#15836)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-17 06:43:31 +00:00
soupy-boy 8655282c2d autoremove and autoclean after apt full-upgrade (#15831) 2026-07-17 08:43:05 +02:00
Michel Roegl-Brunner e15db754a6 github: close PRs that do not follow the PR template
Add a workflow that validates description, prerequisites, and type-of-change checkboxes, with exemptions for bots, maintainers, and the keep open label.
2026-07-16 11:46:06 +02:00
community-scripts-pr-app[bot] 772430de7e Update CHANGELOG.md (#15818)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-16 08:28:51 +00:00
community-scripts-pr-app[bot] 04a84f5052 Update CHANGELOG.md (#15816)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sam Heinz <sam@samheinz.com>
2026-07-16 10:28:25 +02:00
8 changed files with 321 additions and 13 deletions
+163
View File
@@ -0,0 +1,163 @@
name: Close PRs Missing Template
on:
pull_request_target:
branches: ["main"]
types: [opened, edited, reopened, synchronize, labeled]
jobs:
validate-pr-template:
if: github.repository == 'community-scripts/ProxmoxVE'
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
contents: read
steps:
- name: Close PR if it does not follow the PR template
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const prNumber = pr.number;
const author = pr.user.login;
const owner = context.repo.owner;
const repo = context.repo.repo;
const allowedBots = [
"push-app-to-main[bot]",
"push-app-to-main",
"community-scripts-pr-app",
"github-actions[bot]",
"dependabot[bot]",
];
if (allowedBots.includes(author) || author.endsWith("[bot]")) {
core.info(`PR #${prNumber} by bot "${author}" — skipping template validation.`);
return;
}
const association = pr.author_association;
const exemptAssociations = ["OWNER", "MEMBER", "COLLABORATOR"];
if (exemptAssociations.includes(association)) {
core.info(`PR #${prNumber} by ${association} "${author}" — skipping template validation.`);
return;
}
const labels = 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.`);
return;
}
if (pr.draft) {
core.info(`PR #${prNumber} is a draft — skipping template validation.`);
return;
}
const body = pr.body || "";
const failures = [];
const requiredSections = [
"## ✍️ Description",
"## ✅ Prerequisites",
"## 🛠️ Type of Change",
];
for (const section of requiredSections) {
if (!body.includes(section)) {
failures.push(`Missing required section: \`${section}\``);
}
}
const descriptionMatch = body.match(
/## ✍️ Description\s*\n+([\s\S]*?)(?=\n## )/i
);
const description = (descriptionMatch?.[1] || "").trim();
if (!description) {
failures.push("The **Description** section is empty.");
}
const prerequisiteCheckboxes = [
"**Self-review completed**",
"**Tested thoroughly**",
"**No security risks**",
];
for (const checkbox of prerequisiteCheckboxes) {
const escaped = checkbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1");
const regex = new RegExp(`- \\[(x|X)\\]\\s*${escaped}`, "i");
if (!regex.test(body)) {
failures.push(`Prerequisite not checked: ${checkbox}`);
}
}
const typeOfChangeCheckboxes = [
"🐞 **Bug fix**",
"✨ **New feature**",
"💥 **Breaking change**",
"🆕 **New script**",
"🌍 **Website update**",
"🔧 **Refactoring / Code Cleanup**",
"📝 **Documentation update**",
];
const hasTypeChecked = typeOfChangeCheckboxes.some((checkbox) => {
const escaped = checkbox.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1");
const regex = new RegExp(`- \\[(x|X)\\]\\s*${escaped}`, "i");
return regex.test(body);
});
if (!hasTypeChecked) {
failures.push("At least one **Type of Change** checkbox must be checked.");
}
if (failures.length === 0) {
core.info(`PR #${prNumber} follows the PR template.`);
return;
}
core.info(`Closing PR #${prNumber} — template validation failed.`);
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");
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: comment,
});
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"],
});
+16 -1
View File
@@ -502,11 +502,26 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-07-17
### 🚀 Updated Scripts
- #### ✨ New Features
- Pin Opencloud to v7.3.0 [@vhsdream](https://github.com/vhsdream) ([#15826](https://github.com/community-scripts/ProxmoxVE/pull/15826))
### 🧰 Tools
- #### ✨ New Features
- update-lxc: autoremove and autoclean after apt full-upgrade [@soupy-boy](https://github.com/soupy-boy) ([#15831](https://github.com/community-scripts/ProxmoxVE/pull/15831))
## 2026-07-16
### 🆕 New Scripts
- Beaverhabits ([#15813](https://github.com/community-scripts/ProxmoxVE/pull/15813))
- Sync-In ([#15812](https://github.com/community-scripts/ProxmoxVE/pull/15812))
- Beaverhabits ([#15813](https://github.com/community-scripts/ProxmoxVE/pull/15813))
- Notediscovery ([#15811](https://github.com/community-scripts/ProxmoxVE/pull/15811))
### 🚀 Updated Scripts
+1 -1
View File
@@ -45,7 +45,7 @@ EOF
systemctl daemon-reload
fi
$STD npm install -g n8n@latest
$STD npm install -g n8n@2.27.5
systemctl restart n8n
msg_ok "Updated n8n"
msg_ok "Updated successfully!"
+1 -1
View File
@@ -30,7 +30,7 @@ function update_script() {
exit
fi
RELEASE="v7.2.2"
RELEASE="v7.3.0"
if check_for_gh_release "OpenCloud" "opencloud-eu/opencloud" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
msg_info "Stopping services"
systemctl stop opencloud opencloud-wopi
+2 -1
View File
@@ -16,6 +16,7 @@ update_os
msg_info "Installing Dependencies"
$STD apt install -y \
build-essential \
python3 \
python3-setuptools \
graphicsmagick
msg_ok "Installed Dependencies"
@@ -23,7 +24,7 @@ msg_ok "Installed Dependencies"
NODE_VERSION="24" setup_nodejs
msg_info "Installing n8n (Patience)"
$STD npm install -g n8n@latest
$STD npm install -g n8n@2.27.5
msg_ok "Installed n8n"
msg_info "Creating Service"
+1 -1
View File
@@ -64,7 +64,7 @@ $STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPA
echo "$COOLPASS" >~/.coolpass
msg_ok "Installed Collabora Online"
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.2.2" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.3.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
mv /usr/bin/OpenCloud /usr/bin/opencloud
msg_info "Configuring OpenCloud"
+136 -7
View File
@@ -1009,6 +1009,7 @@ base_settings() {
APT_CACHER=${var_apt_cacher:-""}
APT_CACHER_IP=${var_apt_cacher_ip:-""}
INHERIT_HOST_CA="${var_inherit_host_ca:-auto}"
# Runtime check: Verify APT cacher is reachable if configured
if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then
@@ -1088,7 +1089,7 @@ load_vars_file() {
# Allowed var_* keys
local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1285,6 +1286,12 @@ load_vars_file() {
continue
fi
;;
var_inherit_host_ca)
if [[ "$var_val" != "yes" && "$var_val" != "no" && "$var_val" != "auto" ]]; then
msg_warn "Invalid host CA inheritance value '$var_val' in $file (must be yes/no/auto), ignoring"
continue
fi
;;
var_container_storage | var_template_storage)
# Validate that the storage exists and is active on the current node
local _storage_status
@@ -1324,7 +1331,7 @@ default_var_settings() {
# Allowed var_* keys (alphabetically sorted)
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
local VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
@@ -1407,6 +1414,7 @@ var_ssh=no
# HTTP/HTTPS proxy (optional - for networks requiring a proxy)
# var_http_proxy=http://proxy.local:8080
# var_http_no_proxy=localhost,127.0.0.1,.local
# var_inherit_host_ca=auto
# Features/Tags/verbosity
var_fuse=no
@@ -1507,7 +1515,7 @@ get_app_defaults_path() {
if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
declare -ag VAR_WHITELIST=(
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_keyctl
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_http_no_proxy var_http_proxy var_inherit_host_ca var_keyctl
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1657,6 +1665,7 @@ _build_current_app_vars_tmp() {
_apt_cacher_ip="${APT_CACHER_IP:-}"
_http_proxy="${HTTP_PROXY:-${var_http_proxy:-}}"
_http_no_proxy="${HTTP_NO_PROXY:-${var_http_no_proxy:-}}"
_inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-auto}}"
_fuse="${ENABLE_FUSE:-no}"
_tun="${ENABLE_TUN:-no}"
_gpu="${ENABLE_GPU:-no}"
@@ -1710,6 +1719,7 @@ _build_current_app_vars_tmp() {
[ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")"
[ -n "$_http_proxy" ] && echo "var_http_proxy=$(_sanitize_value "$_http_proxy")"
[ -n "$_http_no_proxy" ] && echo "var_http_no_proxy=$(_sanitize_value "$_http_no_proxy")"
[ -n "$_inherit_host_ca" ] && echo "var_inherit_host_ca=$(_sanitize_value "$_inherit_host_ca")"
[ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")"
[ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")"
@@ -1874,7 +1884,7 @@ advanced_settings() {
TAGS="community-script${var_tags:+;${var_tags}}"
fi
local STEP=1
local MAX_STEP=30
local MAX_STEP=31
# Store values for back navigation - inherit from var_* app defaults
local _ct_type="${var_unprivileged:-1}"
@@ -1896,6 +1906,7 @@ advanced_settings() {
local _apt_cacher_ip="${var_apt_cacher_ip:-}"
local _http_proxy="${var_http_proxy:-}"
local _http_no_proxy="${var_http_no_proxy:-}"
local _inherit_host_ca="${var_inherit_host_ca:-auto}"
local _mtu="${var_mtu:-}"
local _sd="${var_searchdomain:-}"
local _ns="${var_ns:-}"
@@ -2725,9 +2736,47 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 25: Container Timezone
# STEP 25: Host CA Inheritance
# ═══════════════════════════════════════════════════════════════════════════
25)
local host_ca_count=0
local host_ca_dir="/usr/local/share/ca-certificates"
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_ca_count=$((host_ca_count + 1))
done
shopt -u nullglob
if [[ $host_ca_count -eq 0 ]]; then
_inherit_host_ca="auto"
((STEP++))
continue
fi
local host_ca_default_flag=""
[[ "$_inherit_host_ca" == "no" ]] && host_ca_default_flag="--defaultno"
if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
--title "HOST CA INHERITANCE" \
--ok-button "Next" --cancel-button "Back" \
$host_ca_default_flag \
--yesno "\nInherit host CA certificates into this container?\n\nDetected on host: ${host_ca_count} certificate(s) in:\n${host_ca_dir}\n\nRecommended for private PKI / TLS-inspection environments.\n\n(App default: ${var_inherit_host_ca:-auto})" 16 72; then
_inherit_host_ca="yes"
else
if [ $? -eq 1 ]; then
_inherit_host_ca="no"
else
((STEP--))
continue
fi
fi
((STEP++))
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 26: Container Timezone
# ═══════════════════════════════════════════════════════════════════════════
26)
local tz_hint="$_ct_timezone"
[[ -z "$tz_hint" ]] && tz_hint="(empty - will use host timezone)"
@@ -2750,9 +2799,9 @@ advanced_settings() {
;;
# ═══════════════════════════════════════════════════════════════════════════
# STEP 26: Container Protection
# STEP 27: Container Protection
# ═══════════════════════════════════════════════════════════════════════════
26)
27)
local protect_default_flag="--defaultno"
[[ "$_protect_ct" == "yes" || "$_protect_ct" == "1" ]] && protect_default_flag=""
@@ -2904,6 +2953,7 @@ Leave empty to skip."
local apt_display="${_apt_cacher:-no}"
[[ "$_apt_cacher" == "yes" && -n "$_apt_cacher_ip" ]] && apt_display="$_apt_cacher_ip"
local http_proxy_display="${_http_proxy:-(none)}"
local inherit_ca_display="${_inherit_host_ca:-auto}"
local post_install_display="${_post_install:-(none)}"
local post_install_warn=""
@@ -2934,6 +2984,7 @@ Advanced:
Timezone: $tz_display
APT Cacher: $apt_display
HTTP Proxy: $http_proxy_display
Inherit Host CAs: $inherit_ca_display
Verbose: $_verbose
Post-Install Script: ${post_install_display}${post_install_warn}"
@@ -2979,6 +3030,7 @@ Advanced:
APT_CACHER_IP="$_apt_cacher_ip"
HTTP_PROXY="$_http_proxy"
HTTP_NO_PROXY="$_http_no_proxy"
INHERIT_HOST_CA="$_inherit_host_ca"
VERBOSE="$_verbose"
var_post_install="$_post_install"
@@ -2997,6 +3049,7 @@ Advanced:
var_sdn_vnet="$_sdn_vnet"
var_http_proxy="$_http_proxy"
var_http_no_proxy="$_http_no_proxy"
var_inherit_host_ca="$_inherit_host_ca"
# Format optional values
[[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU=""
@@ -3922,6 +3975,81 @@ EOF
msg_ok "Applied HTTP proxy in container"
}
# ------------------------------------------------------------------------------
# _apply_host_ca_certs_in_container()
#
# - Copies administrator-provided CA certificates from the Proxmox host into the
# container before base package bootstrap
# - Source: /usr/local/share/ca-certificates/*.crt (Debian convention)
# - Refreshes the container trust store when update-ca-certificates is available
# - No-op when no host certificates are present; failures are non-fatal
# ------------------------------------------------------------------------------
_apply_host_ca_certs_in_container() {
local host_ca_dir="/usr/local/share/ca-certificates"
[[ -z "${CTID:-}" ]] && return 0
local inherit_host_ca="${INHERIT_HOST_CA:-${var_inherit_host_ca:-auto}}"
local -a host_certs=()
local cert
shopt -s nullglob
for cert in "$host_ca_dir"/*.crt; do
host_certs+=("$cert")
done
shopt -u nullglob
[[ ${#host_certs[@]} -eq 0 ]] && return 0
case "${inherit_host_ca,,}" in
no | false | 0 | off)
msg_info "Skipping host CA inheritance by configuration"
return 0
;;
esac
msg_info "Inheriting host CA certificates into container"
local found=${#host_certs[@]}
local copied=0
local skipped=0
local cert_name
pct exec "$CTID" -- mkdir -p /usr/local/share/ca-certificates >/dev/null 2>&1 || {
msg_warn "Failed to create CA certificate directory in container"
return 0
}
for cert in "${host_certs[@]}"; do
cert_name="$(basename "$cert")"
if [[ ! -r "$cert" || "$cert_name" != *.crt ]]; then
msg_warn "Skipping invalid or unreadable host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
continue
fi
if pct push "$CTID" "$cert" "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1; then
pct exec "$CTID" -- chmod 644 "/usr/local/share/ca-certificates/${cert_name}" >/dev/null 2>&1 || true
copied=$((copied + 1))
else
msg_warn "Failed to push host CA certificate: ${cert_name}"
skipped=$((skipped + 1))
fi
done
if [[ $copied -eq 0 ]]; then
msg_warn "No host CA certificates were copied (${found} found, ${skipped} skipped)"
return 0
fi
local refresh_shell="bash"
[[ "$var_os" == "alpine" ]] && refresh_shell="ash"
if pct exec "$CTID" -- "$refresh_shell" -c 'command -v update-ca-certificates >/dev/null 2>&1 && update-ca-certificates' >/dev/null 2>&1; then
msg_ok "Inherited ${copied} host CA certificate(s) and updated trust store (${skipped} skipped)"
else
msg_warn "Copied ${copied} host CA certificate(s), but trust store update failed or update-ca-certificates is unavailable (${skipped} skipped)"
fi
}
# ------------------------------------------------------------------------------
# build_container()
#
@@ -4542,6 +4670,7 @@ EOF
local install_exit_code=0
_apply_http_proxy_in_container
_apply_host_ca_certs_in_container
# Continue with standard container setup
if [ "$var_os" == "alpine" ]; then
+1 -1
View File
@@ -78,7 +78,7 @@ function update_container() {
alpine) pct exec "$container" -- ash -c "apk -U upgrade" ;;
archlinux) pct exec "$container" -- bash -c "pacman -Syyu --noconfirm" ;;
fedora | rocky | centos | alma) pct exec "$container" -- bash -c "dnf -y update && dnf -y upgrade" ;;
ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update 2>/dev/null | grep 'packages.*upgraded'; apt list --upgradable 2>/dev/null | cat && apt-get -yq dist-upgrade 2>&1; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED || true" ;;
ubuntu | debian | devuan) pct exec "$container" -- bash -c "apt-get update 2>/dev/null | grep 'packages.*upgraded'; apt list --upgradable 2>/dev/null | cat && apt-get -yq dist-upgrade 2>&1; apt-get -yq autoremove 2>&1; apt-get -yq autoclean 2>&1; rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED || true" ;;
opensuse) pct exec "$container" -- bash -c "zypper ref && zypper --non-interactive dup" ;;
esac
}