mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-14 10:47:36 +00:00
preserve Helper-Scripts updater detection after updates
This commit is contained in:
@@ -615,7 +615,20 @@ _helpers_cache_lock = threading.RLock()
|
|||||||
_helpers_cache: Optional[dict] = None
|
_helpers_cache: Optional[dict] = None
|
||||||
_helpers_cache_ts: float = 0.0
|
_helpers_cache_ts: float = 0.0
|
||||||
|
|
||||||
_UPDATE_SLUG_RE = re.compile(r"ct/([a-z0-9._-]+)\.sh")
|
_HELPER_SLUG_VALUE = r"[a-z0-9][a-z0-9._-]*"
|
||||||
|
_UPDATE_SLUG_RE = re.compile(rf"ct/({_HELPER_SLUG_VALUE})\.sh")
|
||||||
|
_SCRIPT_SLUG_RE = re.compile(
|
||||||
|
rf"^\s*(?:export\s+)?SCRIPT_SLUG\s*=\s*"
|
||||||
|
rf'(?:"({_HELPER_SLUG_VALUE})"|\'({_HELPER_SLUG_VALUE})\'|({_HELPER_SLUG_VALUE}))'
|
||||||
|
rf"\s*(?:#.*)?$",
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
|
_UPDATE_SCRIPT_NAME_RE = re.compile(
|
||||||
|
rf"^\s*(?:export\s+)?UPDATE_SCRIPT_NAME\s*=\s*"
|
||||||
|
rf'(?:"({_HELPER_SLUG_VALUE})"|\'({_HELPER_SLUG_VALUE})\'|({_HELPER_SLUG_VALUE}))'
|
||||||
|
rf"\s*(?:#.*)?$",
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
_BASE_OS_HELPER_SLUGS = frozenset({
|
_BASE_OS_HELPER_SLUGS = frozenset({
|
||||||
"alpine", "archlinux", "archlinux-vm", "debian", "fedora",
|
"alpine", "archlinux", "archlinux-vm", "debian", "fedora",
|
||||||
"gentoo", "opensuse", "ubuntu",
|
"gentoo", "opensuse", "ubuntu",
|
||||||
@@ -690,15 +703,29 @@ def _fetch_helpers_cache() -> dict:
|
|||||||
return _helpers_cache
|
return _helpers_cache
|
||||||
|
|
||||||
|
|
||||||
def _probe_helper_scripts_slug(vmid: str) -> Optional[str]:
|
def _extract_helper_slug_from_update_wrapper(content: str) -> Optional[str]:
|
||||||
"""Return the community-scripts app slug for a CT by extracting the
|
"""Extract a static app slug from a Helper-Scripts update wrapper.
|
||||||
``ct/<slug>.sh`` reference embedded in ``/usr/bin/update``.
|
|
||||||
|
|
||||||
The community-scripts installers write ``/usr/bin/update`` as a
|
Historical wrappers contain a literal ``ct/<slug>.sh`` URL. Current
|
||||||
single line: ``bash -c "$(curl -fsSL …/ct/<slug>.sh)"``. Parsing
|
wrappers are regenerated after successful updates and declare
|
||||||
that URL gives us both the app identity AND a stable key into
|
``SCRIPT_SLUG`` / ``UPDATE_SCRIPT_NAME`` before constructing that URL
|
||||||
:func:`_fetch_helpers_cache`. Returns None when the file is
|
with shell variables. Only a plain, tightly constrained assignment is
|
||||||
missing, unreadable, or doesn't match the expected pattern.
|
accepted; the wrapper is never evaluated or sourced.
|
||||||
|
"""
|
||||||
|
for pattern in (_SCRIPT_SLUG_RE, _UPDATE_SCRIPT_NAME_RE):
|
||||||
|
match = pattern.search(content or "")
|
||||||
|
if match:
|
||||||
|
return next((value for value in match.groups() if value), None)
|
||||||
|
match = _UPDATE_SLUG_RE.search(content or "")
|
||||||
|
return match.group(1) if match else None
|
||||||
|
|
||||||
|
|
||||||
|
def _probe_helper_scripts_slug(vmid: str) -> Optional[str]:
|
||||||
|
"""Return the Helper-Scripts app slug declared by ``/usr/bin/update``.
|
||||||
|
|
||||||
|
Supports both the historical literal URL and the current generated
|
||||||
|
entrypoint format. Returns None when the file is missing, unreadable,
|
||||||
|
or contains no safe static application slug.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
r = subprocess.run(
|
r = subprocess.run(
|
||||||
@@ -708,8 +735,7 @@ def _probe_helper_scripts_slug(vmid: str) -> Optional[str]:
|
|||||||
)
|
)
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
return None
|
return None
|
||||||
m = _UPDATE_SLUG_RE.search(r.stdout)
|
return _extract_helper_slug_from_update_wrapper(r.stdout)
|
||||||
return m.group(1) if m else None
|
|
||||||
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# BACKUP_STORAGE — PVE storage name for vzdump (required when BACKUP=1)
|
# BACKUP_STORAGE — PVE storage name for vzdump (required when BACKUP=1)
|
||||||
# RESTART — "1" to `pct reboot` after update, "0" to skip
|
# RESTART — "1" to `pct reboot` after update, "0" to skip
|
||||||
# RUN_HELPER — "1" to run the verified community-scripts
|
# RUN_HELPER — "1" to run the verified community-scripts
|
||||||
# updater referenced by /usr/bin/update, "0"
|
# updater declared by /usr/bin/update, "0"
|
||||||
# to leave it alone. Never inferred from names.
|
# to leave it alone. Never inferred from names.
|
||||||
# UPDATE_COMMAND — optional user-defined bash string. When set
|
# UPDATE_COMMAND — optional user-defined bash string. When set
|
||||||
# and TARGET is "app" or "both", the script
|
# and TARGET is "app" or "both", the script
|
||||||
@@ -82,6 +82,36 @@ if [[ "$UPDATE_DOCKER_ENGINE" != "0" && "$UPDATE_DOCKER_ENGINE" != "1" ]]; then
|
|||||||
exit 5
|
exit 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Resolve only static, constrained metadata from a Proxmox VE
|
||||||
|
# Helper-Scripts update entrypoint. Historical wrappers contain a literal
|
||||||
|
# ct/<slug>.sh URL; current wrappers are regenerated after updates and
|
||||||
|
# declare SCRIPT_SLUG / UPDATE_SCRIPT_NAME before composing the URL.
|
||||||
|
# Never source or evaluate the wrapper: its contents belong to the CT.
|
||||||
|
extract_helper_slug_from_wrapper() {
|
||||||
|
local wrapper="$1"
|
||||||
|
local key slug
|
||||||
|
|
||||||
|
for key in SCRIPT_SLUG UPDATE_SCRIPT_NAME; do
|
||||||
|
slug=$(printf '%s\n' "$wrapper" | sed -nE \
|
||||||
|
"s/^[[:space:]]*(export[[:space:]]+)?${key}[[:space:]]*=[[:space:]]*[\"']?([a-z0-9][a-z0-9._-]*)[\"']?[[:space:]]*(#.*)?$/\\2/p" \
|
||||||
|
| head -n 1)
|
||||||
|
if [[ "$slug" =~ ^[a-z0-9][a-z0-9._-]*$ ]]; then
|
||||||
|
printf '%s\n' "$slug"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
slug=$(printf '%s\n' "$wrapper" \
|
||||||
|
| grep -oE 'https?://[^"'"'"' ]+ct/[a-z0-9][a-z0-9._-]*\.sh' \
|
||||||
|
| head -n 1 \
|
||||||
|
| sed -nE 's|.*/ct/([a-z0-9][a-z0-9._-]*)\.sh$|\1|p')
|
||||||
|
if [[ "$slug" =~ ^[a-z0-9][a-z0-9._-]*$ ]]; then
|
||||||
|
printf '%s\n' "$slug"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# One update per CT at a time, regardless of whether it came from the
|
# One update per CT at a time, regardless of whether it came from the
|
||||||
# UI or the scheduler. The descriptor remains open for this process.
|
# UI or the scheduler. The descriptor remains open for this process.
|
||||||
LOCK_DIR="${PROXMENUX_LOCK_DIR:-/run/lock}"
|
LOCK_DIR="${PROXMENUX_LOCK_DIR:-/run/lock}"
|
||||||
@@ -197,8 +227,9 @@ fi
|
|||||||
|
|
||||||
# 6) Application update. Explicit methods only:
|
# 6) Application update. Explicit methods only:
|
||||||
# a) RUN_HELPER=1 + a valid /usr/bin/update wrapper →
|
# a) RUN_HELPER=1 + a valid /usr/bin/update wrapper →
|
||||||
# parses the ct/<slug>.sh URL from the wrapper, canonicalises it
|
# parses its static slug (legacy URL or current generated
|
||||||
# to the official repository, then runs the current helper
|
# SCRIPT_SLUG format), canonicalises it to the official repository,
|
||||||
|
# then runs the current helper
|
||||||
# inside the CT with PHS_SILENT=1.
|
# inside the CT with PHS_SILENT=1.
|
||||||
# b) UPDATE_COMMAND set → run it verbatim via `sh -c`
|
# b) UPDATE_COMMAND set → run it verbatim via `sh -c`
|
||||||
# inside the CT. The one intentional shell-exec-with-variable
|
# inside the CT. The one intentional shell-exec-with-variable
|
||||||
@@ -222,11 +253,12 @@ if [[ "$TARGET" == "app" || "$TARGET" == "both" ]]; then
|
|||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
if [[ "$RUN_HELPER" == "1" ]]; then
|
if [[ "$RUN_HELPER" == "1" ]]; then
|
||||||
|
UPDATE_WRAPPER=""
|
||||||
UPDATE_URL=""
|
UPDATE_URL=""
|
||||||
RESOLVED_SLUG=""
|
RESOLVED_SLUG=""
|
||||||
if pct exec "$VMID" -- test -f /usr/bin/update 2>/dev/null; then
|
if pct exec "$VMID" -- test -f /usr/bin/update 2>/dev/null; then
|
||||||
UPDATE_URL=$(pct exec "$VMID" -- cat /usr/bin/update 2>/dev/null | grep -oE 'https?://[^"'"'"' ]+ct/[a-zA-Z0-9._-]+\.sh' | head -1)
|
UPDATE_WRAPPER=$(pct exec "$VMID" -- cat /usr/bin/update 2>/dev/null)
|
||||||
RESOLVED_SLUG=$(echo "$UPDATE_URL" | sed -nE 's|.*/ct/([a-zA-Z0-9._-]+)\.sh$|\1|p')
|
RESOLVED_SLUG=$(extract_helper_slug_from_wrapper "$UPDATE_WRAPPER")
|
||||||
fi
|
fi
|
||||||
case "$RESOLVED_SLUG" in
|
case "$RESOLVED_SLUG" in
|
||||||
alpine|archlinux|archlinux-vm|debian|fedora|gentoo|opensuse|ubuntu)
|
alpine|archlinux|archlinux-vm|debian|fedora|gentoo|opensuse|ubuntu)
|
||||||
@@ -237,7 +269,7 @@ if [[ "$TARGET" == "app" || "$TARGET" == "both" ]]; then
|
|||||||
esac
|
esac
|
||||||
if [[ -z "$RESOLVED_SLUG" ]]; then
|
if [[ -z "$RESOLVED_SLUG" ]]; then
|
||||||
if [[ "$APP_FAILED" -eq 0 ]]; then
|
if [[ "$APP_FAILED" -eq 0 ]]; then
|
||||||
echo "ERROR: RUN_HELPER=1 but /usr/bin/update contains no valid community-scripts app reference." >&2
|
echo "ERROR: RUN_HELPER=1 but /usr/bin/update contains no valid Proxmox VE Helper-Scripts app slug." >&2
|
||||||
APP_FAILED=1
|
APP_FAILED=1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user