# 10 — Templates APT : inventaire, sémantique et pseudo-shell > Axe A + livrables §4.1 et §4.2. Cohérent avec `templates/apt/check.sh.tpl`, `full-upgrade.sh.tpl`, `reboot.sh.tpl` existants, la convention `===SU:XXX===`, `LC_ALL=C`, `DEBIAN_FRONTEND=noninteractive`, exécution sous `sudo -S` (`server/ssh/client.ts`). --- ## 1. Sémantique APT clarifiée (manpage `apt-get`) | Commande | Effet | Peut supprimer ? | Peut installer du nouveau ? | |---|---|---|---| | `apt-get update` | Resynchronise les index de paquets. Ne modifie aucun paquet installé. | non | non | | `apt-get -s upgrade` | **Simulation** : installe les nouvelles versions des paquets installés **sans jamais supprimer** ni installer de nouveaux paquets. Les paquets dont l'upgrade exigerait une suppression/installation restent *held back*. | non | non | | `apt-get -s dist-upgrade` / `full-upgrade` | **Simulation** : gère intelligemment les changements de dépendances, peut **installer de nouveaux paquets et en supprimer** pour satisfaire les dépendances. | oui | oui | | `apt-get autoremove` | Retire les dépendances automatiquement installées et devenues inutiles. | oui | non | | `apt-get clean` | Vide le cache local `/var/cache/apt/archives`. N'affecte pas l'état des paquets. | non | non | > `apt full-upgrade` (commande `apt`) ≡ `apt-get dist-upgrade` (commande `apt-get`). **On utilise toujours `apt-get` en script** (non interactif, stable), jamais `apt` (UI humaine). L'UI parle d'« full-upgrade » comme alias convivial ; la commande système est `apt-get dist-upgrade`. Lignes documentées parsées : `Inst [] ( [])`, `Conf `, `Remv `. Le log brut complet reste archivé ; seules ces lignes + `E:`/`W:`/`dpkg:`/`reboot-required` alimentent Hermes. Sources : `apt-get` https://manpages.debian.org/apt-get · `dpkg` https://manpages.debian.org/dpkg · `dpkg-query` https://manpages.debian.org/dpkg-query · `apt-listchanges` https://manpages.debian.org/bookworm/apt-listchanges/apt-listchanges.1.en.html · `needrestart` https://manpages.debian.org/bookworm/needrestart/needrestart.1.en.html --- ## 2. Inventaire des templates APT | Template | Action (`ActionType`) | Rôle | Type | OS ciblés | Destructif ? | Marqueurs | |---|---|---|---|---|---|---| | `apt/update-analyze.sh.tpl` | `apt_update_analyze` | Refresh index + simulation `upgrade` et `dist-upgrade` + reboot-check. **Tâche de fond, non destructif.** Remplace/étend l'actuel `check.sh.tpl`. | snapshot | tous | non | `===SU:APT_UPDATE===`, `===SU:APT_SIM_UPGRADE===`, `===SU:APT_SIM_DISTUPGRADE===`, `===SU:APT_HELD===`, `===SU:REBOOT===`, `===SU:EXIT=N===` | | `apt/upgrade.sh.tpl` | `apt_upgrade` | Applique l'upgrade simple (sans suppression volontaire). Snapshot dpkg avant/après. | action | tous | oui (modif paquets) | `===SU:DPKG_BEFORE===`, `===SU:APT_UPGRADE===`, `===SU:DPKG_AFTER===`, `===SU:REBOOT===`, `===SU:EXIT=N===` | | `apt/full-upgrade.sh.tpl` | `apt_full_upgrade` | Applique `apt-get dist-upgrade`. **Conserve l'existant (jalon 1) en l'enrichissant du diff dpkg.** | action | tous | oui (peut supprimer) | idem upgrade + `===SU:APT_FULLUPGRADE===` | | `apt/autoremove.sh.tpl` | `apt_autoremove` | Retire les dépendances inutiles, avec simulation préalable affichée. | action | tous | oui (supprime) | `===SU:APT_SIM_AUTOREMOVE===`, `===SU:DPKG_BEFORE===`, `===SU:APT_AUTOREMOVE===`, `===SU:DPKG_AFTER===`, `===SU:EXIT=N===` | | `apt/clean.sh.tpl` | `apt_clean` | Vide le cache des paquets téléchargés. Action séparée, peu risquée. | action | tous | non (cache only) | `===SU:APT_CLEAN===`, `===SU:EXIT=N===` | | `apt/reboot-check.sh.tpl` | (intégré au refresh) | Vérifie `/run/reboot-required`, `/var/run/reboot-required`, liste `reboot-required.pkgs`, état `needrestart -b`. | snapshot | tous | non | `===SU:REBOOT===` | | `apt/reboot.sh.tpl` | `reboot_verified` | Lit `boot_id` avant, planifie le reboot, émet un marqueur parsable avant coupure SSH. Conserve l'existant en ajoutant `boot_id`. | action | tous | oui (reboot) | `===SU:BOOT_ID_BEFORE===`, `===SU:REBOOT_NOW===` | > **Compatibilité jalon 1** : `apt_full_upgrade` et `reboot` restent des actions valides. `check.sh.tpl` n'est pas supprimé ; `update-analyze.sh.tpl` est son successeur enrichi (le refresh peut basculer dessus sans casser le parsing existant — voir §6 ci-dessous). --- ## 3. Variables de rendu (Mustache) Étend `TemplateVars` (extension proposée, voir `40-contrats-json.md`). Variables utilisées par les templates APT : ```text aptProxy string|null proxy apt-cacher-ng injecté à l'exécution (mode runtime) osProfile string debian | ubuntu | proxmox | raspbian machineKind string physical | vm | proxmox_host | lxc | raspberry_pi | workstation confValues bool true => --force-confdef --force-confold (défaut) inactivityTimeout int secondes; 0 = désactivé (défaut 600) ``` Les sections Mustache `{{#aptProxy}}…{{/aptProxy}}` restent identiques à l'existant. --- ## 4. Pseudo-shell des templates clés ### 4.1 `apt/update-analyze.sh.tpl` (refresh + analyse, non destructif) ```sh #!/bin/sh # Refresh index + simulations upgrade/dist-upgrade + reboot-check. # Exécuté entier sous sudo par la couche SSH. Non destructif. export LC_ALL=C export DEBIAN_FRONTEND=noninteractive {{#aptProxy}}export http_proxy="{{aptProxy}}"; export https_proxy="{{aptProxy}}" {{/aptProxy}} 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===" # Paquets retenus (held back) : présents en dist-upgrade mais pas en upgrade. 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 # needrestart en mode batch/list si présent (jamais interactif). command -v needrestart >/dev/null 2>&1 && needrestart -b 2>/dev/null | grep -E '^NEEDRESTART-(KSTA|SVC)' || true echo "===SU:EXIT=${UPD}===" ``` Le backend parse : - section `APT_SIM_UPGRADE` → liste `upgrade` (paquets `Inst`), - section `APT_SIM_DISTUPGRADE` → liste `dist-upgrade` (inclut `Inst` nouveaux + `Remv` suppressions), - `held` = `APT_HELD` (et/ou paquets présents en dist-upgrade absents d'upgrade), - `REBOOT_REQUIRED` + `PKG=` → reboot requis + paquets concernés. Statut snapshot APT : `ok` si rien ; `updates_available` si `Inst` non vides ; `warning` si dist-upgrade implique des `Remv` ou des `held` ; `error` si `APT_UPDATE` échoue (dépôt injoignable, clé GPG…). ### 4.2 `apt/full-upgrade.sh.tpl` (application, avec diff dpkg) ```sh #!/bin/sh 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}===" ``` Le backend compare `DPKG_BEFORE` et `DPKG_AFTER` (clé = `package+arch`) → `installed` / `upgraded` / `removed` / `unchanged`, versions finales réelles. **L'exit code seul ne suffit pas** : un paquet annoncé en simulation mais resté inchangé est signalé comme anomalie. > Politique non interactive justifiée : `--force-confdef`+`--force-confold` conservent les fichiers de config locaux quand dpkg ne peut pas trancher, pour ne pas écraser une configuration distante. Les prompts (conffile, debconf, apt-listchanges, needrestart) sont traités comme **risques de blocage à détecter** (timeout d'inactivité), pas comme dialogues à exposer. Voir `50-erreurs.md` (`human_interaction_required`). ### 4.3 `apt/autoremove.sh.tpl` ```sh #!/bin/sh export LC_ALL=C export DEBIAN_FRONTEND=noninteractive echo "===SU:APT_SIM_AUTOREMOVE===" apt-get -s -y autoremove 2>&1 # prévisualisation des Remv echo "===SU:DPKG_BEFORE===" dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null echo "===SU:APT_AUTOREMOVE===" apt-get -y autoremove 2>&1 CODE=$? echo "===SU:DPKG_AFTER===" dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n' 2>/dev/null echo "===SU:EXIT=${CODE}===" ``` Confirmation UI explicite requise (action qui supprime des paquets). ### 4.4 `apt/clean.sh.tpl` ```sh #!/bin/sh export LC_ALL=C echo "===SU:APT_CLEAN===" BEFORE=$(du -sb /var/cache/apt/archives 2>/dev/null | awk '{print $1}') apt-get clean 2>&1 AFTER=$(du -sb /var/cache/apt/archives 2>/dev/null | awk '{print $1}') echo "FREED_BYTES=$((BEFORE - AFTER))" echo "===SU:EXIT=0===" ``` ### 4.5 `apt/reboot.sh.tpl` (reboot vérifié — capture boot_id) ```sh #!/bin/sh export LC_ALL=C echo "===SU:BOOT_ID_BEFORE===" cat /proc/sys/kernel/random/boot_id 2>/dev/null echo "===SU:REBOOT_NOW===" # Reboot différé pour laisser le canal SSH se fermer proprement. nohup sh -c 'sleep 2; reboot' >/dev/null 2>&1 & echo "reboot planifié" ``` Le **backend** orchestre la vérification (hors template) : il a lu `boot_id` *avant*, attend la coupure SSH, retente la connexion (délai adaptatif), relit `boot_id` via `runPlain` (`cat /proc/sys/kernel/random/boot_id`). Reboot `ok` seulement si la machine revient ET `boot_id` a changé. Statuts d'échec : `reboot_command_failed`, `ssh_never_went_down`, `machine_did_not_return`, `boot_id_unchanged`, `timeout`. Champs résultat : `beforeBootId`, `afterBootId`, `requestedAt`, `sshWentDownAt`, `sshCameBackAt`, `waitedSeconds`, `status`, `errors`. Délai adaptatif par machine : `lastRebootDurationSeconds` → `nextRecommendedWaitSeconds` (avec marge). Voir `40-contrats-json.md` (`RebootResult`). --- ## 5. Spécificités par profil OS (détail dans `60-profils-os-machine.md`) - **Debian** : avant de proposer firmware/drivers propriétaires, vérifier la présence des composants `contrib`, `non-free`, `non-free-firmware` (template `apt/check-components.sh.tpl`, lecture seule). Pas d'activation automatique. - **Ubuntu** : `ubuntu-drivers devices` (lecture) pour proposer des drivers (NVIDIA/GPU) — proposition uniquement, jamais installé par défaut. - **Proxmox** : profil dédié. Vérifier dépôts PVE (`pve-no-subscription` vs `enterprise`), meta-package `proxmox-ve`, kernel PVE, Ceph éventuel, puis `apt-get dist-upgrade`. Ne **jamais** traiter comme une Debian générique. - **Raspberry Pi OS** : profil dédié. Attention firmware/kernel, **vérifier l'espace disque avant upgrade**, utiliser `full-upgrade`. Le template le plus spécifique disponible sous `templates//.sh.tpl` est choisi, sinon fallback `templates/apt/.sh.tpl` (profil `base`). --- ## 6. Compatibilité et migration (non-régression jalon 1) - L'actuel `check.sh.tpl` produit `===SU:UPDATE===` / `===SU:SIMULATE===` / `===SU:REBOOT===` / `===SU:END===`. Le nouveau `update-analyze.sh.tpl` ajoute des sections **sans supprimer** la sémantique : le parsing actuel (`extractSection(raw, "===SU:SIMULATE===", "===SU:REBOOT===")`) peut être conservé en parallèle pendant la migration. - **Recommandation MVP** : introduire `update-analyze.sh.tpl` comme nouveau template et faire pointer le refresh dessus dans un sous-jalon dédié, en gardant `check.sh.tpl` jusqu'à bascule validée. Aucune rupture du flux prouvé en prod. - `full-upgrade.sh.tpl` et `reboot.sh.tpl` existants restent fonctionnels ; on **ajoute** les sections `DPKG_BEFORE/AFTER` et `BOOT_ID_BEFORE` (extension, pas remplacement de marqueurs).