0fbca06d3d
Cartographie complète (liste_taches/coherence_taches), briefs tacheN + gates validation_tacheN, design tâche 2 (docs/design/tache2/), specs/plans jalon 1-2 et tâche 1.9/2 (Phase 1, Phase 2, SJ-0→3). Validations consignées (1.9 ✅, 2-8 🟡). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
203 lines
12 KiB
Markdown
203 lines
12 KiB
Markdown
# 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 <pkg> [<cur>] (<target> <origin> [<arch>])`, `Conf <pkg>`, `Remv <pkg>`. 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/<profil>/<commande>.sh.tpl` est choisi, sinon fallback `templates/apt/<commande>.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).
|