feat(os): profils Proxmox/RPi + machine_probe + proxy persistent (tâche 2 SJ-7)

- templates proxmox/ (update-analyze: dépôts PVE ; full-upgrade) et raspbian/
  (update-analyze: espace disque ; full-upgrade)
- execute résout les actions APT par profil OS (resolveTemplate) → proxmox/
  raspbian si dispo, sinon fallback apt/ (non-régression debian/ubuntu vérifiée)
- machine_probe (lecture seule) : template + parseProbe/proposeCorrections (TDD)
  → propose os_family/machine_kind/virtualization, persiste machine_hardware,
  n'applique jamais auto ; branche execute + allowlist route
- apt_proxy_persistent : ActionType + template idempotent (/etc/apt/apt.conf.d/
  01proxy, backup) + TemplateVars.aptProxyUrl + allowlist route

tsc 0 · 95 tests · build OK · résolution OS vérifiée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 07:14:43 +02:00
parent b5ec14dcd8
commit bafb085995
13 changed files with 490 additions and 15 deletions
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
# Proxy APT persistant : écrit /etc/apt/apt.conf.d/01proxy (idempotent, sauvegarde l'existant).
# Action explicite (écriture disque). aptProxyUrl est fourni par le backend (jamais un secret).
export LC_ALL=C
CONF=/etc/apt/apt.conf.d/01proxy
echo "===SU:PROXY_BEFORE==="
[ -f "$CONF" ] && cat "$CONF" || echo "ABSENT"
echo "===SU:PROXY_WRITE==="
{{#aptProxyUrl}}
# Sauvegarde horodatée si le fichier existe déjà.
[ -f "$CONF" ] && cp -a "$CONF" "${CONF}.bak.$(date +%Y%m%d%H%M%S)" && echo "BACKUP=1"
printf 'Acquire::http::Proxy "%s";\nAcquire::https::Proxy "%s";\n' "{{aptProxyUrl}}" "{{aptProxyUrl}}" > "$CONF"
CODE=$?
echo "WROTE=$CONF"
{{/aptProxyUrl}}
{{^aptProxyUrl}}
echo "NO_PROXY_URL"
CODE=2
{{/aptProxyUrl}}
echo "===SU:PROXY_AFTER==="
cat "$CONF" 2>/dev/null || echo "ABSENT"
echo "===SU:EXIT=${CODE}==="
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
# Sonde lecture seule : OS, arch, virtualisation, Proxmox/RPi, GPU, réseau.
# Aucune écriture. Le backend propose des corrections (jamais appliquées sans validation).
export LC_ALL=C
echo "===SU:PROBE_OS==="
cat /etc/os-release 2>/dev/null
echo "===SU:PROBE_ARCH==="
uname -m
dpkg --print-architecture 2>/dev/null
echo "===SU:PROBE_VIRT==="
systemd-detect-virt 2>/dev/null || echo "none"
echo "===SU:PROBE_PROXMOX==="
[ -d /etc/pve ] && echo "PROXMOX=1" || echo "PROXMOX=0"
echo "===SU:PROBE_RPI==="
grep -qi raspberry /proc/cpuinfo 2>/dev/null && echo "RPI=1" || echo "RPI=0"
echo "===SU:PROBE_GPU==="
command -v lspci >/dev/null 2>&1 && lspci 2>/dev/null | grep -Ei 'vga|3d|display' || echo "no-lspci"
echo "===SU:PROBE_NET==="
ip -o -4 addr show 2>/dev/null | awk '{print $2, $4}'
echo "===SU:EXIT=0==="
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
# Proxmox VE : dist-upgrade (kernel PVE, proxmox-ve, Ceph). Capture diff dpkg.
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
{{#aptProxy}}export http_proxy="{{aptProxy}}"; export https_proxy="{{aptProxy}}"
{{/aptProxy}}
echo "===SU:DPKG_BEFORE==="
dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null
echo "===SU:APT_FULLUPGRADE==="
apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold dist-upgrade 2>&1
CODE=$?
echo "===SU:DPKG_AFTER==="
dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null
echo "===SU:REBOOT==="
if [ -f /run/reboot-required ] || [ -f /var/run/reboot-required ]; then echo "REBOOT_REQUIRED=1"; else echo "REBOOT_REQUIRED=0"; fi
echo "===SU:EXIT=${CODE}==="
+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
# Proxmox VE : refresh index + simulations + held + reboot-check + état des dépôts PVE.
# Non destructif. Exécuté entier sous sudo par la couche SSH.
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
{{#aptProxy}}export http_proxy="{{aptProxy}}"; export https_proxy="{{aptProxy}}"
{{/aptProxy}}
echo "===SU:PVE_REPOS==="
# Détecte le dépôt entreprise actif sans abonnement (cause classique d'échec apt update).
grep -RhsE '^[^#]*deb .*enterprise\.proxmox\.com' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null \
| sed 's/^/ENTERPRISE_REPO=/' || true
grep -RhsE '^[^#]*deb .*download\.proxmox\.com.*pve-no-subscription' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null \
| sed 's/^/NOSUB_REPO=/' || true
echo "===SU:APT_UPDATE==="
apt-get update -qq 2>&1
UPD=$?
echo "===SU:APT_SIM_UPGRADE==="
apt-get -s -y upgrade 2>&1
echo "===SU:APT_SIM_DISTUPGRADE==="
apt-get -s -y dist-upgrade 2>&1
echo "===SU:APT_HELD==="
apt-mark showhold 2>/dev/null
echo "===SU:REBOOT==="
if [ -f /run/reboot-required ] || [ -f /var/run/reboot-required ]; then
echo "REBOOT_REQUIRED=1"
[ -f /var/run/reboot-required.pkgs ] && sed 's/^/PKG=/' /var/run/reboot-required.pkgs
else
echo "REBOOT_REQUIRED=0"
fi
echo "===SU:EXIT=${UPD}==="
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# Raspberry Pi OS : full-upgrade (apt) après contrôle d'espace disque. Capture diff dpkg.
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
{{#aptProxy}}export http_proxy="{{aptProxy}}"; export https_proxy="{{aptProxy}}"
{{/aptProxy}}
echo "===SU:DISK==="
df -Pk / 2>/dev/null | awk 'NR==2{print "ROOT_AVAIL_KB="$4"\nROOT_USE_PCT="$5}'
echo "===SU:DPKG_BEFORE==="
dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null
echo "===SU:APT_FULLUPGRADE==="
apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold full-upgrade 2>&1
CODE=$?
echo "===SU:DPKG_AFTER==="
dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null
echo "===SU:REBOOT==="
if [ -f /run/reboot-required ] || [ -f /var/run/reboot-required ]; then echo "REBOOT_REQUIRED=1"; else echo "REBOOT_REQUIRED=0"; fi
echo "===SU:EXIT=${CODE}==="
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
# Raspberry Pi OS : refresh + simulations + held + reboot-check + espace disque (carte SD).
# Non destructif. rpi-update volontairement NON utilisé (risqué).
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
{{#aptProxy}}export http_proxy="{{aptProxy}}"; export https_proxy="{{aptProxy}}"
{{/aptProxy}}
echo "===SU:DISK==="
# Espace libre sur / en Ko (carte SD souvent petite) → le backend peut avertir avant upgrade.
df -Pk / 2>/dev/null | awk 'NR==2{print "ROOT_AVAIL_KB="$4"\nROOT_USE_PCT="$5}'
echo "===SU:APT_UPDATE==="
apt-get update -qq 2>&1
UPD=$?
echo "===SU:APT_SIM_UPGRADE==="
apt-get -s -y upgrade 2>&1
echo "===SU:APT_SIM_DISTUPGRADE==="
apt-get -s -y dist-upgrade 2>&1
echo "===SU:APT_HELD==="
apt-mark showhold 2>/dev/null
echo "===SU:REBOOT==="
if [ -f /run/reboot-required ] || [ -f /var/run/reboot-required ]; then
echo "REBOOT_REQUIRED=1"
[ -f /var/run/reboot-required.pkgs ] && sed 's/^/PKG=/' /var/run/reboot-required.pkgs
else
echo "REBOOT_REQUIRED=0"
fi
echo "===SU:EXIT=${UPD}==="