diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8f50e88 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Scripts Windows : CRLF obligatoires pour cmd.exe. Stockes tels quels +# (pas de conversion) pour que le fichier brut telecharge depuis GitHub +# garde aussi ses CRLF. +*.bat -text diff --git a/CLAUDE.md b/CLAUDE.md index c096eb9..89be4e9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,11 +16,11 @@ Un export sera possible en pdf avec une mise en page adaptée. ##Environnement de travail -La formation s'adresse à deux types de public avec des environnements différents. Le détail de l'installation et de la configuration est documenté dans `cours_2026/annexes/installation.md`. L'installation est un prérequis réalisé hors séances. +La formation s'adresse à deux types de public avec des environnements différents. Le détail de l'installation et de la configuration est documenté dans deux annexes autonomes : `cours_2026/annexes/installation_ssh.md` (public A) et `cours_2026/annexes/installation_virtualbox.md` (public B). L'installation est un prérequis réalisé hors séances. **Public A - VM distante SSH** : une VM Linux est mise à disposition du stagiaire. Un client SSH est configuré avec accès par paire de clés. L'installation est immédiate, la connexion se fait depuis le premier jour. -**Public B - VirtualBox sur poste Windows** : le stagiaire travaille sur D:\ dans un répertoire créé à son nom. Seuls les fichiers de ce répertoire sont conservés entre les sessions (les applications installées sont supprimées à chaque redémarrage : "le freeze"). VirtualBox est installé et une VM Debian 13 est créée à partir d'une image ISO. L'image de VM est stockée sur D:\ afin d'être réutilisée d'une semaine à l'autre. +**Public B - VirtualBox sur poste Windows (CID)** : les postes sont figés sur C: (les applications installées sont supprimées à chaque redémarrage : "le freeze") ; seul D:\ est conservé. Le stagiaire travaille dans D:\PrenomNOM\, avec deux sous-dossiers : `sources\` (Visual C++ Redistributable, installeur VirtualBox, Extension Pack, image DVD de Debian 13, script `installer_virtualbox.bat`) et `VirtualBox\` (la VM). VirtualBox est réinstallé à chaque séance par le script `ressources/scripts/installer_virtualbox.bat` (double-clic), qui réenregistre la VM. La VM FormationLinux exécute Debian 13 avec le bureau GNOME, en réseau par pont (accessible en SSH depuis Windows). ##Plan de formation @@ -136,7 +136,8 @@ formation_linux/ 02_scripts_bash.md 03_cron_alias_personnalisation.md annexes/ - installation.md (guide d'installation pour les deux environnements) + installation_ssh.md (public A : se connecter à sa VM distante) + installation_virtualbox.md (public B : Debian 13 dans VirtualBox, illustré) supports/ modules_additionnels/ (toujours actif : modules Git et Docker) module_git/ @@ -170,6 +171,11 @@ formation_linux/ evaluations/ (quiz et evaluations finales de l'ancien cours) ressources/ images/ + installation/ (captures VirtualBox et installeur Debian des annexes) + scripts/ + installer_virtualbox.bat (réinstallation de VirtualBox au CID, fins de ligne CRLF) + outils/ + captures/ (scripts de production des captures, hors CI) pdf/ (chaîne de génération PDF) build (point d'entrée unique) generer.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ca0c83..4fd962f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ 3. **Modifier** les fichiers Markdown dans `cours_2026/` (cours de base) ou dans les modules additionnels (`supports/modules_additionnels/`, `travaux_pratiques/tp_additionnels/`) 4. **Tester** localement (optionnel, Docker requis) : ```bash - ./pdf/build # Génère les 11 PDF et lance les tests + ./pdf/build # Génère les 12 PDF et lance les tests ``` 5. **Commit** et **push** vos modifications 6. **Créer** une Pull Request @@ -18,7 +18,7 @@ Dès que vous créez une PR, le workflow GitHub Actions `PDF` lance les mêmes commandes qu'en local : - `./pdf/build check` : vérification du code de la chaîne (lint) ; -- `./pdf/build pdf` : génération des 11 PDF ; +- `./pdf/build pdf` : génération des 12 PDF ; - `./pdf/build test` : tests des PDF produits, résultat visible dans la PR. En local, `./pdf/build` seul enchaîne la génération et les tests, sans le lint : si vous modifiez `pdf/`, lancez aussi `./pdf/build check`. diff --git a/README.md b/README.md index 14670d5..6d4634e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ Cette formation s'adresse à un public généraliste souhaitant découvrir et ma **Prérequis :** connaissance générale d'un système d'exploitation, notion de fichier et d'arborescence, savoir utiliser un clavier. Le cours théorique de base est organisé en **6 séances de 2 heures**, une fois l'environnement installé. -Guide d'installation : `cours_2026/annexes/installation.md` +Guides d'installation : `cours_2026/annexes/installation_ssh.md` (VM distante, SSH) et +`cours_2026/annexes/installation_virtualbox.md` (Debian 13 dans VirtualBox, poste Windows du CID). Chaque chapitre contient : - la théorie vue en séance (environ 30 minutes), @@ -66,7 +67,8 @@ Chaque chapitre contient : ### Annexe -- Guide d'installation de l'environnement (VM SSH ou VirtualBox/Debian 13) +- Se connecter à sa VM distante (SSH) +- Installer Debian 13 dans VirtualBox (illustré, avec le script `ressources/scripts/installer_virtualbox.bat`) ## Tableau des séances (indicatif) @@ -115,7 +117,8 @@ formation_linux/ | +-- module_07_reseaux/ | +-- module_08_automatisation/ | +-- annexes/ -| +-- installation.md +| +-- installation_ssh.md +| +-- installation_virtualbox.md | +-- supports/ | +-- modules_additionnels/ # Modules additionnels (contenu) @@ -140,7 +143,7 @@ formation_linux/ ## Génération PDF -Les PDF de la formation (un par module, plus les modules additionnels et l'annexe d'installation) sont disponibles dans les [Releases](../../releases/latest) du dépôt. +Les PDF de la formation (un par module, plus les modules additionnels et les deux annexes d'installation) sont disponibles dans les [Releases](../../releases/latest) du dépôt. Pour les générer localement, Docker suffit : diff --git a/cours_2026/annexes/installation.md b/cours_2026/annexes/installation.md deleted file mode 100644 index 833c825..0000000 --- a/cours_2026/annexes/installation.md +++ /dev/null @@ -1,255 +0,0 @@ -# Annexe — Installer son environnement de travail - -> Ce guide est un prérequis : la formation commence une fois -> l'environnement installé et fonctionnel. - ---- - -## Option A — VM Linux distante (accès SSH) - -Cette option concerne les stagiaires qui disposent d'une VM Linux -fournie par le formateur et d'un client SSH configuré à l'avance. - -### Ce dont vous avez besoin - -- L'adresse IP de votre VM (communiquée par le formateur) -- Votre nom d'utilisateur -- Votre fichier de clé privée (extension `.ppk` pour PuTTY, ou `.pem`/sans extension pour OpenSSH) - -### Connexion avec PuTTY (Windows) - -1. Ouvrir PuTTY. -2. Dans le champ **Host Name**, saisir l'adresse IP de votre VM. -3. Aller dans **Connection > SSH > Auth > Credentials** et charger votre clé privée. -4. Revenir dans **Session**, donner un nom dans **Saved Sessions**, puis cliquer **Save**. -5. Cliquer **Open** pour lancer la connexion. -6. À l'invite `login as:`, saisir votre nom d'utilisateur. - -### Connexion avec le Terminal Windows (OpenSSH) - -Si votre poste dispose de Windows 10 version 1809 ou supérieure, -OpenSSH est intégré. Ouvrir un terminal PowerShell ou Invite de -commandes, puis : - -```powershell -ssh -i C:\chemin\vers\cle_privee utilisateur@ADRESSE_IP -``` - -### Vérification de la connexion - -Une fois connecté, le prompt doit ressembler à : - -``` -utilisateur@debian-formation:~$ -``` - -Taper les commandes suivantes pour confirmer que tout fonctionne : - -```bash -whoami -hostname -cat /etc/os-release -``` - -### Premiers réglages recommandés - -```bash -# Historique plus grand pour ne pas perdre les commandes précédentes -echo 'export HISTSIZE=5000' >> ~/.bashrc -echo 'alias ll="ls -la"' >> ~/.bashrc -source ~/.bashrc -``` - ---- - -## Option B — VM VirtualBox sur poste Windows - -Cette option concerne les stagiaires qui travaillent sur un poste -Windows en salle, soumis au « freeze » (les applications installées -sont supprimées à chaque redémarrage). Vous travaillerez donc sur -le lecteur `D:\` qui, lui, est persistant. - -### Prérequis - -- Poste Windows 10 ou 11 avec au moins 4 Go de RAM et 25 Go libres sur `D:\` -- Virtualisation matérielle activée (VT-x ou AMD-V — généralement déjà activée) - -### Étape 1 : Créer votre répertoire personnel sur D:\ - -``` -D:\ -+-- PrenomNOM\ - +-- VirtualBox\ (disque de la VM) - +-- iso\ (image ISO Debian) -``` - -Dans l'Explorateur Windows, créer ce répertoire avant de commencer. -Tous vos fichiers de formation seront dans `D:\PrenomNOM\`. - -### Étape 2 : Installer VirtualBox - -1. Télécharger VirtualBox sur https://www.virtualbox.org (section - « Windows hosts »). -2. Exécuter l'installateur et suivre l'assistant avec les options - par défaut. -3. Redémarrer le poste si VirtualBox le demande. - -> VirtualBox sera supprimé au prochain redémarrage du poste (freeze). -> Cependant, le disque de votre VM reste sur `D:\PrenomNOM\VirtualBox\` -> et sera réutilisable la semaine suivante après réinstallation de VirtualBox. - -### Étape 3 : Télécharger l'image ISO Debian 13 - -1. Aller sur https://www.debian.org/distrib/ -2. Choisir « Small CDs or USB sticks » puis l'image **amd64** (netinst). -3. Sauvegarder le fichier `.iso` dans `D:\PrenomNOM\iso\`. - -> La taille de l'ISO netinst est d'environ 400 Mo. Si l'accès -> internet est lent en salle, le formateur peut vous fournir l'ISO -> sur une clé USB. - -### Étape 4 : Créer la machine virtuelle - -Dans VirtualBox, cliquer **Nouveau** et renseigner : - -``` -Nom : FormationLinux -Dossier : D:\PrenomNOM\VirtualBox\ -Type : Linux -Version : Debian (64-bit) -RAM : 2048 Mo -Disque : Créer un disque VDI de 20 Go (allocation dynamique) -``` - -Avant de démarrer, ouvrir les **Paramètres** de la VM : - -- **Système > Processeur** : mettre 2 CPU si le poste le permet. -- **Stockage** : cliquer sur le lecteur CD vide, puis l'icône de disque - à droite et choisir « Choisir un fichier de disque » ; sélectionner - l'ISO Debian. -- **Réseau** : laisser la carte en mode NAT (par défaut). - -### Étape 5 : Installer Debian - -Démarrer la VM. Choisir **Install** (installation en mode texte, -plus rapide et suffisante pour cette formation). - -Paramètres recommandés pendant l'installation : - -``` -Langue : Français -Pays : France -Clavier : Français (azerty) -Nom de machine : debian-formation -Nom d'utilisateur : votre prénom en minuscules -Mot de passe : choisir quelque chose de simple à retenir -Partitionnement : "Utiliser un disque entier" (option simple) -Sélection logiciels : décocher "Environnement de bureau GNOME", - garder "Utilitaires usuels du système" et - "Serveur SSH" -``` - -L'installation dure environ 15 à 20 minutes. - -### Étape 6 : Premier démarrage - -Après l'installation, la VM redémarrera sur Debian. Se connecter avec -le nom d'utilisateur et le mot de passe choisis. - -Vérifier que le réseau fonctionne : - -```bash -ping -c 3 debian.org -``` - -Mettre le système à jour : - -```bash -su - -apt update && apt upgrade -y -exit -``` - -### Réutilisation d'une semaine sur l'autre - -Au début de chaque séance : - -1. Réinstaller VirtualBox (les installateurs peuvent être placés dans - `D:\PrenomNOM\` pour éviter de les retélécharger). -2. Dans VirtualBox, cliquer **Ajouter** et pointer vers - `D:\PrenomNOM\VirtualBox\FormationLinux\FormationLinux.vbox`. -3. Démarrer la VM : votre environnement est intact. - ---- - -## Vérifier que tout fonctionne - -Quelle que soit l'option choisie, exécuter les commandes suivantes -dans le terminal Linux : - -```bash -# Vérifier l'identité -whoami - -# Vérifier le répertoire de travail -pwd - -# Vérifier la distribution -cat /etc/os-release - -# Vérifier l'espace disque disponible -df -h / - -# Vérifier la mémoire disponible -free -h -``` - -Redimensionner la fenêtre du terminal ou de PuTTY pour être à l'aise. -Un terminal de 80 colonnes minimum est recommandé. - ---- - -## En cas de problème - -### Option A (SSH) — Problèmes courants - -**La connexion est refusée (« Connection refused » ou « Connection timed out »)** -Vérifier l'adresse IP communiquée par le formateur. Si le message est -« Connection timed out », la VM cible est peut-être éteinte ; prévenir -le formateur. - -**« Permission denied (publickey) »** -La clé privée n'est pas celle attendue par le serveur, ou elle n'est -pas chargée dans PuTTY. Vérifier le chemin du fichier `.ppk` dans -PuTTY > Connection > SSH > Auth > Credentials. - -**Le prompt ne s'affiche pas après la connexion** -Appuyer sur Entrée. Si le problème persiste, fermer et rouvrir la -session. - ---- - -### Option B (VirtualBox) — Problèmes courants - -**La VM ne démarre pas : « VT-x is not available »** -La virtualisation n'est pas activée dans le BIOS. Le formateur peut -activer cette option ; c'est une manipulation de quelques secondes -au démarrage du poste. - -**L'installation de Debian se bloque sur « Configurer le réseau »** -Passer cette étape en choisissant « Ne pas configurer le réseau pour -l'instant ». Le réseau peut être configuré après l'installation en -passant en root puis en lançant `dhclient` : - -```bash -su - -dhclient -``` - -**La VM est très lente** -Vérifier que le nombre de CPU est d'au moins 2 dans les paramètres -VirtualBox. Si d'autres VMs tournent sur le poste, les éteindre. - -**Après le freeze, VirtualBox ne trouve plus la VM** -Utiliser **Ajouter** (pas **Nouveau**) et pointer vers le fichier -`.vbox` dans `D:\PrenomNOM\VirtualBox\FormationLinux\`. diff --git a/cours_2026/annexes/installation_ssh.md b/cours_2026/annexes/installation_ssh.md new file mode 100644 index 0000000..e221425 --- /dev/null +++ b/cours_2026/annexes/installation_ssh.md @@ -0,0 +1,133 @@ +# Annexe — Se connecter à sa VM distante (SSH) + +> Ce guide est un prérequis, réalisé hors séances : la formation commence +> une fois la connexion à votre VM établie et vérifiée. + +Cette annexe concerne les stagiaires qui disposent d'une VM Linux +fournie par le formateur et d'un client SSH configuré à l'avance avec +une paire de clés. Aucune installation n'est nécessaire : il suffit de +se connecter à la VM depuis son poste Windows, avec PuTTY ou avec le +client OpenSSH intégré à Windows. + +## Ce dont vous avez besoin + +Le formateur vous communique trois éléments : + +- l'adresse IP de votre VM ; +- votre nom d'utilisateur ; +- votre fichier de clé privée : extension `.ppk` pour PuTTY, extension + `.pem` ou sans extension pour OpenSSH. + +La clé privée est personnelle : ne la partagez pas et ne la déposez pas +dans un dossier accessible à d'autres personnes. + +## Se connecter avec PuTTY + +1. Ouvrir PuTTY. +2. Dans le champ **Host Name**, saisir l'adresse IP de votre VM. +3. Aller dans **Connection > SSH > Auth > Credentials** et charger votre + clé privée (fichier `.ppk`). +4. Revenir dans **Session**, donner un nom dans **Saved Sessions**, puis + cliquer sur **Save**. Les fois suivantes, il suffira de sélectionner + cette session enregistrée et de cliquer sur **Load**. +5. Cliquer sur **Open** pour lancer la connexion. +6. À l'invite `login as:`, saisir votre nom d'utilisateur. + +## Se connecter avec le Terminal Windows (OpenSSH) + +Si votre poste dispose de Windows 10 version 1809 ou supérieure, le +client OpenSSH est intégré. Ouvrir un terminal PowerShell ou une Invite +de commandes, puis taper : + +```powershell +ssh -i C:\chemin\vers\cle_privee utilisateur@ADRESSE_IP +``` + +Remplacer `C:\chemin\vers\cle_privee` par l'emplacement de votre fichier +de clé, `utilisateur` par votre nom d'utilisateur et `ADRESSE_IP` par +l'adresse de votre VM. + +## Vérifier la connexion + +Une fois connecté, le prompt (l'invite) doit ressembler à celui-ci, +avec votre nom d'utilisateur et le nom de votre VM, par exemple +`debian-formation` : + +``` +utilisateur@debian-formation:~$ +``` + +Taper les commandes suivantes pour confirmer que vous êtes bien sur +votre VM : + +```bash +whoami +hostname +cat /etc/os-release +``` + +`whoami` affiche votre nom d'utilisateur, `hostname` le nom de la +machine et `cat /etc/os-release` la distribution installée. + +## Premiers réglages recommandés + +Ces trois lignes agrandissent l'historique des commandes, créent le +raccourci `ll` (liste détaillée des fichiers) et rechargent la +configuration : + +```bash +# Historique plus grand pour ne pas perdre les commandes précédentes +echo 'export HISTSIZE=5000' >> ~/.bashrc +echo 'alias ll="ls -la"' >> ~/.bashrc +source ~/.bashrc +``` + +Ces notions seront expliquées pendant la formation (voir chapitres 6.3 +et 8.3) : pour l'instant, il suffit de recopier les commandes. + +## Vérifier que tout fonctionne + +Une fois la connexion vérifiée, exécuter ces dernières commandes dans +le terminal Linux : + +```bash +pwd # répertoire de travail +df -h / # espace disque disponible +free -h # mémoire disponible +``` + +Redimensionner la fenêtre du terminal ou de PuTTY pour être à l'aise. +Un terminal de 80 colonnes minimum est recommandé. + +## Problèmes courants + +**« Connection timed out »** + +Vérifier l'adresse IP communiquée par le formateur. Si elle est +correcte, la VM est peut-être éteinte ou injoignable : prévenir le +formateur. + +**« Connection refused »** + +La VM répond, mais le service SSH n'y fonctionne pas (arrêté ou en +cours de démarrage). Patienter une minute et réessayer ; si le message +persiste, prévenir le formateur. + +**« Permission denied (publickey) »** + +La clé privée n'est pas celle attendue par le serveur, ou elle n'est +pas chargée. Avec PuTTY, vérifier le chemin du fichier `.ppk` dans +**Connection > SSH > Auth > Credentials**. Avec OpenSSH, vérifier le +chemin indiqué après l'option `-i`. + +**« WARNING: UNPROTECTED PRIVATE KEY FILE! » (OpenSSH)** + +OpenSSH refuse une clé privée lisible par d'autres comptes du poste +(dossier partagé, clé USB...). Ranger la clé dans le dossier `.ssh` de votre +profil Windows, `%USERPROFILE%\.ssh` (le créer s'il n'existe pas), et +indiquer ce nouveau chemin après l'option `-i`. + +**Le prompt ne s'affiche pas après la connexion** + +Appuyer sur Entrée. Si le problème persiste, fermer et rouvrir la +session. diff --git a/cours_2026/annexes/installation_virtualbox.md b/cours_2026/annexes/installation_virtualbox.md new file mode 100644 index 0000000..7bd4920 --- /dev/null +++ b/cours_2026/annexes/installation_virtualbox.md @@ -0,0 +1,1150 @@ +# Annexe — Installer Debian 13 dans VirtualBox + +> Ce guide est un prérequis, réalisé hors séances : la formation commence +> une fois votre machine virtuelle Debian installée et vérifiée. + +Cette annexe concerne les stagiaires qui travaillent sur un poste +Windows de la salle du CID. Vous allez installer Debian 13, avec son +bureau GNOME, dans une machine virtuelle (VM) VirtualBox. + +Les postes du CID sont **figés** : le disque `C:` est remis à zéro à +chaque redémarrage. Tout ce qui est installé sur `C:`, VirtualBox +compris, disparaît donc d'une séance à l'autre. Le lecteur `D:`, lui, +est conservé : vous y rangez les installeurs et la VM. + +Ce qui est fait **une seule fois**, lors de la première séance : + +- télécharger les fichiers nécessaires dans votre dossier sur `D:` ; +- créer la VM et y installer Debian ; +- installer les Additions invité dans Debian. + +Ce qui est refait **à chaque séance** : réinstaller VirtualBox, en +double-cliquant sur un script fourni, puis démarrer la VM. Comptez +quelques minutes. + +## Prérequis matériels + +- Un poste Windows 10 ou 11, 64 bits. +- Au moins 8 Go de mémoire vive : la VM en utilise 4 Go. +- Au moins 40 Go libres sur `D:` : environ 4 Go pour les fichiers + téléchargés, et jusqu'à 30 Go pour le disque de la VM, qui grossit au + fur et à mesure qu'il se remplit. +- La virtualisation matérielle activée (VT-x chez Intel, AMD-V chez + AMD). C'est en général déjà le cas ; sinon, voir la section + « Problèmes courants ». +- Un accès à Internet pour les téléchargements et les mises à jour. + +## Préparer son dossier sur le lecteur D + +Dans l'Explorateur Windows, créer un dossier à votre nom sur `D:`, +par exemple `D:\PrenomNOM`, avec deux sous-dossiers : + +``` +D:\ ++-- PrenomNOM\ + +-- sources\ (installeurs, image de Debian, script) + +-- VirtualBox\ (la machine virtuelle) +``` + +- `sources\` reçoit les cinq fichiers de la section suivante. +- `VirtualBox\` reçoit la VM : VirtualBox y crée un dossier + `FormationLinux\`, qui contient le disque virtuel. + +Dans la suite, remplacez `PrenomNOM` par le nom de votre dossier. + +## Télécharger les fichiers + +Télécharger les quatre fichiers suivants et les enregistrer dans +`D:\PrenomNOM\sources\` sans les renommer. Les versions ont été +vérifiées le 1er octobre 2026. + +**1. Visual C++ Redistributable** (version 2015-2022 x64, 26 Mo) : +bibliothèques de Microsoft dont VirtualBox a besoin. VirtualBox 7.2 ne +les fournit plus : sans elles, il refuse de s'installer. + +- Fichier : `VC_redist.x64.exe` +- Lien : + +**2. VirtualBox** (version 7.2.20, 178 Mo) : le logiciel qui fait +fonctionner la VM. + +- Fichier : `VirtualBox-7.2.20-175154-Win.exe` +- Lien : + +**3. Extension Pack** (version 7.2.20, 20 Mo) : un module officiel +d'Oracle qui complète VirtualBox. Le script l'installe avec le reste. + +- Fichier : `Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack` +- Lien : + +**4. Image DVD de Debian** (version 13.7, 4,0 Go) : le disque +d'installation de Debian, sous forme de fichier. L'image DVD contient le +bureau GNOME : l'installation dépend peu de la vitesse du réseau. + +- Fichier : `debian-13.7.0-amd64-DVD-1.iso` +- Lien : + +Le lien de Debian contient `current` : il change à chaque nouvelle +version mineure (13.8, 13.9...). S'il ne fonctionne plus, ouvrir la +page d'index et prendre le fichier `debian-13.x.0-amd64-DVD-1.iso` le +plus récent : + + + +### Vérifier les fichiers téléchargés + +Un fichier mal téléchargé provoque des erreurs difficiles à comprendre. +On vérifie donc son **empreinte SHA256** : une suite de 64 caractères +calculée à partir du contenu du fichier. Si un seul octet diffère, +l'empreinte change complètement. + +Ouvrir une Invite de commandes (menu Démarrer, taper `cmd`), puis : + +``` +cd /d D:\PrenomNOM\sources +certutil -hashfile debian-13.7.0-amd64-DVD-1.iso SHA256 +``` + +Comparer la ligne affichée avec l'empreinte publiée, ci-dessous sous +le nom de chaque fichier : + +``` +VirtualBox-7.2.20-175154-Win.exe +a81777d2b36380ce042a29e9c554cf032eb46a793f62e3cc82e7411e535c2c26 + +Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack +0a050da993f2e3cf2e4e9cda2fa8667e44a2b25479b27abd41af6812c1e43591 + +debian-13.7.0-amd64-DVD-1.iso +347b6c67a3cc0b7ddb60b178f683470c4e2b7ac426c996d9337a2ff36c1a32d2 +``` + +Faire de même pour les deux fichiers de VirtualBox, en remplaçant le +nom du fichier dans la commande `certutil`. Pour l'image de Debian, le +calcul peut prendre une minute. Si l'empreinte ne correspond pas, +supprimer le fichier et le télécharger à nouveau. + +`VC_redist.x64.exe` n'a pas d'empreinte ici : le lien de Microsoft +donne toujours la dernière version, dont l'empreinte change à chaque +mise à jour. + +### Récupérer le script d'installation + +Le script `installer_virtualbox.bat` réinstalle VirtualBox en une seule +opération. Il est disponible à cette adresse : + + + +Depuis une page web qui contient ce lien, faire un clic droit sur le +lien, puis **Enregistrer le lien sous** et choisir le dossier +`D:\PrenomNOM\sources\`. Si le lien s'ouvre directement dans le +navigateur (du texte s'affiche), utiliser **Ctrl+S** pour enregistrer +la page. Dans les deux cas, vérifier que le fichier s'appelle bien +`installer_virtualbox.bat`, et non `installer_virtualbox.bat.txt`. + +Son contenu intégral est reproduit à la fin de ce document. Il : + +- retrouve les trois installeurs dans `sources\`, quelle que soit leur + version, et s'arrête avec un message clair s'il en manque un ; +- installe Visual C++, VirtualBox et l'Extension Pack sans poser de + question ; +- indique à VirtualBox que les machines virtuelles sont rangées dans + `D:\PrenomNOM\VirtualBox\` ; +- réenregistre la VM `FormationLinux` si elle existe déjà ; +- affiche un résumé de chaque étape. + +À la fin, `sources\` contient cinq fichiers : les quatre téléchargements +et le script. + +## Première séance : installer VirtualBox + +Double-cliquer sur `installer_virtualbox.bat` dans `D:\PrenomNOM\sources\`. +Une fenêtre noire s'ouvre et affiche la progression. Si Windows demande +une autorisation, répondre **Oui**. + +L'installation de VirtualBox prend une à trois minutes. À la fin, le +script affiche un résumé. Lors de la première séance, la ligne +« Machine virtuelle » indique `pas encore de VM` : c'est normal. Appuyer +sur une touche pour fermer la fenêtre. + +Lancer ensuite **Oracle VirtualBox** depuis le menu Démarrer. + +> Les captures de VirtualBox de ce document ont été réalisées sous +> Linux : les chemins y commencent par `/mnt/D/PrenomNOM`. Sur votre +> poste, ils commencent par `D:\PrenomNOM`. Certains libellés de +> VirtualBox 7.2.20 ne sont pas traduits en français (« VM Name », +> « Finish »...) : ils sont cités tels quels. + +Au premier lancement, VirtualBox propose deux modes d'affichage. +Cliquer sur **Basic Mode** : l'interface simplifiée suffit pour la +formation. + +![Premier lancement : choisir Basic Mode](../../ressources/images/installation/vbox-01-premier-lancement.png) + +Le profil Windows étant remis à zéro, cette question reviendra à chaque +séance : répondre de la même façon. + +## Première séance : créer la machine virtuelle + +Cliquer sur **Nouvelle** dans la barre d'outils. L'assistant « New +Virtual Machine » s'ouvre. + +### Nom, dossier et image de Debian + +Remplir la première page : + +- **VM Name** : `FormationLinux`. Le script retrouve la VM grâce à ce + nom : le recopier exactement. +- **VM Folder** : `D:\PrenomNOM\VirtualBox`. Le champ est normalement + déjà rempli grâce au script ; sinon, le choisir dans la liste. +- **ISO Image** : ouvrir la liste déroulante du champ, choisir + **Autre...**, puis sélectionner + `D:\PrenomNOM\sources\debian-13.7.0-amd64-DVD-1.iso`. + +VirtualBox reconnaît alors Debian 13 Trixie (64-bit) et remplit seul +les champs OS, OS Distribution et OS Version. + +Il coche aussi la case **Proceed with Unattended Installation** +(installation sans surveillance) : **la décocher**. On veut l'installeur +classique de Debian, décrit pas à pas dans ce document. + +![Page 1 de l'assistant : installation sans surveillance décochée](../../ressources/images/installation/vbox-08-nouvelle-machine-sans-surveillance-decochee.png) + +Cliquer sur **Suivant**. + +### Matériel virtuel + +- **Base Memory** : `4096` Mo, soit 4 Go de mémoire pour la VM. +- **Number of CPUs** : `2`, pour que le bureau reste fluide. +- **Disk Size** : `30 Gio`. Le disque est créé au format VDI et + « dynamique » : il n'occupe sur `D:` que la place réellement utilisée. +- **Use EFI** : laisser décoché. + +![Page 2 : mémoire, processeurs et disque](../../ressources/images/installation/vbox-09-nouvelle-machine-materiel.png) + +Cliquer sur **Suivant**. Le récapitulatif doit indiquer +`Proceed with Unattended Installation : false`, 4096 Mo de mémoire, +2 processeurs et un disque de 30 Gio. + +![Récapitulatif avant création](../../ressources/images/installation/vbox-10-nouvelle-machine-recapitulatif.png) + +Cliquer sur **Finish**. La VM `FormationLinux` apparaît dans la liste, +à l'état « Éteinte ». + +### Réseau en accès par pont + +Par défaut, la VM est en mode « NAT » : elle accède à Internet, mais +reste invisible depuis le reste du réseau. On choisit plutôt l'**accès +par pont** : la VM obtient sa propre adresse sur le réseau du CID, comme +un poste de plus. On pourra ainsi s'y connecter en SSH depuis Windows. + +1. Sélectionner `FormationLinux` et cliquer sur **Configuration**. +2. Dans la colonne de gauche, choisir **Réseau**. +3. Dans **Attached to**, choisir **Accès par pont**. +4. Dans **Name**, choisir la carte Ethernet du poste (la carte filaire, + pas une carte Wi-Fi). +5. Cliquer sur **OK**. + +![Configuration > Réseau : accès par pont](../../ressources/images/installation/vbox-12-configuration-reseau-pont.png) + +Sur la capture, la carte s'appelle `eth0` (nom Linux) ; sous Windows, +la liste affiche le nom de la carte réseau du poste. + +Les détails de la VM résument maintenant sa configuration : 4096 Mo, +2 processeurs, l'image de Debian dans le lecteur optique, le disque de +30 Gio et la ligne Réseau en « Interface pont ». + +![La machine virtuelle prête à démarrer](../../ressources/images/installation/vbox-14-machine-configuree.png) + +Cliquer sur **Démarrer** : une nouvelle fenêtre s'ouvre, l'écran de la +VM. + +> Quand vous cliquez dans la fenêtre de la VM, le clavier et la souris +> lui sont réservés. Pour les rendre à Windows, appuyer sur la touche +> **Ctrl de droite** (la « touche hôte » de VirtualBox). + +## Première séance : installer Debian + +L'installeur de Debian démarre sur l'image DVD. Il se pilote à la +souris ou au clavier : **Entrée** valide, les flèches déplacent la +sélection, **Tab** passe d'un champ à l'autre. Le bouton **Continuer** +fait passer à l'écran suivant ; **Revenir en arrière** permet de +corriger une réponse. + +Les captures de l'installeur ont été réalisées dans un autre logiciel +de virtualisation (QEMU) : le disque y apparaît sous le nom +« ATA QEMU HARDDISK ». Dans VirtualBox, il s'appellera « ATA VBOX +HARDDISK ». Le reste est identique. + +### Menu de démarrage + +Choisir **Graphical install** (installation graphique) et appuyer sur +Entrée. + +![Menu de démarrage du DVD](../../ressources/images/installation/debian-01-menu-demarrage.png) + +### Langue, pays et clavier + +**Langue** : choisir **French - Français**. Ce premier écran est encore +en anglais ; les suivants seront en français. + +![Choix de la langue](../../ressources/images/installation/debian-02-langue.png) + +**Pays** : choisir **France**. Ce choix règle aussi le fuseau horaire +(Europe/Paris) : l'installeur ne le demandera pas. + +![Choix du pays](../../ressources/images/installation/debian-03-pays.png) + +**Clavier** : choisir **Français**, la disposition AZERTY des postes +du CID. + +![Disposition du clavier](../../ressources/images/installation/debian-04-clavier.png) + +L'installeur charge ensuite ses composants et configure le réseau seul : +grâce à l'accès par pont, la VM reçoit une adresse du réseau du CID. + +### Nom de la machine + +**Nom de machine** : `debian-formation`. C'est le nom qui apparaîtra +dans l'invite du terminal (`cid@debian-formation`). + +![Nom de la machine](../../ressources/images/installation/debian-05-nom-machine.png) + +**Domaine** : laisser le champ vide et continuer. Il ne sert que dans un +réseau d'entreprise organisé en domaine. + +### Comptes et mots de passe + +**Mot de passe du superutilisateur (root)** : `cid`, saisi deux fois. +Le compte root est l'administrateur du système (voir chapitre 5.1). Un +mot de passe aussi simple ne convient qu'à une VM de formation. + +![Mot de passe de root](../../ressources/images/installation/debian-07-mot-de-passe-root.png) + +**Nom complet du nouvel utilisateur** : `cid`. C'est le compte que vous +utiliserez au quotidien. + +![Nom complet de l'utilisateur](../../ressources/images/installation/debian-08-nom-complet.png) + +**Identifiant** : l'installeur propose `cid` ; le garder et continuer. + +**Mot de passe de l'utilisateur** : `cid`, saisi deux fois. + +![Mot de passe de l'utilisateur cid](../../ressources/images/installation/debian-10-mot-de-passe-utilisateur.png) + +### Partitionnement du disque + +Le partitionnement découpe le disque en zones (partitions). La VM +n'ayant qu'un disque, vide, on laisse l'installeur faire. + +**Méthode** : **Assisté - utiliser un disque entier**. + +![Méthode de partitionnement](../../ressources/images/installation/debian-11-partitionnement-methode.png) + +**Disque** : il n'y en a qu'un, de 32,2 Go (les 30 Gio de VirtualBox, +comptés autrement) ; le sélectionner. Seul le disque virtuel est +effacé : vos fichiers Windows ne risquent rien. + +![Choix du disque](../../ressources/images/installation/debian-12-partitionnement-disque.png) + +**Schéma** : **Tout dans une seule partition (recommandé pour les +débutants)**. + +![Schéma de partitionnement](../../ressources/images/installation/debian-13-partitionnement-schema.png) + +L'installeur affiche le résultat : une grande partition `ext4` montée +sur `/`, et une petite partition d'échange (`swap`). Choisir +**Terminer le partitionnement et appliquer les changements**. + +![Résumé du partitionnement](../../ressources/images/installation/debian-14-partitionnement-resume.png) + +À la question « Faut-il appliquer les changements sur les disques ? », +choisir **Oui**, puis **Continuer**. + +![Confirmation de l'écriture sur le disque](../../ressources/images/installation/debian-15-partitionnement-confirmation.png) + +L'installeur copie alors le système de base : quelques minutes. + +### Gestionnaire de paquets + +**Analyser d'autres supports** : **Non**. Il n'y a qu'un DVD. + +![Analyse d'autres supports](../../ressources/images/installation/debian-17-autres-supports.png) + +**Utiliser un miroir sur le réseau** : **Oui**. Un miroir est un +serveur qui distribue les logiciels de Debian. Le DVD suffit pour +l'installation, mais le miroir apporte les mises à jour de sécurité. + +![Utilisation d'un miroir réseau](../../ressources/images/installation/debian-18-miroir-reseau.png) + +**Pays du miroir** : **France**. **Miroir** : garder **deb.debian.org**, +proposé en premier. + +![Choix du miroir](../../ressources/images/installation/debian-20-miroir-choix.png) + +**Mandataire HTTP** (proxy) : laisser vide et continuer, sauf consigne +contraire du formateur. + +### Enquête de popularité + +**Participer à l'étude statistique** : **Non**. Ce service envoie +chaque semaine à Debian la liste des logiciels utilisés : inutile pour +une VM de formation. + +![Enquête de popularité des paquets](../../ressources/images/installation/debian-22-popularite.png) + +### Sélection des logiciels + +C'est l'écran le plus important. Cocher (avec la souris ou la barre +d'espace) **exactement** : + +- **environnement de bureau Debian** : l'interface graphique ; +- **... GNOME** : le bureau utilisé pendant la formation ; +- **serveur SSH** : pour se connecter à la VM depuis Windows ; +- **utilitaires usuels du système** : les commandes de base. + +Toutes les autres cases restent vides. + +![Sélection des logiciels](../../ressources/images/installation/debian-23-logiciels.png) + +Cliquer sur **Continuer**. L'installation des logiciels est l'étape la +plus longue : de 10 à 30 minutes selon le poste et le réseau. + +### Programme de démarrage + +**Installer GRUB sur le disque principal** : **Oui**. GRUB est le +programme qui démarre Debian quand on allume la VM. + +![Installation de GRUB](../../ressources/images/installation/debian-25-grub.png) + +**Périphérique** : choisir **/dev/sda**, le disque de la VM (et non +« Choix manuel du périphérique »). + +![Disque où installer GRUB](../../ressources/images/installation/debian-26-grub-disque.png) + +### Fin de l'installation + +L'installation est terminée. Cliquer sur **Continuer** : la VM +redémarre. L'installeur éjecte normalement le DVD virtuel ; s'il +réapparaît au redémarrage, voir « Problèmes courants ». + +![Fin de l'installation](../../ressources/images/installation/debian-27-fin-installation.png) + +## Première séance : premier démarrage + +Le menu de GRUB s'affiche quelques secondes, puis lance **Debian +GNU/Linux** tout seul. + +![Menu de GRUB au démarrage](../../ressources/images/installation/debian-28-menu-grub.png) + +L'écran de connexion de GNOME apparaît. Cliquer sur **cid**, saisir le +mot de passe `cid`, puis Entrée. + +![Écran de connexion](../../ressources/images/installation/debian-29-connexion.png) + +À la première connexion, une fenêtre « Bienvenue dans Debian +GNU/Linux 13 (trixie) » propose une visite guidée : cliquer sur +**Passer**. + +![Fenêtre de bienvenue : cliquer sur Passer](../../ressources/images/installation/debian-31-bienvenue.png) + +### Ouvrir un terminal + +Le terminal est la fenêtre où l'on tape les commandes. Pour l'ouvrir : +appuyer sur la touche **Windows** (ou cliquer sur **Activités**, en +haut à gauche), taper `terminal`, puis cliquer sur **Terminal**. + +![Recherche de l'application Terminal](../../ressources/images/installation/debian-33-recherche-terminal.png) + +L'invite `cid@debian-formation:~$` indique que le terminal attend une +commande. + +![Le terminal ouvert](../../ressources/images/installation/debian-34-terminal.png) + +### Donner les droits d'administration à cid et mettre à jour + +Le compte `cid` ne peut pas encore administrer le système. On l'ajoute +au groupe `sudo`, ce qui lui permettra d'utiliser la commande `sudo` +(voir chapitre 5.3). On en profite pour mettre le système à jour, puis +on redémarre. Taper, ligne par ligne : + +```bash +su - +usermod -aG sudo cid +sed -i 's/^deb cdrom:/# deb cdrom:/' /etc/apt/sources.list +apt update +apt upgrade -y +systemctl reboot +``` + +- `su -` ouvre une session root : saisir le mot de passe de root + (`cid`). Rien ne s'affiche pendant la saisie : c'est normal. +- `usermod -aG sudo cid` ajoute `cid` au groupe `sudo`. +- La commande `sed` désactive, dans la liste des sources de logiciels + (`/etc/apt/sources.list`), la ligne qui désigne le DVD d'installation, + en la transformant en commentaire. Sans elle, `apt` réclamerait le DVD + à chaque installation ; les logiciels viennent désormais du miroir + réseau. +- `apt update` récupère la liste des mises à jour, `apt upgrade -y` les + installe. +- `systemctl reboot` redémarre la VM. + +Pourquoi redémarrer ? L'ajout à un groupe ne prend effet qu'à +l'ouverture d'une nouvelle session, et sous GNOME une simple +déconnexion ne suffit pas toujours. Le redémarrage règle aussi le cas +d'un nouveau noyau Linux installé par la mise à jour. + +Au retour, se reconnecter avec `cid`, rouvrir un terminal et vérifier : + +```bash +groups +sudo apt update +``` + +La liste affichée par `groups` doit contenir `sudo`. `sudo` demande le +mot de passe de `cid` (`cid`), puis `apt update` doit se terminer sans +erreur. + +### Installer les Additions invité + +Les **Additions invité** sont des pilotes fournis par VirtualBox. Elles +adaptent l'affichage à la taille de la fenêtre et rendent la souris et +la VM plus fluides. Elles s'installent **une seule fois** : elles restent +sur le disque de la VM. + +**1. Installer les outils de compilation.** Les Additions doivent être +compilées pour le noyau Linux : + +```bash +sudo apt install -y build-essential linux-headers-amd64 +``` + +`build-essential` fournit le compilateur, `linux-headers-amd64` les +fichiers du noyau. `linux-headers-amd64` est un « méta-paquet » : il suit les +mises à jour du noyau, et les fichiers restent donc à jour pour chaque +nouveau noyau. + +**2. Insérer le CD des Additions.** Dans le menu de la fenêtre de la VM, +choisir **Périphériques > Insérer l'image CD des Additions invité...**. +Un CD virtuel apparaît dans Debian. Si GNOME propose de lancer un +logiciel du CD, refuser : on l'installe à la main ci-dessous. + +**3. Trouver le CD.** Ouvrir l'application **Fichiers** : le CD +apparaît dans la colonne de gauche, sous un nom du type +`VBox_GAs_7.2.20` (le nom suit la version de VirtualBox). Cliquer dessus +le rend accessible. Dans le terminal, vérifier où il se trouve : + +```bash +ls /media/cid/ +``` + +Si un dossier `VBox_GAs_7.2.20` s'affiche, le CD est dans +`/media/cid/VBox_GAs_7.2.20`. Sinon, le monter soi-même à +l'emplacement prévu pour le lecteur de CD, `/media/cdrom0` (pas besoin +de `sudo` pour cette commande) : + +```bash +mount /media/cdrom0 +``` + +Le message `WARNING: source write-protected, mounted read-only` peut +s'afficher : il est normal, un CD est toujours en lecture seule. + +**4. Lancer l'installation**, avec le chemin trouvé à l'étape 3 : + +```bash +sudo sh /media/cid/VBox_GAs_7.2.20/VBoxLinuxAdditions.run +``` + +ou, si le CD a été monté à la main : + +```bash +sudo sh /media/cdrom0/VBoxLinuxAdditions.run +``` + +L'installation dure une à deux minutes et se termine sans message +d'erreur. Redémarrer : + +```bash +sudo reboot +``` + +Après le redémarrage, l'affichage de Debian suit la taille de la +fenêtre de la VM. Le CD des Additions peut être éjecté depuis +l'application Fichiers. + +## Séances suivantes + +Au début de chaque séance, le poste a été remis à zéro : VirtualBox a +disparu, mais votre VM est intacte sur `D:`. + +1. Double-cliquer sur `D:\PrenomNOM\sources\installer_virtualbox.bat`. + Si Windows demande une autorisation, répondre **Oui**. +2. Attendre le résumé. La ligne « Machine virtuelle » doit indiquer + `OK (VM reenregistree)` ou `OK (VM deja enregistree)` ; appuyer sur + une touche. +3. Lancer **Oracle VirtualBox** depuis le menu Démarrer et choisir + **Basic Mode**. +4. Sélectionner `FormationLinux` et cliquer sur **Démarrer**. + +Si le résumé signale une erreur, lire le message affiché au-dessus : +il indique la cause et la marche à suivre. En cas de doute, appliquer la +procédure manuelle ci-dessous. + +### Procédure manuelle de secours + +Si le script ne fonctionne pas, faire ses étapes à la main, dans cet +ordre. + +**1. Visual C++.** Double-cliquer sur `VC_redist.x64.exe`, accepter la +licence et cliquer sur **Installer**. + +**2. VirtualBox.** Double-cliquer sur `VirtualBox-7.2.20-175154-Win.exe` +et garder les choix proposés. L'installeur prévient que la connexion +réseau sera brièvement coupée : accepter. Si Windows demande une +autorisation, répondre **Oui**. + +**3. Extension Pack.** Lancer VirtualBox, puis menu **Fichier > Outils > +Extensions** (ou Ctrl+T) et cliquer sur **Install**. Sélectionner +`Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack`. Un +double-clic sur ce fichier dans l'Explorateur mène au même écran. +VirtualBox demande une confirmation : cliquer sur **Installation**. + +![Confirmation de l'installation de l'Extension Pack](../../ressources/images/installation/vbox-03-extension-confirmation.png) + +La licence s'affiche ensuite. Le bouton **J'accepte** ne devient actif +qu'une fois le texte parcouru jusqu'en bas : faire défiler, puis +cliquer sur **J'accepte**. + +![Licence de l'Extension Pack, lue jusqu'au bout](../../ressources/images/installation/vbox-05-extension-licence-fin.png) + +L'Extension Pack apparaît alors dans la liste, coché comme actif. + +![Extension Pack installé](../../ressources/images/installation/vbox-06-extension-installee.png) + +**4. Dossier des machines.** Menu **Fichier > Paramètres**, rubrique +**Général** : choisir `D:\PrenomNOM\VirtualBox` comme dossier par +défaut des machines, puis **OK**. + +**5. Retrouver la VM.** Cliquer sur **Open** dans la barre d'outils +(selon les versions : menu **Machine > Ajouter...**) et sélectionner le +fichier : + +``` +D:\PrenomNOM\VirtualBox\FormationLinux\FormationLinux.vbox +``` + +Attention : utiliser **Open** (ou **Ajouter**), jamais **Nouvelle**. +**Nouvelle** créerait une deuxième VM, vide. + +## Vérifier que tout fonctionne + +Dans un terminal de la VM, exécuter les commandes suivantes : + +```bash +# Vérifier l'identité +whoami + +# Vérifier le répertoire de travail +pwd + +# Vérifier la distribution +cat /etc/os-release + +# Vérifier l'espace disque disponible +df -h / + +# Vérifier la mémoire disponible +free -h + +# Vérifier l'accès à Internet +ping -c 3 debian.org +``` + +`whoami` doit afficher `cid`, `cat /etc/os-release` mentionner Debian +GNU/Linux 13 (trixie), et `ping` recevoir trois réponses. Certains +réseaux bloquent `ping` : dans ce cas, vérifier plutôt l'accès à +Internet avec `sudo apt update`, qui doit se terminer sans erreur. + +### Se connecter à la VM en SSH depuis Windows + +Grâce à l'accès par pont, la VM a sa propre adresse sur le réseau du +CID. On peut donc travailler depuis Windows, dans un terminal SSH, la VM +restant ouverte à côté. + +**1. Trouver l'adresse de la VM.** Dans un terminal de la VM : + +```bash +ip a +``` + +Repérer la carte réseau autre que `lo` (son nom s'affiche en début de +bloc, par exemple `enp0s3`) et la ligne `inet` qui la suit : l'adresse est le nombre qui suit `inet`, sans la +partie `/24` (par exemple `192.168.1.42`). + +**2. Se connecter depuis Windows.** Ouvrir un terminal PowerShell ou une +Invite de commandes et taper, en remplaçant `ADRESSE` : + +```powershell +ssh cid@ADRESSE +``` + +À la première connexion, SSH demande s'il faut faire confiance à cette +machine : taper `yes`, puis Entrée. Saisir ensuite le mot de passe +`cid`. L'invite `cid@debian-formation:~$` confirme la connexion ; +`exit` la ferme. + +L'adresse peut changer d'une séance à l'autre : la vérifier avec `ip a` +à chaque fois. + +## Problèmes courants + +**VirtualBox refuse de s'installer et réclame Visual C++** + +Le Visual C++ Redistributable n'est pas installé. Vérifier que +`VC_redist.x64.exe` est bien dans `sources\`, puis relancer le script, +ou l'installer à la main (procédure manuelle, étape 1). + +**La VM ne démarre pas : « VT-x is not available » (ou AMD-V)** + +La virtualisation matérielle est désactivée dans le BIOS du poste. +Prévenir le formateur : l'option s'active au démarrage du poste, en +quelques secondes. + +**La VM n'a pas de réseau (pas d'adresse, `ping` échoue)** + +En accès par pont, la VM utilise la carte choisie dans **Name**. Si +c'est une carte inactive (Wi-Fi, carte virtuelle), elle n'obtient pas +d'adresse. Éteindre la VM, ouvrir **Configuration > Réseau** et choisir +la carte Ethernet du poste. Si le problème persiste, prévenir le +formateur. En dépannage, le mode **NAT** donne accès à Internet, mais +la connexion SSH depuis Windows ne fonctionne plus. + +**`apt` demande d'insérer un disque** + +`apt` demande d'insérer le disque d'installation (« Changement de support ») : la +ligne du DVD d'installation est encore active dans les sources de +logiciels. La désactiver, en root (`su -`), puis relancer `apt update` : + +```bash +sed -i 's/^deb cdrom:/# deb cdrom:/' /etc/apt/sources.list +``` + +**L'installeur de Debian réapparaît au redémarrage** + +Le DVD virtuel est resté dans le lecteur. Éteindre la VM, puis dans +**Configuration > Stockage**, sélectionner le lecteur optique et retirer +le disque. Redémarrer la VM. + +**La VM est très lente** + +Vérifier dans **Configuration > System > Processeur** que **Number of +CPUs** vaut 2, et que les Additions invité sont installées. Fermer les +applications Windows inutiles. + +![Configuration > System > Processeur : 2 processeurs](../../ressources/images/installation/vbox-13-configuration-processeur.png) + +**Après le freeze, VirtualBox ne trouve plus la VM** + +Relancer le script : il réenregistre la VM. Sinon, utiliser **Open** +(pas **Nouvelle**) et choisir +`D:\PrenomNOM\VirtualBox\FormationLinux\FormationLinux.vbox`. + +**Le clavier est en QWERTY dans Debian** + +Une autre disposition a été choisie pendant l'installation. Dans GNOME, +ouvrir **Paramètres > Clavier**, ajouter la source de saisie +**Français** et supprimer les autres. + +**La souris reste prisonnière de la VM** + +Appuyer sur la touche **Ctrl de droite** pour rendre le clavier et la +souris à Windows. + +**Windows bloque le script téléchargé** + +Windows se méfie parfois des fichiers venus d'Internet et affiche +« Windows a protégé votre ordinateur ». Cliquer sur **Informations +complémentaires**, puis **Exécuter quand même**. + +## Contenu du script installer_virtualbox.bat + +Voici le contenu intégral du script, tel qu'il est publié sur GitHub. +Les messages sont volontairement sans accents : c'est la forme qui +s'affiche correctement dans la fenêtre noire de Windows. + +``` +@echo off +rem ====================================================================== +rem installer_virtualbox.bat -- Formation Linux (CID) +rem +rem Reinstalle VirtualBox sur un poste "fige" (C: remis a zero a chaque +rem redemarrage) et retrouve la VM conservee sur D:. +rem +rem Utilisation : +rem 1. Placer ce fichier dans D:\PrenomNOM\sources\ avec : +rem VC_redist.x64.exe +rem VirtualBox---Win.exe +rem Oracle_VirtualBox_Extension_Pack-.vbox-extpack +rem 2. Double-cliquer sur le fichier. Si Windows demande une +rem autorisation, repondre Oui. +rem +rem Le script : +rem - installe Visual C++ 2015-2022 x64, VirtualBox et l'Extension Pack +rem en silencieux ; +rem - regle le dossier des machines sur D:\PrenomNOM\VirtualBox ; +rem - reenregistre la VM FormationLinux si elle existe deja. +rem +rem Les messages sont volontairement sans accents : cmd.exe affiche les +rem fichiers .bat dans la page de code OEM (850), et "chcp 65001" est +rem source de bugs d'analyse des scripts sur certaines versions de +rem Windows. L'ASCII pur fonctionne partout. +rem +rem Precautions cmd.exe suivies dans ce fichier : aucune variable n'est +rem modifiee puis lue dans un meme bloc entre parentheses (pas besoin de +rem "delayed expansion"), les chemins sont toujours entre guillemets, et +rem les codes retour sont recopies dans RC juste apres chaque commande. +rem ====================================================================== + +setlocal EnableExtensions +title Installation de VirtualBox - Formation Linux + +rem ---------------------------------------------------------------------- +rem Parametres +rem ---------------------------------------------------------------------- + +rem Hash de la licence de l'Extension Pack (SHA256 du fichier de licence, +rem releve pour la version 7.2.20). Si Oracle change la licence, ce hash +rem change et l'installation silencieuse du pack echoue : le script +rem affiche alors la commande a lancer a la main. +set "LICENCE_EXTPACK=eb31505e56e9b4d0fbca139104da41ac6f6b98f8e78968bdf01b1f3da3c4f9ae" + +rem Nom de la VM et emplacement de VBoxManage (dossier d'installation +rem par defaut de VirtualBox). +set "NOM_VM=FormationLinux" +set "VBOXMANAGE=%ProgramFiles%\Oracle\VirtualBox\VBoxManage.exe" + +rem Dossier du script (D:\PrenomNOM\sources\, avec la barre finale) +rem et dossier parent (D:\PrenomNOM). +set "SOURCES=%~dp0" +for %%I in ("%~dp0..") do set "BASE=%%~fI" +set "DOSSIER_VMS=%BASE%\VirtualBox" +set "FICHIER_VM=%DOSSIER_VMS%\%NOM_VM%\%NOM_VM%.vbox" +set "JOURNAL=%SOURCES%installation_virtualbox.log" + +rem Etat de chaque etape, affiche dans le resume final. +set "ETAT_VCREDIST=non fait" +set "ETAT_VIRTUALBOX=non fait" +set "ETAT_EXTPACK=non fait" +set "ETAT_DOSSIER=non fait" +set "ETAT_VM=non fait" +set "AVERTISSEMENTS=0" + +echo ====================================================================== +echo Installation de VirtualBox - Formation Linux +echo ====================================================================== +echo. +echo Dossier des installeurs : %SOURCES% +echo Dossier des machines : %DOSSIER_VMS% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 1 : recherche des installeurs par motif +rem (le script ne depend pas des numeros de version) +rem ---------------------------------------------------------------------- + +rem Le script n'a pas besoin d'etre lance en administrateur : chaque +rem installeur demande lui-meme l'autorisation. Lance en administrateur +rem (souvent un autre compte), il reglerait le profil de ce compte et non +rem celui du stagiaire : simple avertissement, non bloquant. +net session >nul 2>&1 +if not errorlevel 1 echo [ATTENTION] Inutile de lancer en administrateur : un double-clic suffit. +if not errorlevel 1 echo. + +echo Recherche des installeurs dans %SOURCES% +call :trouver FICHIER_VCREDIST "VC_redist.x64*.exe" +call :trouver FICHIER_VIRTUALBOX "VirtualBox-*-Win.exe" +call :trouver FICHIER_EXTPACK "Oracle_VirtualBox_Extension_Pack-*.vbox-extpack" + +if not defined FICHIER_VCREDIST goto erreur_fichiers +if not defined FICHIER_VIRTUALBOX goto erreur_fichiers +if not defined FICHIER_EXTPACK goto erreur_fichiers +echo. + +rem ---------------------------------------------------------------------- +rem Etape 2 : Microsoft Visual C++ Redistributable (requis par VirtualBox) +rem Codes retour acceptes : 0 = installe, 1638 = version plus recente deja +rem presente, 3010 = installe, redemarrage demande (on ne redemarre pas : +rem le poste fige perdrait tout). +rem +rem Les installeurs sont lances avec start /wait (et non directement) : +rem start passe par l'explorateur de Windows, qui affiche lui-meme la +rem demande d'autorisation si l'installeur en a besoin. Lance directement +rem depuis cmd, il echouerait avec le code 740 (elevation requise). +rem start /wait attend la fin de l'installeur et recupere son code retour. +rem Codes 740 et 1223 : autorisation impossible ou refusee ; 1602 : +rem installation annulee (VC++). Code 1618 : une autre installation +rem Windows est deja en cours. +rem ---------------------------------------------------------------------- + +echo [1/5] Installation de Visual C++ Redistributable... +start "" /wait "%FICHIER_VCREDIST%" /install /quiet /norestart +set "RC=%ERRORLEVEL%" +if "%RC%"=="740" goto vcredist_autorisation +if "%RC%"=="1223" goto vcredist_autorisation +if "%RC%"=="1602" goto vcredist_autorisation +if "%RC%"=="1618" goto vcredist_occupe +if "%RC%"=="0" set "ETAT_VCREDIST=OK" +if "%RC%"=="1638" set "ETAT_VCREDIST=OK (deja present)" +if "%RC%"=="3010" set "ETAT_VCREDIST=OK" +if "%ETAT_VCREDIST%"=="non fait" goto erreur_vcredist +echo %ETAT_VCREDIST% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 3 : VirtualBox +rem --silent : installation sans fenetre ni question ; +rem --ignore-reboot : renvoie 0 meme si Windows demande un redemarrage ; +rem --msi-log-file : journal detaille, garde sur D: en cas de probleme. +rem Si VBoxManage existe deja (script relance dans la meme session), +rem l'installation est sautee. +rem ---------------------------------------------------------------------- + +echo [2/5] Installation de VirtualBox (1 a 3 minutes, patienter)... +if exist "%VBOXMANAGE%" goto virtualbox_deja_la +start "" /wait "%FICHIER_VIRTUALBOX%" --silent --ignore-reboot --msi-log-file "%JOURNAL%" +set "RC=%ERRORLEVEL%" +if "%RC%"=="740" goto virtualbox_autorisation +if "%RC%"=="1223" goto virtualbox_autorisation +if "%RC%"=="1618" goto virtualbox_occupe +if not "%RC%"=="0" goto erreur_virtualbox +if not exist "%VBOXMANAGE%" goto erreur_vboxmanage +set "ETAT_VIRTUALBOX=OK" +goto virtualbox_fin +:virtualbox_deja_la +set "ETAT_VIRTUALBOX=OK (deja installe)" +:virtualbox_fin +echo %ETAT_VIRTUALBOX% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 4 : Extension Pack +rem --replace : remplace une version deja installee ; +rem --accept-license= : accepte la licence sans question (hash ci-dessus). +rem Un echec n'est pas bloquant : la VM fonctionne sans le pack. +rem Les commandes VBoxManage sont lancees normalement, avec le compte du +rem stagiaire (le reglage du dossier et la VM sont propres a ce compte) ; +rem VBoxManage demande lui-meme l'autorisation si l'installation du pack +rem en a besoin. +rem ---------------------------------------------------------------------- + +echo [3/5] Installation de l'Extension Pack... +"%VBOXMANAGE%" extpack install --replace --accept-license=%LICENCE_EXTPACK% "%FICHIER_EXTPACK%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto avertissement_extpack +set "ETAT_EXTPACK=OK" +goto extpack_fin +:avertissement_extpack +set "ETAT_EXTPACK=ECHEC (non bloquant, voir ci-dessus)" +set /a AVERTISSEMENTS+=1 +echo. +echo [ATTENTION] L'Extension Pack ne s'est pas installe (code %RC%). +echo Cause probable : Oracle a modifie la licence, le hash du script +echo n'est plus le bon. Pour l'installer a la main, double-cliquer sur +echo le fichier .vbox-extpack, ou taper dans une invite de commandes : +echo. +echo "%VBOXMANAGE%" extpack install --replace "%FICHIER_EXTPACK%" +echo. +echo puis accepter la licence en tapant y. Si Windows demande une +echo autorisation, repondre Oui. +:extpack_fin +echo %ETAT_EXTPACK% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 5 : dossier des machines virtuelles +rem Le reglage est stocke dans le profil Windows (sur C:, efface a chaque +rem redemarrage) : on le refait a chaque fois. +rem ---------------------------------------------------------------------- + +echo [4/5] Reglage du dossier des machines : %DOSSIER_VMS% +if not exist "%DOSSIER_VMS%\" mkdir "%DOSSIER_VMS%" +"%VBOXMANAGE%" setproperty machinefolder "%DOSSIER_VMS%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto erreur_dossier +set "ETAT_DOSSIER=OK" +echo %ETAT_DOSSIER% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 6 : reenregistrement de la VM +rem - VM absente (premiere seance) : rien a faire, on la creera ; +rem - VM deja connue de VirtualBox : rien a faire ; +rem - sinon : VBoxManage registervm. +rem ---------------------------------------------------------------------- + +echo [5/5] Recherche de la VM %NOM_VM%... +if not exist "%FICHIER_VM%" goto vm_absente +"%VBOXMANAGE%" showvminfo "%NOM_VM%" >nul 2>&1 +if not errorlevel 1 goto vm_deja_enregistree +"%VBOXMANAGE%" registervm "%FICHIER_VM%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto avertissement_vm +set "ETAT_VM=OK (VM reenregistree)" +goto vm_fin +:vm_absente +set "ETAT_VM=pas encore de VM (normal a la premiere seance)" +goto vm_fin +:vm_deja_enregistree +set "ETAT_VM=OK (VM deja enregistree)" +goto vm_fin +:avertissement_vm +set "ETAT_VM=ECHEC (non bloquant, voir ci-dessus)" +set /a AVERTISSEMENTS+=1 +echo. +echo [ATTENTION] La VM n'a pas pu etre enregistree (code %RC%). +echo Dans VirtualBox : menu Machine, Ajouter..., puis choisir le fichier +echo %FICHIER_VM% +:vm_fin +echo %ETAT_VM% +echo. + +rem ---------------------------------------------------------------------- +rem Resume +rem ---------------------------------------------------------------------- + +call :resume +if not "%AVERTISSEMENTS%"=="0" echo Termine, mais avec des avertissements : lire les messages ci-dessus. +if "%AVERTISSEMENTS%"=="0" echo Termine. VirtualBox est pret : lancer "Oracle VirtualBox" depuis le menu Demarrer. +echo. +pause +endlocal +exit /b 0 + +rem ====================================================================== +rem Erreurs bloquantes : message, resume, pause, code retour 1 +rem ====================================================================== + +:erreur_fichiers +echo. +echo [ERREUR] Il manque au moins un fichier dans %SOURCES% +echo Fichiers attendus (le numero de version peut varier) : +echo VC_redist.x64.exe +echo VirtualBox-7.2.20-175154-Win.exe +echo Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack +echo Les telecharger (liens dans l'annexe d'installation), les placer +echo dans ce dossier, puis relancer le script. +goto fin_erreur + +:erreur_vcredist +echo. +echo [ERREUR] L'installation de Visual C++ a echoue (code %RC%). +echo Essayer de lancer %FICHIER_VCREDIST% par un double-clic. +set "ETAT_VCREDIST=ECHEC (code %RC%)" +goto fin_erreur + +:vcredist_autorisation +set "ETAT_VCREDIST=ECHEC (autorisation refusee, code %RC%)" +goto erreur_autorisation + +:virtualbox_autorisation +set "ETAT_VIRTUALBOX=ECHEC (autorisation refusee, code %RC%)" +goto erreur_autorisation + +:vcredist_occupe +set "ETAT_VCREDIST=ECHEC (autre installation en cours, code %RC%)" +goto erreur_occupe + +:virtualbox_occupe +set "ETAT_VIRTUALBOX=ECHEC (autre installation en cours, code %RC%)" +goto erreur_occupe + +:erreur_occupe +echo. +echo [ERREUR] Une autre installation est en cours sur ce poste (code %RC%). +echo Patienter quelques minutes (par exemple la fin des mises a jour de +echo Windows), puis relancer le script. +goto fin_erreur + +:erreur_autorisation +echo. +echo [ERREUR] Windows n'a pas donne l'autorisation d'installer (code %RC%). +echo Relancer le script et, quand Windows demande une autorisation, +echo repondre Oui. +goto fin_erreur + +:erreur_virtualbox +echo. +echo [ERREUR] L'installation de VirtualBox a echoue (code %RC%). +echo Journal detaille : %JOURNAL% +echo Essayer d'installer VirtualBox par un double-clic sur +echo %FICHIER_VIRTUALBOX% +set "ETAT_VIRTUALBOX=ECHEC (code %RC%)" +goto fin_erreur + +:erreur_vboxmanage +echo. +echo [ERREUR] VirtualBox semble installe, mais ce fichier est introuvable : +echo %VBOXMANAGE% +echo VirtualBox a peut-etre ete installe dans un autre dossier. +set "ETAT_VIRTUALBOX=ECHEC (VBoxManage introuvable)" +goto fin_erreur + +:erreur_dossier +echo. +echo [ERREUR] Impossible de regler le dossier des machines (code %RC%). +echo Dans VirtualBox : menu Fichier, Parametres, General, puis choisir +echo %DOSSIER_VMS% +set "ETAT_DOSSIER=ECHEC (code %RC%)" +goto fin_erreur + +:fin_erreur +echo. +call :resume +echo Installation interrompue. Prevenir le formateur si le probleme persiste. +echo. +pause +endlocal +exit /b 1 + +rem ====================================================================== +rem Sous-programmes +rem ====================================================================== + +rem ---------------------------------------------------------------------- +rem :trouver VARIABLE "motif" +rem Cherche le motif dans le dossier du script et met le chemin complet +rem dans VARIABLE (vide si aucun fichier). Une boucle "for" sur un motif +rem sans correspondance ne fait aucun tour : la variable reste vide. +rem Si plusieurs fichiers correspondent, le dernier trouve est garde et +rem un avertissement est affiche. +rem ---------------------------------------------------------------------- +:trouver +set "%~1=" +set "NB_TROUVES=0" +for %%F in ("%SOURCES%%~2") do ( + set "%~1=%%~fF" + set /a NB_TROUVES+=1 +) +if "%NB_TROUVES%"=="0" echo [MANQUANT] %~2 +if "%NB_TROUVES%"=="0" exit /b 1 +call echo [OK] %%%~1%% +if not "%NB_TROUVES%"=="1" echo [ATTENTION] %NB_TROUVES% fichiers correspondent a %~2 : garder une seule version. +exit /b 0 + +rem ---------------------------------------------------------------------- +rem :resume -- affiche l'etat de chaque etape +rem ---------------------------------------------------------------------- +:resume +echo ====================================================================== +echo Resume +echo ====================================================================== +echo Visual C++ Redistributable : %ETAT_VCREDIST% +echo VirtualBox : %ETAT_VIRTUALBOX% +echo Extension Pack : %ETAT_EXTPACK% +echo Dossier des machines : %ETAT_DOSSIER% +echo Machine virtuelle : %ETAT_VM% +echo ====================================================================== +echo. +exit /b 0 +``` diff --git a/docs/superpowers/plans/2026-10-01-annexes-installation.md b/docs/superpowers/plans/2026-10-01-annexes-installation.md new file mode 100644 index 0000000..1286c2c --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-annexes-installation.md @@ -0,0 +1,256 @@ +# Annexes d'installation -- Plan d'implémentation + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development to implement this plan task-by-task. + +**Goal :** remplacer l'annexe unique par deux annexes autonomes (SSH, +VirtualBox), la seconde illustrée par des captures reproductibles, et +publier deux PDF. + +**Spec :** `docs/superpowers/specs/2026-10-01-annexes-installation-design.md` +(versions, liens, SHA256, comptes, réglages de VM : s'y référer, ne pas +re-décider). + +**Branche :** `annexes-installation`. + +--- + +## Contexte technique (vérifié le 2026-10-01) + +- Chaîne PDF : `./pdf/build` (tout), `./pdf/build pdf [X.pdf]`, + `./pdf/build test [args]`, `./pdf/build check`, `./pdf/build shell CMD`. + Tout Python/pandoc/LaTeX tourne dans l'image `formation-linux-pdf` + (`pdf/Dockerfile`). Code : `pdf/generer.py` (importé `generer` dans les + tests), catalogue `pdf/documents.yaml` (+ liste `ATTENDUS` dans + `pdf/tests/test_documents.py`), préambule `pdf/preambule.tex`. +- Règles de contenu (CLAUDE.md) : accents préservés ; aucun caractère de + boîte, flèche Unicode ni emoji ; un seul titre `#` par source ; aucun `--` + hors code (test `pdf/tests/test_sources.py`). +- L'hôte a Docker et `/dev/kvm` (accès via `--device /dev/kvm`), 8 CPU, + 15 Go de RAM, ~56 Go libres. Les gros téléchargements vont dans + `build/captures/cache/` (ignoré par git), jamais dans le dépôt. +- Commits : messages en français, terminés par + `Co-Authored-By: Claude Opus 5.5 ` puis + `Claude-Session: https://claude.ai/code/session_01JeyfNQCc5qMpWwPZKnFa93`. + +--- + +### Task 1 : images dans la chaîne PDF + +**Files :** `pdf/generer.py`, `pdf/preambule.tex`, `pdf/tests/`. + +Exigences (TDD : test d'abord, échec constaté, puis code) : + +1. Une image Markdown `![Légende](chemin.png)` dont le chemin est relatif au + **fichier source** est trouvée (pandoc `--resource-path` construit à + partir des dossiers des sources du document) et apparaît dans le PDF. + Le `.tex` est compilé dans `build/pdf/debug/` : les chemins d'images + doivent y être valides (piste : `--extract-media` vers + `build/pdf/debug/-media`, à vérifier). +2. Une image introuvable fait échouer le build avec un message qui nomme + l'image (piste : `--fail-if-warnings` si aucun autre avertissement + pandoc n'est émis sur les 11 PDF actuels ; sinon, détection ciblée du + message « Could not fetch resource »). +3. Mise en page : les images restent à leur place dans le texte (pas de + flottant qui part à la page suivante : figure `H` ou équivalent), largeur + maximale 75 % de la ligne, hauteur maximale ~45 % de la page, proportions + conservées, légende sous l'image. +4. Tests : unitaire (commande pandoc) ; compilation réelle d'un petit + document avec une image PNG générée dans `tmp_path` (le PDF contient une + image : pypdf, `page.images` non vide) ; échec sur image manquante. +5. Les 11 PDF actuels se génèrent toujours, tests et `check` propres. + +Commit : `feat: prise en charge des images dans la chaîne PDF`. + +--- + +### Task 2 : captures de l'installeur Debian (QEMU/KVM) + +**Files :** `outils/captures/debian/` (scripts, Dockerfile éventuel, +README court), `ressources/images/installation/debian-*.png`. + +Exigences : + +1. Télécharger `debian-13.7.0-amd64-DVD-1.iso` dans `build/captures/cache/` + et **vérifier son SHA256** (spec) avant usage ; ne pas retélécharger s'il + est présent et valide. +2. Lancer l'installeur sous QEMU/KVM dans un conteneur (`--device + /dev/kvm`), sans affichage, avec un moniteur QMP. Disque qcow2 de 30 Go + dans `build/captures/`, 4 Go de RAM, 2 CPU, résolution proche de celle + d'une VM VirtualBox (1024x768 ou 1280x800 pour l'installeur). +3. Piloter l'**installation graphique** au clavier (QMP `send-key` / + `human-monitor-command sendkey`), avec les réponses de la spec : + français / France / clavier français ; nom de machine + `debian-formation`, domaine vide ; mot de passe root `cid` ; utilisateur + `cid` (nom complet `cid`), mot de passe `cid` ; fuseau horaire proposé ; + partitionnement assisté, disque entier, tout dans une seule partition ; + pas d'analyse d'autre support ; miroir réseau : **oui** si le réseau + fonctionne (le DVD suffit, mais les mises à jour de sécurité sont + utiles) ; enquête de popularité : non ; sélection des logiciels : + environnement de bureau Debian + **GNOME** + **serveur SSH** + + utilitaires usuels ; GRUB sur le disque principal ; fin. +4. Une capture PNG par écran significatif (`screendump`), prise une fois + l'écran stabilisé (attendre que deux captures successives soient + identiques, avec délai maximal), et quelques captures de progression + (copie des fichiers, installation des logiciels). Puis le premier + démarrage : écran GRUB, écran de connexion GNOME, bureau GNOME + (et un terminal ouvert si c'est simple). +5. Noms stables et ordonnés, ex. `debian-01-menu-demarrage.png`, + `debian-02-langue.png`... ; recadrage inutile (écran entier) ; PNG + optimisés (taille totale raisonnable, < 10 Mo si possible). +6. Le script est **rejouable** de bout en bout (`outils/captures/debian/capturer.sh`) + et robuste aux variations de durée (attentes par stabilisation d'écran, + pas de `sleep` fixes seuls). Il produit aussi une liste + `ressources/images/installation/debian-captures.txt` (fichier -> écran + décrit), utile à la rédaction. +7. Regarder chaque PNG produit (outil Read) et vérifier qu'il montre l'écran + attendu, en français, avec les bonnes valeurs saisies. + +**Ne pas committer** (le coordinateur committe, d'autres tâches tournent en +parallèle) : laisser les fichiers dans l'arbre de travail. + +--- + +### Task 3 : captures VirtualBox (Linux, Xvfb) + +**Files :** `outils/captures/virtualbox/`, `ressources/images/installation/vbox-*.png`. + +Exigences : + +1. Conteneur Debian 13 avec VirtualBox **7.2.20** pour Linux (paquet officiel + de virtualbox.org, version identique à la spec), Xvfb, un gestionnaire de + fenêtres léger, `xdotool`, `imagemagick` ; interface en **français** + (`LANG=fr_FR.UTF-8`). Le pilote noyau n'est pas nécessaire : on ne démarre + aucune VM, on capture seulement les fenêtres. +2. Captures (fenêtre ou écran recadré sur la fenêtre utile) : + - gestionnaire VirtualBox au premier lancement ; + - Fichier > Paramètres > Extensions (ou Outils > Gestionnaire + d'extensions) : installation de l'Extension Pack 7.2.20 (fichier + téléchargé dans `build/captures/cache/`, SHA256 vérifié), écran de + licence, résultat ; + - assistant « Nouvelle machine virtuelle » : nom `FormationLinux`, + dossier `D:\PrenomNOM\VirtualBox` saisi, image ISO + `D:\PrenomNOM\sources\debian-13.7.0-amd64-DVD-1.iso` si l'assistant + l'accepte (sinon un chemin Linux équivalent et une note pour la + rédaction), case « ignorer l'installation sans surveillance » cochée ; + matériel 4096 Mo / 2 CPU ; disque 30 Go VDI dynamique ; récapitulatif ; + - Configuration > Réseau : mode **Accès par pont** ; + - Configuration > Système > Processeur (2 CPU) si utile. +3. Pilotage reproductible (`outils/captures/virtualbox/capturer.sh`) ; noms + ordonnés `vbox-01-...png` ; liste `vbox-captures.txt`. +4. Si l'interface refuse de démarrer sans pilote ou si une étape est + impossible, le dire précisément dans le rapport (ce qui manque, pourquoi) + plutôt que de contourner par des images retouchées. +5. Regarder chaque PNG (outil Read). + +**Ne pas committer** : laisser les fichiers dans l'arbre de travail. + +--- + +### Task 4 : script `installer_virtualbox.bat` + +**Files :** `ressources/scripts/installer_virtualbox.bat`. + +Exigences : + +1. Fichier batch Windows (fins de ligne CRLF, encodage compatible + `cmd.exe` : messages sans accents ou `chcp 65001` en tête), commenté + en français. +2. Travaille depuis son propre dossier (`%~dp0`), qui est + `D:\PrenomNOM\sources\` ; en déduit `D:\PrenomNOM\VirtualBox\`. +3. Se lance par double-clic, sans droits administrateur : chaque + installeur est lancé par `start /wait` et demande lui-même + l'autorisation ; VBoxManage tourne avec le compte du stagiaire. Plus de + blocage `net session` : s'il est lancé en administrateur, simple + avertissement non bloquant (un autre compte réglerait le mauvais + profil). +4. Trouve par motif `VC_redist.x64.exe`, `VirtualBox-*-Win.exe`, + `Oracle_VirtualBox_Extension_Pack-*.vbox-extpack` ; s'arrête avec un + message clair si l'un manque. +5. Installe en silencieux : VC++ (`/install /quiet /norestart`), + VirtualBox (`--silent --ignore-reboot`), Extension Pack + (`VBoxManage extpack install --replace --accept-license=` : le + hash de licence de la 7.2.20 est **relevé réellement** lors de la Task 3 + via `VBoxManage extpack install` sur Linux et reporté ici). +6. Règle le dossier des machines (`VBoxManage setproperty machinefolder`) + et réenregistre `FormationLinux.vbox` s'il existe (sans erreur s'il est + déjà enregistré ou absent, avec message). +7. Vérifie chaque code retour, affiche un résumé, `pause` final. +8. Contrôle statique : pas d'outil d'exécution Windows ici ; relecture + attentive + au moins une vérification syntaxique raisonnable (ex. + `wine cmd /c` si disponible dans un conteneur, sinon noter « non + exécuté » dans le rapport). + +Commit : `feat: script de réinstallation de VirtualBox pour les postes figés`. + +--- + +### Task 5 : `installation_ssh.md` + +**Files :** `cours_2026/annexes/installation_ssh.md`. + +Reprendre l'option A de l'annexe actuelle (`installation.md`) en document +autonome : titre `# Annexe — Se connecter à sa VM distante (SSH)`, +introduction, prérequis, PuTTY, OpenSSH, vérification, premiers réglages, +problèmes courants. Pas de nouveaux faits inventés. Commit avec la Task 7. + +--- + +### Task 6 : `installation_virtualbox.md` + +**Files :** `cours_2026/annexes/installation_virtualbox.md`. + +Document autonome, titre `# Annexe — Installer Debian 13 dans VirtualBox`, +suivant la spec point par point : + +1. Introduction : poste du CID figé sur `C:`, `D:\` persistant ; ce qui est + fait une seule fois, ce qui est refait à chaque séance. +2. Prérequis matériels. +3. Arborescence `D:\PrenomNOM\sources\` et `D:\PrenomNOM\VirtualBox\`. +4. Tableau des téléchargements (lien, version, taille), puis SHA256 et + vérification (`certutil -hashfile fichier SHA256`). Lien d'index Debian + pour la dernière 13.x. Où récupérer `installer_virtualbox.bat` (lien + brut GitHub vers `ressources/scripts/installer_virtualbox.bat`) et son + contenu intégral en bloc de code. +5. Première séance : installation de VC++, VirtualBox, Extension Pack + (texte ; captures `vbox-*` de l'Extension Pack) ; création de la VM + (captures de l'assistant ; réglages de la spec ; réseau **par pont**) ; + installation de Debian (une capture par étape, réponses de la spec, + explications courtes de chaque choix) ; premier démarrage ; ajout de + `cid` au groupe `sudo` (`su -` puis `usermod -aG sudo cid`, déconnexion) + ; mise à jour ; Additions invité (prérequis `build-essential` et en-têtes + du noyau, insertion du CD, `sh VBoxLinuxAdditions.run`, redémarrage). +6. Séances suivantes : script `.bat` (double-clic ; répondre Oui si + Windows demande une autorisation), puis démarrer la + VM ; procédure manuelle de secours ; « Ajouter » (pas « Nouvelle ») si + la VM n'apparaît pas. +7. Vérifier que tout fonctionne (commandes) ; accès SSH depuis Windows à + la VM (grâce au mode pont : `ip a` pour l'adresse, `ssh cid@ADRESSE`). +8. Problèmes courants : VC++ manquant, VT-x, pont sans réseau (choisir la + bonne carte), VM lente, VM introuvable après le freeze, clavier QWERTY. +9. Légendes de figures courtes ; chemins d'images relatifs + (`../../ressources/images/installation/...`). +10. Respect des règles de contenu (test `test_sources.py`, un seul `#`). + +### Task 7 : catalogue, références, suppression de l'ancienne annexe + +**Files :** `pdf/documents.yaml`, `pdf/tests/test_documents.py`, +`cours_2026/annexes/installation.md` (suppression), `CLAUDE.md`, +`README.md`, `CONTRIBUTING.md` si concerné. + +- `annexe_installation.pdf` -> `annexe_installation_ssh.pdf` (sous-titre + « Annexe ») et `annexe_installation_virtualbox.pdf` ; `ATTENDUS` à 12. +- Supprimer `installation.md` (git mv vers l'un des deux si pertinent pour + l'historique). +- Mettre à jour toutes les références (`grep -rn "installation.md\|annexe_installation"` + hors `archives/` et `docs/superpowers/`), y compris CLAUDE.md (section + Environnement de travail, arborescence) et README. +- Commit : `feat: deux annexes d'installation (SSH, VirtualBox illustrée)`. + +### Task 8 : vérification et livraison + +- `./pdf/build check && ./pdf/build` : 12 PDF, tests verts. +- Revue visuelle du PDF VirtualBox (toutes les pages) et du PDF SSH. +- Commit des captures et scripts de capture (Tasks 2 et 3) en commits + séparés : `feat: captures de l'installeur Debian 13.7`, + `feat: captures de l'assistant VirtualBox 7.2.20`. +- PR vers `master`, CI verte, fusion et tag sur accord de l'utilisateur. diff --git a/docs/superpowers/specs/2026-10-01-annexes-installation-design.md b/docs/superpowers/specs/2026-10-01-annexes-installation-design.md new file mode 100644 index 0000000..5f26e96 --- /dev/null +++ b/docs/superpowers/specs/2026-10-01-annexes-installation-design.md @@ -0,0 +1,91 @@ +# Annexes d'installation (SSH et VirtualBox) -- Design + +Date : 2026-10-01 +Statut : validé + +## Contexte + +L'annexe actuelle `cours_2026/annexes/installation.md` traite les deux +publics dans un seul document, sans illustration. Pour le public B (CID), +elle est en partie fausse : + +- les postes du CID sont **figés sur `C:`** : VirtualBox disparaît à chaque + session et doit être réinstallé, avec ses dépendances ; +- elle propose l'ISO netinst et une installation **sans bureau**, alors que + la saison 2026 vise Debian 13 avec environnement de bureau ; +- elle ne mentionne pas le Microsoft Visual C++ Redistributable, que + VirtualBox 7.2 n'embarque plus et sans lequel il ne s'installe pas. + +## Décisions + +1. **Deux documents, deux PDF** : + `cours_2026/annexes/installation_ssh.md` -> `annexe_installation_ssh.pdf`, + `cours_2026/annexes/installation_virtualbox.md` -> + `annexe_installation_virtualbox.pdf`. Chacun est autonome (distribué + séparément) ; la courte section de vérification finale figure dans les + deux. `installation.md` et `annexe_installation.pdf` disparaissent. +2. **Arborescence du stagiaire** : `D:\PrenomNOM\sources\` (installeurs, + image Debian, script) et `D:\PrenomNOM\VirtualBox\` (la VM). Plus de + dossier `iso\`. +3. **Versions vérifiées le 2026-10-01**, listées dans le document avec lien + direct, taille et SHA256 : + + | Fichier | Version | Taille | Lien | + |---|---|---|---| + | `VC_redist.x64.exe` | Visual C++ 2015-2022 x64 | 26 Mo | https://aka.ms/vs/17/release/vc_redist.x64.exe | + | `VirtualBox-7.2.20-175154-Win.exe` | 7.2.20 | 178 Mo | https://download.virtualbox.org/virtualbox/7.2.20/VirtualBox-7.2.20-175154-Win.exe | + | `Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack` | 7.2.20 | 20 Mo | https://download.virtualbox.org/virtualbox/7.2.20/Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack | + | `debian-13.7.0-amd64-DVD-1.iso` | Debian 13.7 | 4,0 Go | https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.7.0-amd64-DVD-1.iso | + + SHA256 publiés : VirtualBox + `a81777d2b36380ce042a29e9c554cf032eb46a793f62e3cc82e7411e535c2c26`, + Extension Pack + `0a050da993f2e3cf2e4e9cda2fa8667e44a2b25479b27abd41af6812c1e43591`, + Debian DVD-1 + `347b6c67a3cc0b7ddb60b178f683470c4e2b7ac426c996d9337a2ff36c1a32d2`. + Le lien `current/` de Debian change à chaque version mineure : le + document donne aussi la page d'index pour retrouver la dernière 13.x. +4. **VM** : nom `FormationLinux`, dossier `D:\PrenomNOM\VirtualBox`, 4 Go de + RAM, 2 CPU, disque VDI dynamique de 30 Go, **réseau en accès par pont** + (pas de NAT), installation sans surveillance **désactivée** (on veut + l'installeur classique, illustré). +5. **Debian 13.7 DVD-1, installation graphique**, bureau **GNOME** et + applications par défaut, **serveur SSH** coché dans la sélection des + logiciels. Comptes : utilisateur **`cid` / `cid`**, root **`cid`** ; + après le premier démarrage, `cid` est ajouté au groupe `sudo` (les + chapitres `su` 5.1 et `sudo` 5.3 fonctionnent tels quels). Additions + invité installées une fois dans Debian (elles restent sur le disque de + la VM). +6. **Réinstallation à chaque séance** : script + `installer_virtualbox.bat` à placer dans `sources\`, lancé par un + simple double-clic (pas en administrateur) : chaque installeur demande + lui-même l'autorisation à Windows, et les commandes VBoxManage tournent + avec le compte du stagiaire (le dossier des machines et la VM sont + propres à ce compte). Il retrouve les installeurs par motif (indépendant des + versions), installe VC++, VirtualBox et l'Extension Pack en silencieux, + règle le dossier des machines et réenregistre la VM. Il s'arrête avec un + message clair si un fichier manque. La procédure manuelle reste décrite. + Le script est versionné dans `ressources/scripts/` et reproduit dans le + document. **Non testé sur Windows** depuis l'environnement de + développement : à valider une fois au CID. +7. **Captures d'écran reproductibles** (`outils/captures/`, hors CI) : + - installeur Debian : la vraie image DVD-1 sous QEMU/KVM dans Docker, + pilotée au clavier (QMP `send-key`), une capture par écran + (`screendump`) ; + - VirtualBox : VirtualBox 7.2.20 pour Linux, interface en français, dans + un conteneur avec Xvfb, piloté par `xdotool` ; captures de l'assistant + « Nouvelle machine », des réglages (réseau par pont) et de l'Extension + Pack. Fenêtres Linux, mêmes écrans et libellés que sous Windows ; des + chemins `D:\...` sont saisis dans les champs. L'installeur Windows + (MSI) n'est pas illustré. + - PNG versionnés dans `ressources/images/installation/` ; scripts + relancés à la main à chaque nouvelle version. +8. **Chaîne PDF** : prise en charge des images (`--resource-path` du + dossier de chaque source), une image introuvable fait échouer le build, + largeur des captures limitée, tests ajoutés ; catalogue de 11 à 12 PDF. + +## Hors périmètre + +- Captures de l'installeur Windows de VirtualBox. +- Test réel du `.bat` sur un poste Windows (à faire au CID). +- Contenu de cours sur le bureau GNOME (volet de saison distinct). diff --git a/outils/captures/README.md b/outils/captures/README.md new file mode 100644 index 0000000..8ddccf0 --- /dev/null +++ b/outils/captures/README.md @@ -0,0 +1,43 @@ +# Captures d'écran des annexes d'installation + +Ces outils produisent les captures de `ressources/images/installation/`, +utilisées par `cours_2026/annexes/installation_virtualbox.md`. Ils ne +tournent pas en CI (il faut Docker, `/dev/kvm` et environ 4 Go de +téléchargement) : on les relance à la main à chaque nouvelle version de +Debian ou de VirtualBox, puis on relit les images avant de committer. + +| Dossier | Produit | Commande | Durée | +|---------|---------|----------|-------| +| `debian/` | `debian-*.png`, `debian-captures.txt` | `outils/captures/debian/capturer.sh` | ~12 min | +| `virtualbox/` | `vbox-*.png`, `vbox-captures.txt` | `outils/captures/virtualbox/capturer.sh` | ~1 min | + +Les fichiers lourds (ISO, paquets, disque de la VM) restent dans +`build/captures/` (ignoré par git). + +## Changer de version + +Les versions et sommes SHA256 sont écrites à plusieurs endroits, à mettre +à jour ensemble : + +- les deux `capturer.sh` (et le nom d'ISO par défaut de + `debian/piloter.py`) ; +- l'annexe, section des téléchargements ; +- le hash de licence de l'Extension Pack dans + `ressources/scripts/installer_virtualbox.bat` (le script de capture + VirtualBox l'affiche ; il change si Oracle modifie la licence) ; +- la spec `docs/superpowers/specs/2026-10-01-annexes-installation-design.md` + (historique). + +## Captures non utilisées + +Toutes les captures produites ne sont pas insérées dans l'annexe : celles +qui montrent un champ vide, une valeur proposée par défaut ou une barre de +progression sont gardées pour référence mais volontairement omises, le +texte décrivant l'étape. Au 2026-10-01 : + +- Debian : 06 (domaine), 09 (identifiant), 16 (installation du système de + base), 19 (pays du miroir), 21 (mandataire), 24 (installation des + logiciels), 30 (saisie du mot de passe), 32 (vue d'ensemble GNOME) ; +- VirtualBox : 02 (outil Extensions vide), 04 (licence, début), 07 (ISO + détectée, avant de décocher l'installation sans surveillance), 11 (VM + créée, réseau encore en NAT). diff --git a/outils/captures/debian/Dockerfile b/outils/captures/debian/Dockerfile new file mode 100644 index 0000000..1c944a4 --- /dev/null +++ b/outils/captures/debian/Dockerfile @@ -0,0 +1,15 @@ +# Image de capture de l'installeur Debian : QEMU/KVM sans affichage, +# piloté par QMP (python3), reconnaissance d'écran par OCR (tesseract). +FROM debian:trixie-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + qemu-system-x86 qemu-utils \ + python3 python3-pil \ + tesseract-ocr tesseract-ocr-fra \ + pngquant optipng \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /outil +COPY piloter.py /outil/piloter.py +ENTRYPOINT ["python3", "/outil/piloter.py"] diff --git a/outils/captures/debian/README.md b/outils/captures/debian/README.md new file mode 100644 index 0000000..514515c --- /dev/null +++ b/outils/captures/debian/README.md @@ -0,0 +1,38 @@ +# Captures de l'installeur Debian + +Produit les captures d'écran de l'installation graphique de Debian 13.7 +(DVD-1) utilisées par l'annexe VirtualBox : +`ressources/images/installation/debian-*.png` et la liste +`ressources/images/installation/debian-captures.txt` (fichier -> écran). + +```bash +outils/captures/debian/capturer.sh +``` + +Prérequis : Docker, `/dev/kvm` accessible à l'utilisateur, environ 40 Go +libres, accès Internet (miroir Debian pendant l'installation). Hors CI. + +## Fonctionnement + +- `capturer.sh` télécharge l'ISO dans `build/captures/cache/` (une seule + fois, SHA256 vérifié), construit l'image Docker (`Dockerfile`) et lance + `piloter.py` dans le conteneur. +- `piloter.py` démarre QEMU/KVM sans affichage (4 Go, 2 CPU, disque qcow2 + de 30 Go en SATA, carte e1000, écran 1280x800), pilote l'installeur au + clavier par QMP (`send-key`) et capture l'écran (`screendump`). +- Chaque étape attend un écran **stable** (captures successives quasi + identiques) contenant un texte attendu, reconnu par OCR (`tesseract`, + français). Si l'écran ne vient pas dans le délai imparti, le script + s'arrête en nommant l'étape et garde la dernière capture dans + `build/captures/debian/echec-<étape>.png`. +- Les saisies suivent la spec : français, France, clavier français, + `debian-formation`, domaine vide, root `cid`, utilisateur `cid`/`cid`, + disque entier en une partition, miroir réseau, GNOME + serveur SSH. +- Fichiers de travail (disque, journal `piloter.log`, captures brutes) : + `build/captures/debian/`, ignoré par git. + +## Nouvelle version de Debian + +Changer `ISO`, `URL` et `SHA256` dans `capturer.sh`, relancer, puis +regarder chaque capture : l'OCR reconnaît les écrans, mais les positions dans les listes (langue, cases +de la sélection des logiciels) sont supposées connues. diff --git a/outils/captures/debian/capturer.sh b/outils/captures/debian/capturer.sh new file mode 100755 index 0000000..597ce19 --- /dev/null +++ b/outils/captures/debian/capturer.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Captures reproductibles de l'installeur graphique de Debian 13.7 (DVD-1). +# +# - télécharge l'ISO dans build/captures/cache/ (reprise possible) et +# vérifie son SHA256 ; +# - construit l'image Docker voisine (QEMU, python3, tesseract) ; +# - lance l'installation complète sous QEMU/KVM, pilotée par piloter.py ; +# - écrit ressources/images/installation/debian-*.png et debian-captures.txt. +# +# Usage : outils/captures/debian/capturer.sh +# Prérequis : Docker, /dev/kvm accessible, ~40 Go libres, accès Internet. +# Durée : de l'ordre de 30 à 60 minutes selon la machine et le réseau. +set -euo pipefail + +ICI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RACINE="$(cd "$ICI/../../.." && pwd)" +CACHE="$RACINE/build/captures/cache" +TRAVAIL="$RACINE/build/captures/debian" +SORTIE="$RACINE/ressources/images/installation" +IMAGE="formation-linux-captures-debian" + +ISO="debian-13.7.0-amd64-DVD-1.iso" +URL="https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/$ISO" +# Ancienne version : https://cdimage.debian.org/cdimage/archive/13.7.0/amd64/iso-dvd/ +SHA256="347b6c67a3cc0b7ddb60b178f683470c4e2b7ac426c996d9337a2ff36c1a32d2" + +erreur() { echo "ERREUR : $*" >&2; exit 1; } + +[ -r /dev/kvm ] && [ -w /dev/kvm ] || erreur "/dev/kvm inaccessible (KVM activé ? groupe kvm ?)" +command -v docker >/dev/null || erreur "docker introuvable" + +mkdir -p "$CACHE" "$TRAVAIL" "$SORTIE" + +verifier_iso() { echo "$SHA256 $CACHE/$ISO" | sha256sum --check --status; } + +if [ -f "$CACHE/$ISO" ] && verifier_iso; then + echo "ISO présente et valide : $CACHE/$ISO" +else + echo "Téléchargement de $ISO (environ 4 Go)..." + curl -fL -C - -o "$CACHE/$ISO" "$URL" \ + || curl -fL -C - -o "$CACHE/$ISO" "https://cdimage.debian.org/cdimage/archive/13.7.0/amd64/iso-dvd/$ISO" + verifier_iso || erreur "SHA256 incorrect pour $CACHE/$ISO (fichier à supprimer puis relancer)" + echo "SHA256 vérifié." +fi + +docker build -q -t "$IMAGE" "$ICI" >/dev/null + +NOM="captures-debian-$$" +trap 'docker rm -f "$NOM" >/dev/null 2>&1 || true' EXIT +debut=$(date +%s) +docker run --rm --name "$NOM" \ + --device /dev/kvm \ + --user "$(id -u):$(id -g)" --group-add "$(stat -c %g /dev/kvm)" \ + -v "$TRAVAIL:/work" \ + -v "$CACHE:/work/cache:ro" \ + -v "$SORTIE:/sortie" \ + -e ISO_NOM="$ISO" \ + "$IMAGE" +fin=$(date +%s) +echo "Terminé en $(( (fin - debut) / 60 )) min. Captures : $SORTIE/debian-*.png" +echo "Journal : $TRAVAIL/piloter.log" diff --git a/outils/captures/debian/piloter.py b/outils/captures/debian/piloter.py new file mode 100755 index 0000000..34ada46 --- /dev/null +++ b/outils/captures/debian/piloter.py @@ -0,0 +1,673 @@ +#!/usr/bin/env python3 +"""Pilote l'installeur graphique de Debian 13 sous QEMU/KVM et capture +chaque écran significatif. + +Exécuté dans le conteneur décrit par le Dockerfile voisin (voir +capturer.sh). Principe : + +- QEMU tourne sans affichage, avec un moniteur QMP sur socket Unix ; +- chaque étape envoie des touches (QMP send-key), puis attend l'écran + suivant : l'écran doit être stable (captures successives quasi + identiques) et contenir un texte attendu, reconnu par OCR (tesseract) ; +- si l'écran attendu ne vient pas dans le délai imparti, le script + s'arrête en nommant l'étape et en conservant la dernière capture. + +Volumes attendus : + /work dossier de travail (build/captures/debian) : disque, socket, + captures brutes, journal ; l'ISO est dans /work/cache + /sortie dossier des PNG finaux (ressources/images/installation) +""" + +from __future__ import annotations + +import json +import os +import shutil +import socket +import subprocess +import sys +import time +import unicodedata +from dataclasses import dataclass +from pathlib import Path + +from PIL import Image, ImageChops + +TRAVAIL = Path(os.environ.get("TRAVAIL", "/work")) +SORTIE = Path(os.environ.get("SORTIE", "/sortie")) +ISO = TRAVAIL / "cache" / os.environ.get("ISO_NOM", "debian-13.7.0-amd64-DVD-1.iso") +DISQUE = TRAVAIL / "debian.qcow2" +SOCKET_QMP = TRAVAIL / "qmp.sock" +BRUT = TRAVAIL / "brut" +JOURNAL = TRAVAIL / "piloter.log" + +# Seuils de comparaison d'images (en pixels différents). +SEUIL_STABLE = 400 # curseur clignotant, petites animations +PERIODE = 1.0 # secondes entre deux captures de surveillance + + +def journal(message: str) -> None: + ligne = f"[{time.strftime('%H:%M:%S')}] {message}" + print(ligne, flush=True) + with JOURNAL.open("a", encoding="utf-8") as f: + f.write(ligne + "\n") + + +class Echec(RuntimeError): + """Écran attendu non obtenu.""" + + +# -------------------------------------------------------------------------- +# QMP +# -------------------------------------------------------------------------- + + +class Qmp: + def __init__(self, chemin: Path, delai: float = 30.0) -> None: + fin = time.monotonic() + delai + while True: + try: + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.connect(str(chemin)) + break + except OSError: + if time.monotonic() > fin: + raise Echec(f"socket QMP {chemin} injoignable") from None + time.sleep(0.5) + self.fichier = self.sock.makefile("rwb") + self._lire() # bannière + self.commande("qmp_capabilities") + + def _lire(self) -> dict: + while True: + ligne = self.fichier.readline() + if not ligne: + raise Echec("connexion QMP fermée (QEMU arrêté ?)") + message = json.loads(ligne) + if "event" not in message: + return message + + def commande(self, nom: str, **arguments) -> object: + requete = {"execute": nom} + if arguments: + requete["arguments"] = arguments + self.fichier.write(json.dumps(requete).encode() + b"\n") + self.fichier.flush() + reponse = self._lire() + if "error" in reponse: + raise Echec(f"QMP {nom} : {reponse['error']}") + return reponse.get("return") + + +# -------------------------------------------------------------------------- +# Clavier +# -------------------------------------------------------------------------- + +# Disposition de la machine invitée au moment de la saisie : QMP envoie +# des touches physiques (codes de la disposition US), la machine invitée +# les interprète selon sa propre disposition. +AZERTY = { + "a": ["q"], + "q": ["a"], + "z": ["w"], + "w": ["z"], + "m": ["semicolon"], + "-": ["6"], + " ": ["spc"], + ".": ["shift", "comma"], +} +QWERTY = {"-": ["minus"], " ": ["spc"], ".": ["dot"]} + + +class Clavier: + def __init__(self, qmp: Qmp) -> None: + self.qmp = qmp + self.disposition = QWERTY + + def touches(self, *combinaisons: str, pause: float = 0.15) -> None: + """Envoie des touches : 'ret', 'tab', 'shift-tab', 'ctrl-alt-t'...""" + for combinaison in combinaisons: + codes = combinaison.split("-") if combinaison != "minus" else [combinaison] + self._envoyer(codes) + time.sleep(pause) + + def _envoyer(self, codes: list[str]) -> None: + self.qmp.commande( + "send-key", + keys=[{"type": "qcode", "data": c} for c in codes], + **{"hold-time": 60}, + ) + + def taper(self, texte: str) -> None: + for caractere in texte: + if caractere in self.disposition: + codes = self.disposition[caractere] + elif caractere.isalnum() and caractere.isascii(): + codes = [caractere.lower()] + if caractere.isupper(): + codes = ["shift", *codes] + else: + raise ValueError(f"caractère non géré : {caractere!r}") + self._envoyer(codes) + time.sleep(0.08) + + +# -------------------------------------------------------------------------- +# Écran +# -------------------------------------------------------------------------- + + +def normaliser(texte: str) -> str: + sans_accents = unicodedata.normalize("NFKD", texte) + sans_accents = "".join(c for c in sans_accents if not unicodedata.combining(c)) + return " ".join(sans_accents.lower().split()) + + +def pixels_differents(a: Image.Image, b: Image.Image) -> int: + if a.size != b.size: + return a.size[0] * a.size[1] + masque = ImageChops.difference(a, b).convert("L").point(lambda v: 255 if v else 0) + return masque.histogram()[255] + + +def ocr(image: Image.Image) -> str: + # Agrandir améliore nettement la reconnaissance des petites polices. + agrandie = image.convert("L").resize((image.width * 2, image.height * 2)) + chemin = BRUT / "_ocr.png" + agrandie.save(chemin) + resultat = subprocess.run( + ["tesseract", str(chemin), "-", "-l", "fra"], + capture_output=True, + text=True, + check=False, + ) + return normaliser(resultat.stdout) + + +def progression(image: Image.Image) -> float: + """Remplissage (0 à 1) de la barre de progression de l'installeur + graphique (1280x800) : pixels bleus sur la ligne médiane de la barre.""" + debut, fin, ligne = 73, 1207, 196 + bleus = 0 + for x in range(debut, fin): + r, _, b = image.getpixel((x, ligne)) + if b > r + 50: + bleus += 1 + return bleus / (fin - debut) + + +@dataclass +class Capture: + fichier: str + description: str + + +class Ecran: + def __init__(self, qmp: Qmp) -> None: + self.qmp = qmp + self.compteur = 0 + self.derniere: Image.Image | None = None + self.captures: list[Capture] = [] + + def instantane(self) -> Image.Image: + self.compteur += 1 + chemin = BRUT / f"_instantane{self.compteur % 2}.png" + self.qmp.commande("screendump", filename=str(chemin), format="png") + image = Image.open(chemin).convert("RGB") + image.load() + return image + + def attendre( + self, + etape: str, + textes: list[str], + delai: float, + absents: list[str] | None = None, + stable: float = 2.0, + ) -> Image.Image: + """Attend un écran stable contenant l'un des textes (OCR normalisé) + et aucun des textes « absents ». Lève Echec après `delai` secondes.""" + attendus = [normaliser(t) for t in textes] + exclus = [normaliser(t) for t in (absents or [])] + debut = time.monotonic() + precedente = self.instantane() + stable_depuis = time.monotonic() + derniere_lue: Image.Image | None = None + texte = "" + while True: + time.sleep(PERIODE) + image = self.instantane() + if pixels_differents(image, precedente) > SEUIL_STABLE: + stable_depuis = time.monotonic() + precedente = image + nouveau = derniere_lue is None or pixels_differents(image, derniere_lue) > SEUIL_STABLE + if time.monotonic() - stable_depuis >= stable and nouveau: + texte = ocr(image) + derniere_lue = image + if any(t in texte for t in attendus) and not any(t in texte for t in exclus): + duree = time.monotonic() - debut + journal(f"{etape} : écran obtenu en {duree:.0f} s") + self.derniere = image + return image + if time.monotonic() - debut > delai: + echec = TRAVAIL / f"echec-{etape}.png" + image.save(echec) + raise Echec( + f"étape « {etape} » : écran attendu ({' | '.join(textes)}) " + f"non obtenu en {delai:.0f} s. Dernière capture : {echec}. " + f"Texte lu : {texte[:400]!r}" + ) + + def capturer(self, nom: str, description: str) -> None: + assert self.derniere is not None + numero = len(self.captures) + 1 + fichier = f"debian-{numero:02d}-{nom}.png" + self.derniere.save(BRUT / fichier) + self.captures.append(Capture(fichier, description)) + journal(f"capture {fichier} : {description}") + + +# -------------------------------------------------------------------------- +# QEMU +# -------------------------------------------------------------------------- + + +def lancer_qemu(installation: bool) -> subprocess.Popen: + if SOCKET_QMP.exists(): + SOCKET_QMP.unlink() + commande = [ + "qemu-system-x86_64", + "-enable-kvm", + "-cpu", + "host", + "-smp", + "2", + "-m", + "4096", + "-machine", + "pc", + "-device", + "ahci,id=ahci", + "-drive", + f"file={DISQUE},if=none,id=disque,format=qcow2", + "-device", + "ide-hd,drive=disque,bus=ahci.0", + "-drive", + f"file={ISO},if=none,id=dvd,media=cdrom,readonly=on", + "-device", + "ide-cd,drive=dvd,bus=ahci.1", + "-netdev", + "user,id=reseau", + "-device", + "e1000,netdev=reseau", + "-vga", + "std", + "-display", + "none", + "-qmp", + f"unix:{SOCKET_QMP},server=on,wait=off", + "-usb", + "-device", + "usb-tablet", + ] + if installation: + commande += ["-boot", "once=d"] + journal("lancement : " + " ".join(commande)) + return subprocess.Popen(commande, stdout=JOURNAL.open("a"), stderr=subprocess.STDOUT) + + +# -------------------------------------------------------------------------- +# Scénario +# -------------------------------------------------------------------------- + +LONG = 900 # étapes de copie/installation de base +TRES_LONG = 5400 # installation des logiciels (GNOME) + + +def scenario(qmp: Qmp, ecran: Ecran, clavier: Clavier) -> None: + attendre, capturer, touches = ecran.attendre, ecran.capturer, clavier.touches + + def cacher_pointeur() -> None: + """Place le pointeur de la tablette USB dans le coin inférieur droit. + + Deux positions successives : une position identique à la précédente + ne produit aucun événement (cas d'une nouvelle session graphique).""" + for valeur in (32000, 32767): + qmp.commande( + "input-send-event", + events=[ + {"type": "abs", "data": {"axis": "x", "value": valeur}}, + {"type": "abs", "data": {"axis": "y", "value": valeur}}, + ], + ) + time.sleep(0.2) + + def cliquer(x: int, y: int) -> None: + """Clic gauche aux coordonnées (x, y) d'un écran 1280x800.""" + position = [ + {"type": "abs", "data": {"axis": "x", "value": x * 32767 // 1279}}, + {"type": "abs", "data": {"axis": "y", "value": y * 32767 // 799}}, + ] + qmp.commande("input-send-event", events=position) + time.sleep(0.3) + for appui in (True, False): + evenement = {"type": "btn", "data": {"button": "left", "down": appui}} + qmp.commande("input-send-event", events=[evenement]) + time.sleep(0.1) + + def attendre_progression(etape, textes, seuil, delai) -> None: + """Attend un écran de progression dont la barre dépasse `seuil`.""" + fin = time.monotonic() + delai + while True: + reste = fin - time.monotonic() + if reste <= 0: + raise Echec( + f"étape « {etape} » : progression {seuil:.0%} non atteinte en {delai} s" + ) + attendre(etape, textes, reste, stable=1.0) + if progression(ecran.derniere) >= seuil: + return + time.sleep(5) + + def etape( + etape, textes, nom, description, delai=300, absents=None, avant=None, valider=("ret",) + ): + """Attend l'écran, applique les saisies `avant`, capture, valide.""" + attendre(etape, textes, delai, absents) + if avant: + avant() + attendre(etape, textes, 60, absents) + capturer(nom, description) + touches(*valider) + + # --- Démarrage sur le DVD ------------------------------------------- + # Le menu affiche un compte à rebours (synthèse vocale au bout de 30 s) : + # pas d'exigence de stabilité, puis une flèche l'arrête. + attendre("menu", ["installer menu (bios mode)"], 120, stable=0.0) + touches("down", "up") + etape( + "menu", + ["installer menu (bios mode)"], + "menu-demarrage", + "menu de démarrage du DVD (BIOS), « Graphical install » sélectionné", + delai=30, + ) + + # --- Langue, pays, clavier (disposition US jusqu'au choix du clavier) -- + attendre("langue", ["select a language"], 300) + cacher_pointeur() + etape( + "langue", + ["select a language"], + "langue", + "choix de la langue : French - Français", + avant=lambda: touches("down", "down", "down", "down"), + ) + etape("pays", ["situation geographique"], "pays", "choix du pays : France") + etape("clavier", ["configurer le clavier"], "clavier", "disposition du clavier : Français") + clavier.disposition = AZERTY + + # --- Réseau ------------------------------------------------------------ + etape( + "nom-machine", + ["veuillez indiquer le nom de ce systeme"], + "nom-machine", + "nom de machine : debian-formation", + delai=600, + avant=lambda: clavier.taper("debian-formation"), + ) + etape("domaine", ["le domaine est la partie"], "domaine", "nom de domaine laissé vide") + + # --- Comptes ----------------------------------------------------------- + def mots_de_passe() -> None: + clavier.taper("cid") + touches("tab", "tab") # la case « Afficher le mot de passe » est sautée + clavier.taper("cid") + + etape( + "root", + ["mot de passe du superutilisateur"], + "mot-de-passe-root", + "mot de passe du superutilisateur root (cid), saisi deux fois", + avant=mots_de_passe, + ) + etape( + "nom-complet", + ["nom complet du nouvel utilisateur"], + "nom-complet", + "nom complet du nouvel utilisateur : cid", + avant=lambda: clavier.taper("cid"), + ) + etape( + "identifiant", + ["identifiant pour le compte"], + "identifiant", + "identifiant du compte utilisateur : cid (proposé)", + ) + etape( + "mdp-utilisateur", + ["mot de passe pour le nouvel utilisateur"], + "mot-de-passe-utilisateur", + "mot de passe de l'utilisateur cid (cid), saisi deux fois", + avant=mots_de_passe, + ) + + # --- Partitionnement --------------------------------------------------- + etape( + "methode", + ["methode de partitionnement"], + "partitionnement-methode", + "partitionnement assisté : utiliser un disque entier", + ) + etape( + "disque", + ["disque a partitionner"], + "partitionnement-disque", + "choix du disque à partitionner (sda, 32,2 Go)", + ) + etape( + "schema", + ["dans le doute, choisissez le premier"], + "partitionnement-schema", + "schéma : tout dans une seule partition", + ) + etape( + "resume", + ["voici la table des partitions"], + "partitionnement-resume", + "résumé : terminer le partitionnement et appliquer les changements", + ) + etape( + "confirmation", + ["appliquer les changements sur les disques"], + "partitionnement-confirmation", + "confirmation de l'écriture sur le disque : Oui", + avant=lambda: touches("down"), + ) + + # --- Système de base --------------------------------------------------- + attendre_progression("base", ["installation du systeme de base"], 0.3, LONG) + capturer("installation-base", "progression : installation du système de base") + + # --- Outil de gestion des paquets -------------------------------------- + etape( + "autres-supports", + ["analyser d'autres supports"], + "autres-supports", + "analyser d'autres supports : Non", + delai=LONG, + ) + etape( + "miroir", + ["utiliser un miroir sur le reseau"], + "miroir-reseau", + "utiliser un miroir sur le réseau : Oui", + avant=lambda: touches("down"), + ) + etape("pays-miroir", ["pays du miroir"], "miroir-pays", "pays du miroir : France") + etape( + "miroir-choix", ["generalement, deb.debian.org"], "miroir-choix", "miroir : deb.debian.org" + ) + etape("mandataire", ["mandataire http"], "mandataire", "mandataire HTTP laissé vide") + + # --- Popularité, logiciels --------------------------------------------- + etape( + "popcon", + ["popularity-contest", "etude statistique"], + "popularite", + "enquête de popularité des paquets : Non", + delai=LONG, + ) + + def cocher_ssh() -> None: + touches(*["down"] * 10) + touches("spc") + + # Entrée sur la liste basculerait la case : Tab vers « Continuer ». + etape( + "logiciels", + ["logiciels a installer"], + "logiciels", + "sélection des logiciels : bureau Debian, GNOME, serveur SSH, utilitaires usuels", + delai=LONG, + avant=cocher_ssh, + valider=("tab", "ret"), + ) + + attendre_progression( + "logiciels-progression", ["choisir et installer des logiciels"], 0.4, TRES_LONG + ) + capturer("installation-logiciels", "progression : choisir et installer des logiciels") + + # --- GRUB et fin ------------------------------------------------------- + etape( + "grub", + ["grub sur le disque principal ?"], + "grub", + "installer GRUB sur le disque principal : Oui", + delai=TRES_LONG, + ) + etape( + "grub-disque", + ["peripherique ou sera installe"], + "grub-disque", + "périphérique d'installation de GRUB : /dev/sda", + avant=lambda: touches("down"), + ) + etape( + "fin", + ["installation est terminee"], + "fin-installation", + "fin de l'installation : redémarrage", + delai=LONG, + ) + + # --- Premier démarrage ------------------------------------------------- + # GRUB n'attend que 5 s : on surveille sans exiger de stabilité longue, + # puis une flèche arrête le compte à rebours. + attendre("grub-menu", ["gnu grub"], 600, stable=0.0) + touches("down", "up") + attendre("grub-menu", ["gnu grub"], 30) + capturer("menu-grub", "premier démarrage : menu GRUB") + touches("ret") + + attendre("connexion", ["absent de la liste"], 900) + cacher_pointeur() + attendre("connexion", ["absent de la liste"], 60) + capturer("connexion", "écran de connexion GNOME : utilisateur cid") + # Le focus clavier initial de GDM varie : clic sur l'utilisateur. + cliquer(640, 349) + cacher_pointeur() + attendre("connexion-mdp", ["cid"], 120, absents=["absent de la liste"]) + clavier.taper("cid") + attendre("connexion-mdp", ["cid"], 60, absents=["absent de la liste"]) + capturer("connexion-mot-de-passe", "saisie du mot de passe de cid") + touches("ret") + + # --- Session GNOME ----------------------------------------------------- + attendre("bienvenue", ["bienvenue dans debian"], 600) + cacher_pointeur() + attendre("bienvenue", ["bienvenue dans debian"], 60) + capturer("bienvenue", "première session : fenêtre de bienvenue (Passer)") + touches("ret") + # Vue d'ensemble des activités : peu de texte lisible, on attend la + # disparition de la fenêtre de bienvenue. + attendre("activites", [""], 120, absents=["bienvenue", "visite guidee"]) + cacher_pointeur() + attendre("activites", [""], 30, absents=["bienvenue", "visite guidee"]) + capturer("bureau", "bureau GNOME : vue d'ensemble des activités") + clavier.taper("terminal") + attendre("recherche", ["terminal"], 120) + capturer("recherche-terminal", "recherche de l'application Terminal") + touches("ret") + attendre("terminal", ["cid@debian-formation"], 120) + capturer("terminal", "terminal ouvert : invite cid@debian-formation") + + qmp.commande("system_powerdown") + + +def finaliser(ecran: Ecran) -> None: + """Optimise les PNG, les copie dans /sortie et écrit la liste.""" + SORTIE.mkdir(parents=True, exist_ok=True) + for ancien in SORTIE.glob("debian-*.png"): + ancien.unlink() + for capture in ecran.captures: + source = BRUT / capture.fichier + cible = SORTIE / capture.fichier + subprocess.run( + [ + "pngquant", + "--quality=80-98", + "--speed", + "1", + "--force", + "--output", + str(cible), + str(source), + ], + check=False, + ) + if not cible.exists(): # qualité non atteinte : PNG sans perte + shutil.copy(source, cible) + subprocess.run(["optipng", "-quiet", "-o2", str(cible)], check=True) + lignes = [ + "# Captures de l'installeur Debian 13.7 (DVD-1), générées par", + "# outils/captures/debian/capturer.sh ; ne pas modifier à la main.", + "", + ] + lignes += [f"{c.fichier} -> {c.description}" for c in ecran.captures] + (SORTIE / "debian-captures.txt").write_text("\n".join(lignes) + "\n", encoding="utf-8") + journal(f"{len(ecran.captures)} captures écrites dans {SORTIE}") + + +def main() -> int: + BRUT.mkdir(parents=True, exist_ok=True) + JOURNAL.write_text("", encoding="utf-8") + if not ISO.exists(): + journal(f"ISO absente : {ISO}") + return 2 + if DISQUE.exists(): + DISQUE.unlink() + subprocess.run(["qemu-img", "create", "-q", "-f", "qcow2", str(DISQUE), "30G"], check=True) + qemu = lancer_qemu(installation=True) + try: + qmp = Qmp(SOCKET_QMP) + ecran = Ecran(qmp) + clavier = Clavier(qmp) + scenario(qmp, ecran, clavier) + finaliser(ecran) + except Echec as erreur: + journal(f"ÉCHEC : {erreur}") + return 1 + finally: + if qemu.poll() is None: + qemu.terminate() + try: + qemu.wait(timeout=30) + except subprocess.TimeoutExpired: + qemu.kill() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/outils/captures/virtualbox/Dockerfile b/outils/captures/virtualbox/Dockerfile new file mode 100644 index 0000000..06ce0b9 --- /dev/null +++ b/outils/captures/virtualbox/Dockerfile @@ -0,0 +1,37 @@ +# Image de capture : VirtualBox 7.2.20 pour Linux, Xvfb, openbox, xdotool. +# Le paquet .deb est fourni par le contexte nomme « cache » +# (build/captures/vbox-contexte/, copie du cache dont capturer.sh a verifie +# le SHA256). +FROM debian:trixie + +ENV DEBIAN_FRONTEND=noninteractive +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Versions non epinglees : outils generiques de Debian 13 ; seul VirtualBox +# (paquet officiel verifie par SHA256) doit etre a la version exacte. +# hadolint ignore=DL3008 +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + xvfb openbox xdotool xclip x11-utils imagemagick optipng locales \ + fonts-dejavu-core fonts-noto-core procps ca-certificates \ + dbus-x11 \ + && sed -i 's/^# *fr_FR.UTF-8/fr_FR.UTF-8/' /etc/locale.gen \ + && locale-gen \ + && rm -rf /var/lib/apt/lists/* + +# Installation du paquet officiel ; le module noyau ne peut pas etre +# compile dans le conteneur (aucune VM n'est demarree, ce n'est pas utile). +# hadolint ignore=DL3008 +RUN --mount=type=bind,from=cache,target=/cache \ + apt-get update \ + && apt-get install -y --no-install-recommends \ + "/cache/virtualbox-7.2_7.2.20-175154~Debian~trixie_amd64.deb" \ + ; dpkg -s virtualbox-7.2 | grep -q '^Status: install ok installed' \ + && rm -rf /var/lib/apt/lists/* + +# Interface en francais. Le pilotage tourne en root : sous Linux, +# l'installation d'une extension exige les droits d'administration +# (sous Windows, l'assistant demande l'elevation UAC). +ENV LANG=fr_FR.UTF-8 LANGUAGE=fr_FR:fr LC_ALL=fr_FR.UTF-8 \ + USER=root LOGNAME=root HOME=/root XDG_RUNTIME_DIR=/tmp/xdg +WORKDIR /root diff --git a/outils/captures/virtualbox/capturer.sh b/outils/captures/virtualbox/capturer.sh new file mode 100755 index 0000000..54c8f83 --- /dev/null +++ b/outils/captures/virtualbox/capturer.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Captures reproductibles de l'interface de VirtualBox 7.2.20 (Linux, en +# francais) pour l'annexe d'installation VirtualBox. +# +# Usage : outils/captures/virtualbox/capturer.sh +# +# 1. Telecharge (si absent) et verifie par SHA256 le paquet Debian de +# VirtualBox, l'Extension Pack et l'image Debian 13.7 DVD-1 dans +# build/captures/cache/. +# 2. Construit l'image Docker formation-linux-vbox. +# 3. Lance pilote.sh dans un conteneur (Xvfb + openbox + xdotool) : aucune +# VM n'est demarree, seules les fenetres sont capturees. +# 4. Copie les PNG optimises dans ressources/images/installation/ avec la +# liste vbox-captures.txt. +set -euo pipefail + +ICI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RACINE="$(cd "$ICI/../../.." && pwd)" +CACHE="$RACINE/build/captures/cache" +TRAVAIL="$RACINE/build/captures/vbox" +CONTEXTE="$RACINE/build/captures/vbox-contexte" +DEST="$RACINE/ressources/images/installation" +IMAGE="formation-linux-vbox" + +VBOX_URL="https://download.virtualbox.org/virtualbox/7.2.20" +DEB="virtualbox-7.2_7.2.20-175154~Debian~trixie_amd64.deb" +DEB_SHA256="04b25e10058a4e0e561498db05c456f9705b8315e3535522d30af5bc9cc06cd7" +EXTPACK="Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack" +EXTPACK_SHA256="0a050da993f2e3cf2e4e9cda2fa8667e44a2b25479b27abd41af6812c1e43591" +ISO_URL="https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd" +ISO="debian-13.7.0-amd64-DVD-1.iso" +ISO_SHA256="347b6c67a3cc0b7ddb60b178f683470c4e2b7ac426c996d9337a2ff36c1a32d2" + +# telecharger_verifie FICHIER URL SHA256 +telecharger_verifie() { + local fichier="$1" url="$2" attendu="$3" + if [ -f "$CACHE/$fichier" ] \ + && echo "$attendu $CACHE/$fichier" | sha256sum --check --status; then + echo "OK (cache) : $fichier" + return + fi + echo "Telechargement : $url" + curl -fL --retry 3 -o "$CACHE/$fichier.part" "$url" + mv "$CACHE/$fichier.part" "$CACHE/$fichier" + if ! echo "$attendu $CACHE/$fichier" | sha256sum --check --status; then + echo "ERREUR : SHA256 incorrect pour $fichier" >&2 + exit 1 + fi + echo "OK (telecharge) : $fichier" +} + +mkdir -p "$CACHE" "$TRAVAIL" "$CONTEXTE" "$DEST" +telecharger_verifie "$DEB" "$VBOX_URL/$DEB" "$DEB_SHA256" +telecharger_verifie "$EXTPACK" "$VBOX_URL/$EXTPACK" "$EXTPACK_SHA256" +telecharger_verifie "$ISO" "$ISO_URL/$ISO" "$ISO_SHA256" + +# Contexte de construction minimal (le paquet seul, pas l'image ISO). +ln -f "$CACHE/$DEB" "$CONTEXTE/$DEB" 2>/dev/null || cp -f "$CACHE/$DEB" "$CONTEXTE/$DEB" +docker build -t "$IMAGE" -f "$ICI/Dockerfile" \ + --build-context cache="$CONTEXTE" "$ICI" + +rm -f "$TRAVAIL"/vbox-*.png "$TRAVAIL"/vbox-captures.txt "$TRAVAIL"/echec.png +# Le dossier du cache est monte a la place de D:\PrenomNOM\sources\ : +# /mnt/D/PrenomNOM/sources (lecture seule). +docker run --rm \ + -e HOTE_UID="$(id -u)" -e HOTE_GID="$(id -g)" \ + -v "$CACHE:/mnt/D/PrenomNOM/sources:ro" \ + -v "$ICI:/outils:ro" \ + -v "$TRAVAIL:/sortie" \ + "$IMAGE" bash /outils/pilote.sh + +rm -f "$DEST"/vbox-*.png +cp "$TRAVAIL"/vbox-*.png "$TRAVAIL/vbox-captures.txt" "$DEST/" +echo +echo "Captures copiees dans $DEST :" +ls -l "$DEST"/vbox-* +echo +echo "Hash de licence de l'Extension Pack : $(cat "$TRAVAIL/extpack-licence.txt")" diff --git a/outils/captures/virtualbox/pilote.sh b/outils/captures/virtualbox/pilote.sh new file mode 100755 index 0000000..19d1be0 --- /dev/null +++ b/outils/captures/virtualbox/pilote.sh @@ -0,0 +1,243 @@ +#!/bin/bash +# Pilotage de VirtualBox 7.2.20 dans le conteneur (lance par capturer.sh). +# Ecran virtuel Xvfb 1280x800, gestionnaire de fenetres openbox, saisies +# par xdotool (texte colle via le presse-papiers : plus fiable que la frappe +# caractere par caractere dans les champs a completion de VirtualBox). +# Les coordonnees de clic sont relatives a la fenetre visee, dont la taille +# est fixee par le script. +set -euo pipefail + +SORTIE=/sortie +LISTE="$SORTIE/vbox-captures.txt" +SOURCES=/mnt/D/PrenomNOM/sources +MACHINES=/mnt/D/PrenomNOM/VirtualBox +ISO="$SOURCES/debian-13.7.0-amd64-DVD-1.iso" +EXTPACK="$SOURCES/Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack" +VBM=/usr/lib/virtualbox/VBoxManage +export DISPLAY=:99 + +journal() { echo "[pilote] $*" >&2; } +echec() { journal "ERREUR : $*"; import -window root "$SORTIE/echec.png" || true; exit 1; } + +# attendre_fenetre TITRE [DELAI] : affiche l'identifiant de la fenetre +# visible dont le titre contient TITRE. +attendre_fenetre() { + local titre="$1" delai="${2:-30}" i w + for ((i = 0; i < delai * 4; i++)); do + w=$(xdotool search --onlyvisible --name "$titre" 2>/dev/null | tail -1 || true) + if [ -n "$w" ]; then echo "$w"; return; fi + sleep 0.25 + done + echec "fenetre « $titre » absente apres ${delai} s" +} + +# attendre_disparition TITRE [DELAI] +attendre_disparition() { + local titre="$1" delai="${2:-60}" i + for ((i = 0; i < delai * 4; i++)); do + xdotool search --onlyvisible --name "$titre" >/dev/null 2>&1 || return 0 + sleep 0.25 + done + echec "fenetre « $titre » toujours presente apres ${delai} s" +} + +# attendre_stable [DELAI] : attend deux captures successives identiques. +attendre_stable() { + local delai="${1:-20}" i + import -window root /tmp/stable-a.png + for ((i = 0; i < delai * 2; i++)); do + sleep 0.5 + import -window root /tmp/stable-b.png + if [ "$(compare -metric AE /tmp/stable-a.png /tmp/stable-b.png null: 2>&1 || true)" = "0" ]; then + return + fi + mv /tmp/stable-b.png /tmp/stable-a.png + done + journal "avertissement : ecran non stabilise apres ${delai} s" +} + +# clic FENETRE X Y : clic gauche en coordonnees relatives a la fenetre. +clic() { xdotool mousemove --window "$1" "$2" "$3" click 1; sleep 0.5; } + +# coller TEXTE : remplace le contenu du champ actif par TEXTE. +coller() { + printf %s "$1" | xclip -selection clipboard + xdotool key ctrl+a ctrl+v + sleep 0.3 +} + +# placer FENETRE LARGEUR HAUTEUR X Y +placer() { + xdotool windowsize "$1" "$2" "$3" + xdotool windowmove "$1" "$4" "$5" + sleep 1 +} + +# capturer NOM FENETRE DESCRIPTION : capture la fenetre avec son cadre. +capturer() { + local nom="$1" w="$2" description="$3" x y l h ext g d hd b + xdotool mousemove 1279 799 # pointeur hors des fenetres + attendre_stable + x=$(xwininfo -id "$w" | awk '/Absolute upper-left X/ {print $4}') + y=$(xwininfo -id "$w" | awk '/Absolute upper-left Y/ {print $4}') + l=$(xwininfo -id "$w" | awk '/Width:/ {print $2}') + h=$(xwininfo -id "$w" | awk '/Height:/ {print $2}') + ext=$(xprop -id "$w" _NET_FRAME_EXTENTS | sed 's/.*= //; s/,//g') + read -r g d hd b <<< "${ext:-0 0 0 0}" + import -window root -crop "$((l + g + d))x$((h + hd + b))+$((x - g))+$((y - hd))" \ + +repage -strip "$SORTIE/$nom.png" + optipng -quiet -o2 "$SORTIE/$nom.png" + printf '%s.png -> %s\n' "$nom" "$description" >> "$LISTE" + journal "capture $nom.png" +} + +# --- Mise en route ---------------------------------------------------------- +mkdir -m 700 "$XDG_RUNTIME_DIR" +Xvfb :99 -screen 0 1280x800x24 -nolisten tcp >/tmp/xvfb.log 2>&1 & +for _ in $(seq 40); do xdpyinfo >/dev/null 2>&1 && break; sleep 0.25; done +xdpyinfo >/dev/null 2>&1 || echec "Xvfb ne demarre pas" +openbox >/tmp/openbox.log 2>&1 & +sleep 1 + +# Message propre au conteneur (pas d'USB hote) : masque, il n'existe pas sur +# un poste Windows normal. +$VBM setextradata global GUI/SuppressMessages cannotEnumerateHostUSBDevices +mkdir -p "$MACHINES" +cat > "$LISTE" <<'FIN' +# Captures VirtualBox 7.2.20 (Linux, interface en francais, Basic Mode) +# Produites par outils/captures/virtualbox/capturer.sh (Xvfb + openbox). +# Chemins : l'assistant refuse un chemin Windows pour l'image ISO (fichier +# introuvable sous Linux) et transforme un dossier D:\... en chemin relatif ; +# les captures montrent donc l'equivalent Linux : +# /mnt/D/PrenomNOM/sources = D:\PrenomNOM\sources +# /mnt/D/PrenomNOM/VirtualBox = D:\PrenomNOM\VirtualBox +# Apparence : fenetres Linux (theme Qt Fusion, cadres openbox) ; libelles +# identiques a la version Windows. Plusieurs libelles de la 7.2.20 ne sont +# pas traduits en francais (assistant, accueil) : c'est le cas aussi sous +# Windows. Carte reseau de l'hote : eth0 (sous Windows, nom de la carte +# Ethernet du poste). +FIN + +/usr/bin/VirtualBox >/tmp/virtualbox.log 2>&1 & +GEST=$(attendre_fenetre "Gestionnaire de machines" 60) +placer "$GEST" 1100 720 90 40 + +# --- 1. Premier lancement -------------------------------------------------- +capturer vbox-01-premier-lancement "$GEST" \ + "Gestionnaire au premier lancement : choix Basic Mode / Expert Mode (textes d'accueil non traduits dans la 7.2.20)" +clic "$GEST" 990 396 # Basic Mode +sleep 1 + +# --- 2. Extension Pack ------------------------------------------------------ +xdotool key ctrl+t # Outils > Extensions +sleep 1 +capturer vbox-02-outil-extensions "$GEST" \ + "Outil Extensions (Fichier > Outils > Extensions, Ctrl+T) : liste vide, bouton Install" +clic "$GEST" 72 50 # Install +attendre_fenetre "Choisissez un fichier extension" >/dev/null +sleep 0.5 +coller "$EXTPACK" +xdotool key Return +QUESTION=$(attendre_fenetre "VirtualBox - Question") +capturer vbox-03-extension-confirmation "$QUESTION" \ + "Confirmation : Oracle VirtualBox Extension Pack, version 7.2.20r175154, bouton Installation" +xdotool key Return # Installation (bouton par defaut) +LIC=$(attendre_fenetre "Licence VirtualBox") +sleep 1 +capturer vbox-04-extension-licence-debut "$LIC" \ + "Licence PUEL de l'Extension Pack : boutons J'accepte / Je n'accepte pas grises tant que le texte n'est pas lu" +xdotool mousemove --window "$LIC" 290 250 +xdotool click --repeat 250 --delay 5 5 # molette jusqu'en bas du texte +sleep 1 +capturer vbox-05-extension-licence-fin "$LIC" \ + "Licence lue jusqu'au bout : bouton J'accepte actif" +clic "$LIC" 424 426 # J'accepte +attendre_disparition "Licence VirtualBox" 120 +for _ in $(seq 120); do + $VBM list extpacks 2>/dev/null | grep -q "Usable: *true" && break + sleep 0.5 +done +$VBM list extpacks | grep -q "Version: *7.2.20" || echec "Extension Pack non installe" +sleep 2 +capturer vbox-06-extension-installee "$GEST" \ + "Extension installee : Oracle VirtualBox Extension Pack, active, version 7.2.20r175154" + +# --- 3. Assistant Nouvelle machine virtuelle -------------------------------- +clic "$GEST" 23 41 # outil Home +clic "$GEST" 79 50 # Nouvelle +WIZ=$(attendre_fenetre "New Virtual Machine") +sleep 1 +coller FormationLinux # champ VM Name (focus initial) +xdotool key Tab +coller "$MACHINES" # VM Folder +xdotool key Tab +coller "$ISO" # ISO Image +xdotool key Tab +sleep 3 # detection du systeme dans l'ISO +capturer vbox-07-nouvelle-machine-iso-detectee "$WIZ" \ + "Assistant, page 1 : nom FormationLinux, dossier et ISO saisis, Debian 13 Trixie (64-bit) detecte, installation sans surveillance cochee par defaut" +clic "$WIZ" 250 298 # decocher Proceed with Unattended Installation +capturer vbox-08-nouvelle-machine-sans-surveillance-decochee "$WIZ" \ + "Assistant, page 1 : case Proceed with Unattended Installation decochee (installation manuelle)" +xdotool key alt+s # Suivant +sleep 1.5 +xdotool key alt+m; coller 4096 # Base Memory +xdotool key alt+n; coller 2 # Number of CPUs +clic "$WIZ" 752 199; coller "30 Gio" # Disk Size (le raccourci Alt+K vise le curseur) +xdotool key Tab +capturer vbox-09-nouvelle-machine-materiel "$WIZ" \ + "Assistant, page 2 : 4096 Mo de memoire, 2 CPU, disque de 30 Gio (VDI dynamique par defaut en Basic Mode), EFI non coche" +xdotool key alt+s # Suivant +sleep 1.5 +capturer vbox-10-nouvelle-machine-recapitulatif "$WIZ" \ + "Assistant, Recapitulatif : FormationLinux, dossier, ISO, Debian 13 Trixie (64-bit), sans surveillance false, 4096, 2 processeurs, 30,00 Gio" +xdotool key alt+f # Finish +attendre_disparition "New Virtual Machine" 60 +sleep 2 +$VBM showvminfo FormationLinux >/dev/null || echec "VM FormationLinux non creee" +capturer vbox-11-machine-creee "$GEST" \ + "Gestionnaire : machine FormationLinux creee (Eteinte), details 4096 Mo, 2 processeurs, ISO et FormationLinux.vdi 30,00 Gio, reseau NAT par defaut" + +# --- 4. Configuration : reseau par pont, processeur ------------------------- +clic "$GEST" 211 50 # Configuration +CONF=$(attendre_fenetre "FormationLinux - Settings") +placer "$CONF" 1000 680 140 60 +clic "$CONF" 71 269 # section Reseau +clic "$CONF" 466 208 # liste « Attached to » +xdotool key Down Return # NAT -> Acces par pont +sleep 1 +capturer vbox-12-configuration-reseau-pont "$CONF" \ + "Configuration > Reseau : Adapter 1 active, Attached to = Acces par pont, Name = eth0 (carte reseau de l'hote)" +clic "$CONF" 71 109 # section System +clic "$CONF" 383 96 # onglet Processeur +capturer vbox-13-configuration-processeur "$CONF" \ + "Configuration > System > Processeur : Number of CPUs = 2" +clic "$CONF" 778 658 # OK +attendre_disparition "FormationLinux - Settings" 30 +$VBM showvminfo FormationLinux --machinereadable | grep -q '^nic1="bridged"' \ + || echec "le reseau n'est pas en acces par pont" +sleep 1 +capturer vbox-14-machine-configuree "$GEST" \ + "Gestionnaire : details de FormationLinux, Reseau : Interface 1 en Acces par pont" + +# --- 5. Verifications et hash de licence ------------------------------------ +$VBM showvminfo FormationLinux --machinereadable > "$SORTIE/vm-formationlinux.txt" +$VBM showmediuminfo "$MACHINES/FormationLinux/FormationLinux.vdi" > "$SORTIE/disque-formationlinux.txt" +grep -q "Format variant: *dynamic" "$SORTIE/disque-formationlinux.txt" \ + || echec "disque non dynamique" +grep -q "Capacity: *30720 MBytes" "$SORTIE/disque-formationlinux.txt" \ + || echec "disque different de 30 Gio" +grep -q "Storage format: *VDI" "$SORTIE/disque-formationlinux.txt" \ + || echec "disque non VDI" +grep -q '^memory=4096$' "$SORTIE/vm-formationlinux.txt" || echec "memoire differente de 4096 Mo" +grep -q '^cpus=2$' "$SORTIE/vm-formationlinux.txt" || echec "nombre de CPU different de 2" +HASH=$(echo y | $VBM extpack install --replace "$EXTPACK" 2>&1 \ + | grep -o -- '--accept-license=[0-9a-f]*' | cut -d= -f2) +ATTENDU=$(tar -xzOf "$EXTPACK" ./ExtPack-license.txt | sha256sum | cut -d' ' -f1) +[ -n "$HASH" ] && [ "$HASH" = "$ATTENDU" ] || echec "hash de licence inattendu : $HASH" +echo "$HASH" > "$SORTIE/extpack-licence.txt" +journal "hash de licence de l'Extension Pack : $HASH" + +/usr/lib/virtualbox/VBoxManage --version > "$SORTIE/version.txt" +chown -R "${HOTE_UID:-0}:${HOTE_GID:-0}" "$SORTIE" +journal "termine" diff --git a/pdf/documents.yaml b/pdf/documents.yaml index 48a0c0c..98b7e3c 100644 --- a/pdf/documents.yaml +++ b/pdf/documents.yaml @@ -77,8 +77,13 @@ documents: - travaux_pratiques/tp_additionnels/tp_docker/tp3_volumes_donnees.md - travaux_pratiques/tp_additionnels/tp_docker/tp4_reseaux_communication.md - travaux_pratiques/tp_additionnels/tp_docker/tp5_compose_orchestration.md - - fichier: annexe_installation.pdf - titre: Installer son environnement de travail + - fichier: annexe_installation_ssh.pdf + titre: Se connecter à sa VM distante (SSH) sous_titre: Annexe sources: - - cours_2026/annexes/installation.md + - cours_2026/annexes/installation_ssh.md + - fichier: annexe_installation_virtualbox.pdf + titre: Installer Debian 13 dans VirtualBox + sous_titre: Annexe + sources: + - cours_2026/annexes/installation_virtualbox.md diff --git a/pdf/generer.py b/pdf/generer.py index 641b342..7cd5202 100644 --- a/pdf/generer.py +++ b/pdf/generer.py @@ -20,7 +20,9 @@ RACINE = Path(__file__).resolve().parent.parent DOSSIER_PDF = Path(__file__).resolve().parent CHAMPS_DOCUMENT = {"fichier", "titre", "sous_titre", "sources"} -FORMAT_MARKDOWN = "markdown+lists_without_preceding_blankline" +# rebase_relative_paths : le chemin d'une image est relatif à son fichier source. +# L'extension réécrit aussi les liens relatifs (sans effet utile dans un PDF). +FORMAT_MARKDOWN = "markdown+lists_without_preceding_blankline+rebase_relative_paths" LOGO_LICENCE = RACINE / "ressources" / "images" / "licenses" / "cc-by-nc-sa.png" _ECHAPPEMENTS_LATEX = { "\\": r"\textbackslash{}", @@ -111,13 +113,25 @@ def _lire_document(entree: object, contexte: str, racine: Path) -> Document: return Document(fichier, entree["titre"], tuple(sources), entree.get("sous_titre", "")) -def executer(commande: list[str], entree: str | None = None, cwd: Path | None = None) -> str: - """Lance une commande, renvoie sa sortie ; lève ErreurBuild en cas d'échec.""" +def executer( + commande: list[str], + entree: str | None = None, + cwd: Path | None = None, + echec: str | None = None, +) -> str: + """Lance une commande, renvoie sa sortie ; lève ErreurBuild en cas d'échec. + + Le message d'erreur donne echec (par défaut « échec de »), puis + la sortie d'erreur, puis la commande complète. + """ resultat = subprocess.run( commande, input=entree, capture_output=True, text=True, cwd=cwd, check=False ) if resultat.returncode != 0: - raise ErreurBuild(f"échec de : {' '.join(commande)}\n{resultat.stderr.strip()}") + raise ErreurBuild( + f"{echec or f'échec de {commande[0]}'}\n{resultat.stderr.strip()}\n" + f"commande : {' '.join(commande)}" + ) return resultat.stdout @@ -197,8 +211,18 @@ def generer_couverture(document: Document, chapitres: list[str], logo: Path) -> """ +def dossier_medias(tex: Path) -> Path: + """Dossier (absolu) où pandoc copie les images d'un .tex.""" + return tex.absolute().with_name(f"{tex.stem}-media") + + def commande_pandoc(document: Document, auteur: str, couverture: Path, sortie: Path) -> list[str]: - """Commande pandoc Markdown -> LaTeX autonome pour un document.""" + """Commande pandoc Markdown -> LaTeX autonome pour un document. + + Les images sont copiées dans dossier_medias(sortie), en chemin absolu : le + .tex reste valide quel que soit le dossier de compilation. Une image introuvable + n'est qu'un avertissement pour pandoc, d'où --fail-if-warnings. + """ return [ "pandoc", "-f", @@ -210,6 +234,8 @@ def commande_pandoc(document: Document, auteur: str, couverture: Path, sortie: P "--standalone", "--output", str(sortie), + f"--extract-media={dossier_medias(sortie)}", + "--fail-if-warnings", "--top-level-division=chapter", "--toc", "--toc-depth=2", @@ -293,7 +319,13 @@ def construire(document: Document, auteur: str, sortie: Path) -> Path: couverture = debug / f"{nom}-couverture.tex" couverture.write_text(generer_couverture(document, chapitres, LOGO_LICENCE), encoding="utf-8") tex = debug / f"{nom}.tex" - executer(commande_pandoc(document, auteur, couverture, tex)) + medias = dossier_medias(tex) + if medias.exists(): + shutil.rmtree(medias) + executer( + commande_pandoc(document, auteur, couverture, tex), + echec=f"pandoc a échoué pour {document.fichier}", + ) shutil.copy2(compiler(tex), pdf) return pdf diff --git a/pdf/preambule.tex b/pdf/preambule.tex index 25a6e49..939a4a2 100644 --- a/pdf/preambule.tex +++ b/pdf/preambule.tex @@ -38,3 +38,22 @@ \exp_args:NV \__formation_texttt:n \l__formation_code_tl } \ExplSyntaxOff + +% Images : jamais de flottant (la figure reste à sa place dans le texte), +% au plus 75 % de la ligne et 45 % de la hauteur de texte, proportions +% conservées. Remplace le \pandocbounded du modèle pandoc (même principe), +% que le modèle ne définit que si le document contient des images. +\usepackage{float} +\floatplacement{figure}{H} +\makeatletter +\@ifundefined{pandoc@box}{\newsavebox\pandoc@box}{} +\def\pandocbounded#1{% + \sbox\pandoc@box{#1}% + \Gscale@div\@tempa{0.45\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}% + \Gscale@div\@tempb{0.75\linewidth}{\wd\pandoc@box}% + \ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi + \ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}% + \else\usebox{\pandoc@box}% + \fi +} +\makeatother diff --git a/pdf/tests/test_documents.py b/pdf/tests/test_documents.py index 78c116b..46dc9f5 100644 --- a/pdf/tests/test_documents.py +++ b/pdf/tests/test_documents.py @@ -82,7 +82,8 @@ def test_selectionne_les_documents_demandes(): "module_08_automatisation.pdf", "module_additionnel_git.pdf", "module_additionnel_docker.pdf", - "annexe_installation.pdf", + "annexe_installation_ssh.pdf", + "annexe_installation_virtualbox.pdf", ] @@ -90,7 +91,7 @@ def catalogue_du_depot(): return generer.charger_documents(generer.DOSSIER_PDF / "documents.yaml", generer.RACINE) -def test_le_catalogue_du_depot_declare_les_11_pdf(): +def test_le_catalogue_du_depot_declare_les_12_pdf(): assert [document.fichier for document in catalogue_du_depot().documents] == ATTENDUS diff --git a/pdf/tests/test_images.py b/pdf/tests/test_images.py new file mode 100644 index 0000000..0f5462d --- /dev/null +++ b/pdf/tests/test_images.py @@ -0,0 +1,170 @@ +"""Tests des images : résolution des chemins, échec si absente, mise en page.""" + +import struct +import subprocess +import zlib +from pathlib import Path + +import pypdf +import pytest + +import generer + +# A4 (21 x 29,7 cm), marges de 2,2 cm : surface de texte en pouces. +LARGEUR_LIGNE_POUCES = (21 - 2 * 2.2) / 2.54 +HAUTEUR_TEXTE_POUCES = (29.7 - 2 * 2.2) / 2.54 + + +def ecrire_png(chemin: Path, largeur: int, hauteur: int) -> Path: + """PNG RVB en dégradé, écrit sans dépendance externe.""" + ligne = b"\x00" + b"".join(bytes((x * 255 // largeur, 100, 200)) for x in range(largeur)) + + def bloc(type_bloc: bytes, donnees: bytes) -> bytes: + crc = zlib.crc32(type_bloc + donnees) & 0xFFFFFFFF + return struct.pack(">I", len(donnees)) + type_bloc + donnees + struct.pack(">I", crc) + + entete = struct.pack(">IIBBBBB", largeur, hauteur, 8, 2, 0, 0, 0) + chemin.parent.mkdir(parents=True, exist_ok=True) + chemin.write_bytes( + b"\x89PNG\r\n\x1a\n" + + bloc(b"IHDR", entete) + + bloc(b"IDAT", zlib.compress(ligne * hauteur)) + + bloc(b"IEND", b"") + ) + return chemin + + +def ecrire_source(chemin: Path, texte: str) -> Path: + chemin.parent.mkdir(parents=True, exist_ok=True) + chemin.write_text(texte, encoding="utf-8") + return chemin + + +def construire(tmp_path: Path, *sources: Path) -> Path: + document = generer.Document("doc.pdf", "Doc", tuple(sources)) + return generer.construire(document, "Auteur", tmp_path / "sortie") + + +def images_du_texte(pdf: Path) -> list[tuple[int, int, int, float, float]]: + """(page, largeur, hauteur, ppp x, ppp y) des images hors couverture (pdfimages).""" + sortie = subprocess.run( + ["pdfimages", "-list", str(pdf)], capture_output=True, text=True, check=True + ).stdout + images = [] + for ligne in sortie.splitlines()[2:]: + champs = ligne.split() + if champs[2] == "image" and champs[0] != "1": + page, largeur, hauteur = (int(champs[indice]) for indice in (0, 3, 4)) + images.append((page, largeur, hauteur, float(champs[12]), float(champs[13]))) + return images + + +def test_commande_pandoc_resout_les_images_et_echoue_sur_avertissement(): + document = generer.Document("doc.pdf", "Doc", (Path("/s/a.md"),)) + + commande = generer.commande_pandoc(document, "A", Path("/d/couv.tex"), Path("/d/doc.tex")) + + assert "rebase_relative_paths" in commande[commande.index("-f") + 1] + assert "--fail-if-warnings" in commande + assert f"--extract-media={generer.dossier_medias(Path('/d/doc.tex'))}" in commande + assert generer.dossier_medias(Path("/d/doc.tex")) == Path("/d/doc-media") + + +def test_image_relative_au_fichier_source_apparait_dans_le_pdf(tmp_path): + ecrire_png(tmp_path / "chap" / "img" / "capture.png", 200, 100) + source = ecrire_source( + tmp_path / "chap" / "a.md", "# Chapitre\n\nTexte.\n\n![Une capture](img/capture.png)\n" + ) + + pdf = construire(tmp_path, source) + + pages = pypdf.PdfReader(pdf).pages + assert any(page.images for page in pages) + texte = subprocess.run(["pdftotext", str(pdf), "-"], capture_output=True, text=True).stdout + assert "Une capture" in texte + + +def test_images_de_meme_nom_dans_deux_dossiers_restent_distinctes(tmp_path): + ecrire_png(tmp_path / "a" / "fig.png", 300, 100) + ecrire_png(tmp_path / "b" / "fig.png", 100, 300) + source_a = ecrire_source(tmp_path / "a" / "a.md", "# A\n\n![Figure A](fig.png)\n") + source_b = ecrire_source(tmp_path / "b" / "b.md", "# B\n\n![Figure B](fig.png)\n") + + pdf = construire(tmp_path, source_a, source_b) + + tailles = {(largeur, hauteur) for _, largeur, hauteur, _, _ in images_du_texte(pdf)} + assert tailles == {(300, 100), (100, 300)} + + +def test_image_absente_fait_echouer_le_build_en_la_nommant(tmp_path): + source = ecrire_source(tmp_path / "chap" / "a.md", "# Chapitre\n\n![Perdue](absente.png)\n") + + with pytest.raises(generer.ErreurBuild) as erreur: + construire(tmp_path, source) + + lignes = str(erreur.value).splitlines() + assert lignes[0] == "pandoc a échoué pour doc.pdf" + assert "absente.png" in lignes[1] + assert lignes[-1].startswith("commande : pandoc ") + assert not (tmp_path / "sortie" / "doc.pdf").exists() + + +def test_une_image_modifiee_ne_laisse_qu_un_fichier_media(tmp_path): + image = ecrire_png(tmp_path / "img.png", 200, 100) + source = ecrire_source(tmp_path / "a.md", "# Chapitre\n\n![Capture](img.png)\n") + construire(tmp_path, source) + ecrire_png(image, 100, 200) + + construire(tmp_path, source) + + medias = generer.dossier_medias(tmp_path / "sortie" / "debug" / "doc.tex") + assert len(list(medias.iterdir())) == 1 + + +def test_image_large_limitee_a_75_pour_cent_de_la_ligne(tmp_path): + ecrire_png(tmp_path / "large.png", 1600, 800) + source = ecrire_source(tmp_path / "a.md", "# Chapitre\n\n![Large](large.png)\n") + + [(_, _, _, ppp_x, ppp_y)] = images_du_texte(construire(tmp_path, source)) + + assert 1600 / ppp_x == pytest.approx(0.75 * LARGEUR_LIGNE_POUCES, rel=0.02) + assert ppp_x == pytest.approx(ppp_y, rel=0.01) + + +def test_image_haute_limitee_a_45_pour_cent_de_la_page(tmp_path): + ecrire_png(tmp_path / "haute.png", 300, 1200) + source = ecrire_source(tmp_path / "a.md", "# Chapitre\n\n![Haute](haute.png)\n") + + [(_, _, _, ppp_x, ppp_y)] = images_du_texte(construire(tmp_path, source)) + + # La hauteur de texte réelle est un peu inférieure (en-tête de page). + hauteur = 1200 / ppp_y + assert 0.40 * HAUTEUR_TEXTE_POUCES < hauteur <= 0.45 * HAUTEUR_TEXTE_POUCES + assert ppp_x == pytest.approx(ppp_y, rel=0.01) + + +def test_image_reste_a_sa_place_dans_le_texte(tmp_path): + ecrire_png(tmp_path / "haute.png", 300, 1200) + source = ecrire_source( + tmp_path / "a.md", + "# Chapitre\n\n\\vspace*{0.6\\textheight}\n\nAVANT\n\n![Légende](haute.png)\n\nAPRES\n", + ) + + pdf = construire(tmp_path, source) + + [(page_image, *_)] = images_du_texte(pdf) + texte_page = pypdf.PdfReader(pdf).pages[page_image - 1].extract_text() + # Un flottant htbp partirait seul à la page suivante, APRES restant avant lui. + assert "APRES" in texte_page + assert texte_page.index("Légende") < texte_page.index("APRES") + assert "AVANT" not in texte_page + + +def test_image_trouvee_avec_un_dossier_de_sortie_relatif(tmp_path, monkeypatch): + ecrire_png(tmp_path / "chap" / "img.png", 200, 100) + source = ecrire_source(tmp_path / "chap" / "a.md", "# Chapitre\n\n![Capture](img.png)\n") + monkeypatch.chdir(tmp_path) + + pdf = generer.construire(generer.Document("doc.pdf", "Doc", (source,)), "A", Path("sortie")) + + assert len(images_du_texte(pdf)) == 1 diff --git a/pdf/tests/test_titres.py b/pdf/tests/test_titres.py index 149af28..29ba3ec 100644 --- a/pdf/tests/test_titres.py +++ b/pdf/tests/test_titres.py @@ -29,5 +29,13 @@ def test_refuse_zero_ou_plusieurs_titres_de_niveau_1(tmp_path, contenu): def test_signale_une_commande_en_echec(): - with pytest.raises(generer.ErreurBuild, match="échec de : false"): - generer.executer(["false"]) + with pytest.raises(generer.ErreurBuild) as erreur: + generer.executer(["sh", "-c", "echo raison >&2; false"]) + + lignes = str(erreur.value).splitlines() + assert lignes == ["échec de sh", "raison", "commande : sh -c echo raison >&2; false"] + + +def test_echec_avec_message_personnalise(): + with pytest.raises(generer.ErreurBuild, match="^pandoc a échoué pour x.pdf\n"): + generer.executer(["false"], echec="pandoc a échoué pour x.pdf") diff --git a/ressources/images/installation/debian-01-menu-demarrage.png b/ressources/images/installation/debian-01-menu-demarrage.png new file mode 100644 index 0000000..9d80832 Binary files /dev/null and b/ressources/images/installation/debian-01-menu-demarrage.png differ diff --git a/ressources/images/installation/debian-02-langue.png b/ressources/images/installation/debian-02-langue.png new file mode 100644 index 0000000..ab988d7 Binary files /dev/null and b/ressources/images/installation/debian-02-langue.png differ diff --git a/ressources/images/installation/debian-03-pays.png b/ressources/images/installation/debian-03-pays.png new file mode 100644 index 0000000..9d4af00 Binary files /dev/null and b/ressources/images/installation/debian-03-pays.png differ diff --git a/ressources/images/installation/debian-04-clavier.png b/ressources/images/installation/debian-04-clavier.png new file mode 100644 index 0000000..23f9445 Binary files /dev/null and b/ressources/images/installation/debian-04-clavier.png differ diff --git a/ressources/images/installation/debian-05-nom-machine.png b/ressources/images/installation/debian-05-nom-machine.png new file mode 100644 index 0000000..e8ec7f4 Binary files /dev/null and b/ressources/images/installation/debian-05-nom-machine.png differ diff --git a/ressources/images/installation/debian-06-domaine.png b/ressources/images/installation/debian-06-domaine.png new file mode 100644 index 0000000..a2c3469 Binary files /dev/null and b/ressources/images/installation/debian-06-domaine.png differ diff --git a/ressources/images/installation/debian-07-mot-de-passe-root.png b/ressources/images/installation/debian-07-mot-de-passe-root.png new file mode 100644 index 0000000..4be1ebb Binary files /dev/null and b/ressources/images/installation/debian-07-mot-de-passe-root.png differ diff --git a/ressources/images/installation/debian-08-nom-complet.png b/ressources/images/installation/debian-08-nom-complet.png new file mode 100644 index 0000000..52959b9 Binary files /dev/null and b/ressources/images/installation/debian-08-nom-complet.png differ diff --git a/ressources/images/installation/debian-09-identifiant.png b/ressources/images/installation/debian-09-identifiant.png new file mode 100644 index 0000000..c2c3a33 Binary files /dev/null and b/ressources/images/installation/debian-09-identifiant.png differ diff --git a/ressources/images/installation/debian-10-mot-de-passe-utilisateur.png b/ressources/images/installation/debian-10-mot-de-passe-utilisateur.png new file mode 100644 index 0000000..da49589 Binary files /dev/null and b/ressources/images/installation/debian-10-mot-de-passe-utilisateur.png differ diff --git a/ressources/images/installation/debian-11-partitionnement-methode.png b/ressources/images/installation/debian-11-partitionnement-methode.png new file mode 100644 index 0000000..6d734ea Binary files /dev/null and b/ressources/images/installation/debian-11-partitionnement-methode.png differ diff --git a/ressources/images/installation/debian-12-partitionnement-disque.png b/ressources/images/installation/debian-12-partitionnement-disque.png new file mode 100644 index 0000000..a872568 Binary files /dev/null and b/ressources/images/installation/debian-12-partitionnement-disque.png differ diff --git a/ressources/images/installation/debian-13-partitionnement-schema.png b/ressources/images/installation/debian-13-partitionnement-schema.png new file mode 100644 index 0000000..393b931 Binary files /dev/null and b/ressources/images/installation/debian-13-partitionnement-schema.png differ diff --git a/ressources/images/installation/debian-14-partitionnement-resume.png b/ressources/images/installation/debian-14-partitionnement-resume.png new file mode 100644 index 0000000..68541f8 Binary files /dev/null and b/ressources/images/installation/debian-14-partitionnement-resume.png differ diff --git a/ressources/images/installation/debian-15-partitionnement-confirmation.png b/ressources/images/installation/debian-15-partitionnement-confirmation.png new file mode 100644 index 0000000..6c19867 Binary files /dev/null and b/ressources/images/installation/debian-15-partitionnement-confirmation.png differ diff --git a/ressources/images/installation/debian-16-installation-base.png b/ressources/images/installation/debian-16-installation-base.png new file mode 100644 index 0000000..1cd08d4 Binary files /dev/null and b/ressources/images/installation/debian-16-installation-base.png differ diff --git a/ressources/images/installation/debian-17-autres-supports.png b/ressources/images/installation/debian-17-autres-supports.png new file mode 100644 index 0000000..851c0c0 Binary files /dev/null and b/ressources/images/installation/debian-17-autres-supports.png differ diff --git a/ressources/images/installation/debian-18-miroir-reseau.png b/ressources/images/installation/debian-18-miroir-reseau.png new file mode 100644 index 0000000..4bc1030 Binary files /dev/null and b/ressources/images/installation/debian-18-miroir-reseau.png differ diff --git a/ressources/images/installation/debian-19-miroir-pays.png b/ressources/images/installation/debian-19-miroir-pays.png new file mode 100644 index 0000000..ac1b633 Binary files /dev/null and b/ressources/images/installation/debian-19-miroir-pays.png differ diff --git a/ressources/images/installation/debian-20-miroir-choix.png b/ressources/images/installation/debian-20-miroir-choix.png new file mode 100644 index 0000000..afa3dd5 Binary files /dev/null and b/ressources/images/installation/debian-20-miroir-choix.png differ diff --git a/ressources/images/installation/debian-21-mandataire.png b/ressources/images/installation/debian-21-mandataire.png new file mode 100644 index 0000000..e9cf6f7 Binary files /dev/null and b/ressources/images/installation/debian-21-mandataire.png differ diff --git a/ressources/images/installation/debian-22-popularite.png b/ressources/images/installation/debian-22-popularite.png new file mode 100644 index 0000000..a3645a6 Binary files /dev/null and b/ressources/images/installation/debian-22-popularite.png differ diff --git a/ressources/images/installation/debian-23-logiciels.png b/ressources/images/installation/debian-23-logiciels.png new file mode 100644 index 0000000..c4c1927 Binary files /dev/null and b/ressources/images/installation/debian-23-logiciels.png differ diff --git a/ressources/images/installation/debian-24-installation-logiciels.png b/ressources/images/installation/debian-24-installation-logiciels.png new file mode 100644 index 0000000..fc93696 Binary files /dev/null and b/ressources/images/installation/debian-24-installation-logiciels.png differ diff --git a/ressources/images/installation/debian-25-grub.png b/ressources/images/installation/debian-25-grub.png new file mode 100644 index 0000000..cd9f172 Binary files /dev/null and b/ressources/images/installation/debian-25-grub.png differ diff --git a/ressources/images/installation/debian-26-grub-disque.png b/ressources/images/installation/debian-26-grub-disque.png new file mode 100644 index 0000000..c898662 Binary files /dev/null and b/ressources/images/installation/debian-26-grub-disque.png differ diff --git a/ressources/images/installation/debian-27-fin-installation.png b/ressources/images/installation/debian-27-fin-installation.png new file mode 100644 index 0000000..bc5412c Binary files /dev/null and b/ressources/images/installation/debian-27-fin-installation.png differ diff --git a/ressources/images/installation/debian-28-menu-grub.png b/ressources/images/installation/debian-28-menu-grub.png new file mode 100644 index 0000000..ba53e94 Binary files /dev/null and b/ressources/images/installation/debian-28-menu-grub.png differ diff --git a/ressources/images/installation/debian-29-connexion.png b/ressources/images/installation/debian-29-connexion.png new file mode 100644 index 0000000..228b9e4 Binary files /dev/null and b/ressources/images/installation/debian-29-connexion.png differ diff --git a/ressources/images/installation/debian-30-connexion-mot-de-passe.png b/ressources/images/installation/debian-30-connexion-mot-de-passe.png new file mode 100644 index 0000000..0541286 Binary files /dev/null and b/ressources/images/installation/debian-30-connexion-mot-de-passe.png differ diff --git a/ressources/images/installation/debian-31-bienvenue.png b/ressources/images/installation/debian-31-bienvenue.png new file mode 100644 index 0000000..73aba32 Binary files /dev/null and b/ressources/images/installation/debian-31-bienvenue.png differ diff --git a/ressources/images/installation/debian-32-bureau.png b/ressources/images/installation/debian-32-bureau.png new file mode 100644 index 0000000..bd18f28 Binary files /dev/null and b/ressources/images/installation/debian-32-bureau.png differ diff --git a/ressources/images/installation/debian-33-recherche-terminal.png b/ressources/images/installation/debian-33-recherche-terminal.png new file mode 100644 index 0000000..edc52a6 Binary files /dev/null and b/ressources/images/installation/debian-33-recherche-terminal.png differ diff --git a/ressources/images/installation/debian-34-terminal.png b/ressources/images/installation/debian-34-terminal.png new file mode 100644 index 0000000..2a1c7b9 Binary files /dev/null and b/ressources/images/installation/debian-34-terminal.png differ diff --git a/ressources/images/installation/debian-captures.txt b/ressources/images/installation/debian-captures.txt new file mode 100644 index 0000000..b7ae478 --- /dev/null +++ b/ressources/images/installation/debian-captures.txt @@ -0,0 +1,37 @@ +# Captures de l'installeur Debian 13.7 (DVD-1), générées par +# outils/captures/debian/capturer.sh ; ne pas modifier à la main. + +debian-01-menu-demarrage.png -> menu de démarrage du DVD (BIOS), « Graphical install » sélectionné +debian-02-langue.png -> choix de la langue : French - Français +debian-03-pays.png -> choix du pays : France +debian-04-clavier.png -> disposition du clavier : Français +debian-05-nom-machine.png -> nom de machine : debian-formation +debian-06-domaine.png -> nom de domaine laissé vide +debian-07-mot-de-passe-root.png -> mot de passe du superutilisateur root (cid), saisi deux fois +debian-08-nom-complet.png -> nom complet du nouvel utilisateur : cid +debian-09-identifiant.png -> identifiant du compte utilisateur : cid (proposé) +debian-10-mot-de-passe-utilisateur.png -> mot de passe de l'utilisateur cid (cid), saisi deux fois +debian-11-partitionnement-methode.png -> partitionnement assisté : utiliser un disque entier +debian-12-partitionnement-disque.png -> choix du disque à partitionner (sda, 32,2 Go) +debian-13-partitionnement-schema.png -> schéma : tout dans une seule partition +debian-14-partitionnement-resume.png -> résumé : terminer le partitionnement et appliquer les changements +debian-15-partitionnement-confirmation.png -> confirmation de l'écriture sur le disque : Oui +debian-16-installation-base.png -> progression : installation du système de base +debian-17-autres-supports.png -> analyser d'autres supports : Non +debian-18-miroir-reseau.png -> utiliser un miroir sur le réseau : Oui +debian-19-miroir-pays.png -> pays du miroir : France +debian-20-miroir-choix.png -> miroir : deb.debian.org +debian-21-mandataire.png -> mandataire HTTP laissé vide +debian-22-popularite.png -> enquête de popularité des paquets : Non +debian-23-logiciels.png -> sélection des logiciels : bureau Debian, GNOME, serveur SSH, utilitaires usuels +debian-24-installation-logiciels.png -> progression : choisir et installer des logiciels +debian-25-grub.png -> installer GRUB sur le disque principal : Oui +debian-26-grub-disque.png -> périphérique d'installation de GRUB : /dev/sda +debian-27-fin-installation.png -> fin de l'installation : redémarrage +debian-28-menu-grub.png -> premier démarrage : menu GRUB +debian-29-connexion.png -> écran de connexion GNOME : utilisateur cid +debian-30-connexion-mot-de-passe.png -> saisie du mot de passe de cid +debian-31-bienvenue.png -> première session : fenêtre de bienvenue (Passer) +debian-32-bureau.png -> bureau GNOME : vue d'ensemble des activités +debian-33-recherche-terminal.png -> recherche de l'application Terminal +debian-34-terminal.png -> terminal ouvert : invite cid@debian-formation diff --git a/ressources/images/installation/vbox-01-premier-lancement.png b/ressources/images/installation/vbox-01-premier-lancement.png new file mode 100644 index 0000000..ba50d94 Binary files /dev/null and b/ressources/images/installation/vbox-01-premier-lancement.png differ diff --git a/ressources/images/installation/vbox-02-outil-extensions.png b/ressources/images/installation/vbox-02-outil-extensions.png new file mode 100644 index 0000000..fd898f0 Binary files /dev/null and b/ressources/images/installation/vbox-02-outil-extensions.png differ diff --git a/ressources/images/installation/vbox-03-extension-confirmation.png b/ressources/images/installation/vbox-03-extension-confirmation.png new file mode 100644 index 0000000..c9961ab Binary files /dev/null and b/ressources/images/installation/vbox-03-extension-confirmation.png differ diff --git a/ressources/images/installation/vbox-04-extension-licence-debut.png b/ressources/images/installation/vbox-04-extension-licence-debut.png new file mode 100644 index 0000000..678bf14 Binary files /dev/null and b/ressources/images/installation/vbox-04-extension-licence-debut.png differ diff --git a/ressources/images/installation/vbox-05-extension-licence-fin.png b/ressources/images/installation/vbox-05-extension-licence-fin.png new file mode 100644 index 0000000..21994ba Binary files /dev/null and b/ressources/images/installation/vbox-05-extension-licence-fin.png differ diff --git a/ressources/images/installation/vbox-06-extension-installee.png b/ressources/images/installation/vbox-06-extension-installee.png new file mode 100644 index 0000000..efbf708 Binary files /dev/null and b/ressources/images/installation/vbox-06-extension-installee.png differ diff --git a/ressources/images/installation/vbox-07-nouvelle-machine-iso-detectee.png b/ressources/images/installation/vbox-07-nouvelle-machine-iso-detectee.png new file mode 100644 index 0000000..9a42e1d Binary files /dev/null and b/ressources/images/installation/vbox-07-nouvelle-machine-iso-detectee.png differ diff --git a/ressources/images/installation/vbox-08-nouvelle-machine-sans-surveillance-decochee.png b/ressources/images/installation/vbox-08-nouvelle-machine-sans-surveillance-decochee.png new file mode 100644 index 0000000..c61b10e Binary files /dev/null and b/ressources/images/installation/vbox-08-nouvelle-machine-sans-surveillance-decochee.png differ diff --git a/ressources/images/installation/vbox-09-nouvelle-machine-materiel.png b/ressources/images/installation/vbox-09-nouvelle-machine-materiel.png new file mode 100644 index 0000000..9caba37 Binary files /dev/null and b/ressources/images/installation/vbox-09-nouvelle-machine-materiel.png differ diff --git a/ressources/images/installation/vbox-10-nouvelle-machine-recapitulatif.png b/ressources/images/installation/vbox-10-nouvelle-machine-recapitulatif.png new file mode 100644 index 0000000..1a47e48 Binary files /dev/null and b/ressources/images/installation/vbox-10-nouvelle-machine-recapitulatif.png differ diff --git a/ressources/images/installation/vbox-11-machine-creee.png b/ressources/images/installation/vbox-11-machine-creee.png new file mode 100644 index 0000000..49aedf6 Binary files /dev/null and b/ressources/images/installation/vbox-11-machine-creee.png differ diff --git a/ressources/images/installation/vbox-12-configuration-reseau-pont.png b/ressources/images/installation/vbox-12-configuration-reseau-pont.png new file mode 100644 index 0000000..72db2c8 Binary files /dev/null and b/ressources/images/installation/vbox-12-configuration-reseau-pont.png differ diff --git a/ressources/images/installation/vbox-13-configuration-processeur.png b/ressources/images/installation/vbox-13-configuration-processeur.png new file mode 100644 index 0000000..268fed2 Binary files /dev/null and b/ressources/images/installation/vbox-13-configuration-processeur.png differ diff --git a/ressources/images/installation/vbox-14-machine-configuree.png b/ressources/images/installation/vbox-14-machine-configuree.png new file mode 100644 index 0000000..1eeb2db Binary files /dev/null and b/ressources/images/installation/vbox-14-machine-configuree.png differ diff --git a/ressources/images/installation/vbox-captures.txt b/ressources/images/installation/vbox-captures.txt new file mode 100644 index 0000000..86b58d5 --- /dev/null +++ b/ressources/images/installation/vbox-captures.txt @@ -0,0 +1,26 @@ +# Captures VirtualBox 7.2.20 (Linux, interface en francais, Basic Mode) +# Produites par outils/captures/virtualbox/capturer.sh (Xvfb + openbox). +# Chemins : l'assistant refuse un chemin Windows pour l'image ISO (fichier +# introuvable sous Linux) et transforme un dossier D:\... en chemin relatif ; +# les captures montrent donc l'equivalent Linux : +# /mnt/D/PrenomNOM/sources = D:\PrenomNOM\sources +# /mnt/D/PrenomNOM/VirtualBox = D:\PrenomNOM\VirtualBox +# Apparence : fenetres Linux (theme Qt Fusion, cadres openbox) ; libelles +# identiques a la version Windows. Plusieurs libelles de la 7.2.20 ne sont +# pas traduits en francais (assistant, accueil) : c'est le cas aussi sous +# Windows. Carte reseau de l'hote : eth0 (sous Windows, nom de la carte +# Ethernet du poste). +vbox-01-premier-lancement.png -> Gestionnaire au premier lancement : choix Basic Mode / Expert Mode (textes d'accueil non traduits dans la 7.2.20) +vbox-02-outil-extensions.png -> Outil Extensions (Fichier > Outils > Extensions, Ctrl+T) : liste vide, bouton Install +vbox-03-extension-confirmation.png -> Confirmation : Oracle VirtualBox Extension Pack, version 7.2.20r175154, bouton Installation +vbox-04-extension-licence-debut.png -> Licence PUEL de l'Extension Pack : boutons J'accepte / Je n'accepte pas grises tant que le texte n'est pas lu +vbox-05-extension-licence-fin.png -> Licence lue jusqu'au bout : bouton J'accepte actif +vbox-06-extension-installee.png -> Extension installee : Oracle VirtualBox Extension Pack, active, version 7.2.20r175154 +vbox-07-nouvelle-machine-iso-detectee.png -> Assistant, page 1 : nom FormationLinux, dossier et ISO saisis, Debian 13 Trixie (64-bit) detecte, installation sans surveillance cochee par defaut +vbox-08-nouvelle-machine-sans-surveillance-decochee.png -> Assistant, page 1 : case Proceed with Unattended Installation decochee (installation manuelle) +vbox-09-nouvelle-machine-materiel.png -> Assistant, page 2 : 4096 Mo de memoire, 2 CPU, disque de 30 Gio (VDI dynamique par defaut en Basic Mode), EFI non coche +vbox-10-nouvelle-machine-recapitulatif.png -> Assistant, Recapitulatif : FormationLinux, dossier, ISO, Debian 13 Trixie (64-bit), sans surveillance false, 4096, 2 processeurs, 30,00 Gio +vbox-11-machine-creee.png -> Gestionnaire : machine FormationLinux creee (Eteinte), details 4096 Mo, 2 processeurs, ISO et FormationLinux.vdi 30,00 Gio, reseau NAT par defaut +vbox-12-configuration-reseau-pont.png -> Configuration > Reseau : Adapter 1 active, Attached to = Acces par pont, Name = eth0 (carte reseau de l'hote) +vbox-13-configuration-processeur.png -> Configuration > System > Processeur : Number of CPUs = 2 +vbox-14-machine-configuree.png -> Gestionnaire : details de FormationLinux, Reseau : Interface 1 en Acces par pont diff --git a/ressources/scripts/installer_virtualbox.bat b/ressources/scripts/installer_virtualbox.bat new file mode 100644 index 0000000..984c99c --- /dev/null +++ b/ressources/scripts/installer_virtualbox.bat @@ -0,0 +1,373 @@ +@echo off +rem ====================================================================== +rem installer_virtualbox.bat -- Formation Linux (CID) +rem +rem Reinstalle VirtualBox sur un poste "fige" (C: remis a zero a chaque +rem redemarrage) et retrouve la VM conservee sur D:. +rem +rem Utilisation : +rem 1. Placer ce fichier dans D:\PrenomNOM\sources\ avec : +rem VC_redist.x64.exe +rem VirtualBox---Win.exe +rem Oracle_VirtualBox_Extension_Pack-.vbox-extpack +rem 2. Double-cliquer sur le fichier. Si Windows demande une +rem autorisation, repondre Oui. +rem +rem Le script : +rem - installe Visual C++ 2015-2022 x64, VirtualBox et l'Extension Pack +rem en silencieux ; +rem - regle le dossier des machines sur D:\PrenomNOM\VirtualBox ; +rem - reenregistre la VM FormationLinux si elle existe deja. +rem +rem Les messages sont volontairement sans accents : cmd.exe affiche les +rem fichiers .bat dans la page de code OEM (850), et "chcp 65001" est +rem source de bugs d'analyse des scripts sur certaines versions de +rem Windows. L'ASCII pur fonctionne partout. +rem +rem Precautions cmd.exe suivies dans ce fichier : aucune variable n'est +rem modifiee puis lue dans un meme bloc entre parentheses (pas besoin de +rem "delayed expansion"), les chemins sont toujours entre guillemets, et +rem les codes retour sont recopies dans RC juste apres chaque commande. +rem ====================================================================== + +setlocal EnableExtensions +title Installation de VirtualBox - Formation Linux + +rem ---------------------------------------------------------------------- +rem Parametres +rem ---------------------------------------------------------------------- + +rem Hash de la licence de l'Extension Pack (SHA256 du fichier de licence, +rem releve pour la version 7.2.20). Si Oracle change la licence, ce hash +rem change et l'installation silencieuse du pack echoue : le script +rem affiche alors la commande a lancer a la main. +set "LICENCE_EXTPACK=eb31505e56e9b4d0fbca139104da41ac6f6b98f8e78968bdf01b1f3da3c4f9ae" + +rem Nom de la VM et emplacement de VBoxManage (dossier d'installation +rem par defaut de VirtualBox). +set "NOM_VM=FormationLinux" +set "VBOXMANAGE=%ProgramFiles%\Oracle\VirtualBox\VBoxManage.exe" + +rem Dossier du script (D:\PrenomNOM\sources\, avec la barre finale) +rem et dossier parent (D:\PrenomNOM). +set "SOURCES=%~dp0" +for %%I in ("%~dp0..") do set "BASE=%%~fI" +set "DOSSIER_VMS=%BASE%\VirtualBox" +set "FICHIER_VM=%DOSSIER_VMS%\%NOM_VM%\%NOM_VM%.vbox" +set "JOURNAL=%SOURCES%installation_virtualbox.log" + +rem Etat de chaque etape, affiche dans le resume final. +set "ETAT_VCREDIST=non fait" +set "ETAT_VIRTUALBOX=non fait" +set "ETAT_EXTPACK=non fait" +set "ETAT_DOSSIER=non fait" +set "ETAT_VM=non fait" +set "AVERTISSEMENTS=0" + +echo ====================================================================== +echo Installation de VirtualBox - Formation Linux +echo ====================================================================== +echo. +echo Dossier des installeurs : %SOURCES% +echo Dossier des machines : %DOSSIER_VMS% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 1 : recherche des installeurs par motif +rem (le script ne depend pas des numeros de version) +rem ---------------------------------------------------------------------- + +rem Le script n'a pas besoin d'etre lance en administrateur : chaque +rem installeur demande lui-meme l'autorisation. Lance en administrateur +rem (souvent un autre compte), il reglerait le profil de ce compte et non +rem celui du stagiaire : simple avertissement, non bloquant. +net session >nul 2>&1 +if not errorlevel 1 echo [ATTENTION] Inutile de lancer en administrateur : un double-clic suffit. +if not errorlevel 1 echo. + +echo Recherche des installeurs dans %SOURCES% +call :trouver FICHIER_VCREDIST "VC_redist.x64*.exe" +call :trouver FICHIER_VIRTUALBOX "VirtualBox-*-Win.exe" +call :trouver FICHIER_EXTPACK "Oracle_VirtualBox_Extension_Pack-*.vbox-extpack" + +if not defined FICHIER_VCREDIST goto erreur_fichiers +if not defined FICHIER_VIRTUALBOX goto erreur_fichiers +if not defined FICHIER_EXTPACK goto erreur_fichiers +echo. + +rem ---------------------------------------------------------------------- +rem Etape 2 : Microsoft Visual C++ Redistributable (requis par VirtualBox) +rem Codes retour acceptes : 0 = installe, 1638 = version plus recente deja +rem presente, 3010 = installe, redemarrage demande (on ne redemarre pas : +rem le poste fige perdrait tout). +rem +rem Les installeurs sont lances avec start /wait (et non directement) : +rem start passe par l'explorateur de Windows, qui affiche lui-meme la +rem demande d'autorisation si l'installeur en a besoin. Lance directement +rem depuis cmd, il echouerait avec le code 740 (elevation requise). +rem start /wait attend la fin de l'installeur et recupere son code retour. +rem Codes 740 et 1223 : autorisation impossible ou refusee ; 1602 : +rem installation annulee (VC++). Code 1618 : une autre installation +rem Windows est deja en cours. +rem ---------------------------------------------------------------------- + +echo [1/5] Installation de Visual C++ Redistributable... +start "" /wait "%FICHIER_VCREDIST%" /install /quiet /norestart +set "RC=%ERRORLEVEL%" +if "%RC%"=="740" goto vcredist_autorisation +if "%RC%"=="1223" goto vcredist_autorisation +if "%RC%"=="1602" goto vcredist_autorisation +if "%RC%"=="1618" goto vcredist_occupe +if "%RC%"=="0" set "ETAT_VCREDIST=OK" +if "%RC%"=="1638" set "ETAT_VCREDIST=OK (deja present)" +if "%RC%"=="3010" set "ETAT_VCREDIST=OK" +if "%ETAT_VCREDIST%"=="non fait" goto erreur_vcredist +echo %ETAT_VCREDIST% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 3 : VirtualBox +rem --silent : installation sans fenetre ni question ; +rem --ignore-reboot : renvoie 0 meme si Windows demande un redemarrage ; +rem --msi-log-file : journal detaille, garde sur D: en cas de probleme. +rem Si VBoxManage existe deja (script relance dans la meme session), +rem l'installation est sautee. +rem ---------------------------------------------------------------------- + +echo [2/5] Installation de VirtualBox (1 a 3 minutes, patienter)... +if exist "%VBOXMANAGE%" goto virtualbox_deja_la +start "" /wait "%FICHIER_VIRTUALBOX%" --silent --ignore-reboot --msi-log-file "%JOURNAL%" +set "RC=%ERRORLEVEL%" +if "%RC%"=="740" goto virtualbox_autorisation +if "%RC%"=="1223" goto virtualbox_autorisation +if "%RC%"=="1618" goto virtualbox_occupe +if not "%RC%"=="0" goto erreur_virtualbox +if not exist "%VBOXMANAGE%" goto erreur_vboxmanage +set "ETAT_VIRTUALBOX=OK" +goto virtualbox_fin +:virtualbox_deja_la +set "ETAT_VIRTUALBOX=OK (deja installe)" +:virtualbox_fin +echo %ETAT_VIRTUALBOX% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 4 : Extension Pack +rem --replace : remplace une version deja installee ; +rem --accept-license= : accepte la licence sans question (hash ci-dessus). +rem Un echec n'est pas bloquant : la VM fonctionne sans le pack. +rem Les commandes VBoxManage sont lancees normalement, avec le compte du +rem stagiaire (le reglage du dossier et la VM sont propres a ce compte) ; +rem VBoxManage demande lui-meme l'autorisation si l'installation du pack +rem en a besoin. +rem ---------------------------------------------------------------------- + +echo [3/5] Installation de l'Extension Pack... +"%VBOXMANAGE%" extpack install --replace --accept-license=%LICENCE_EXTPACK% "%FICHIER_EXTPACK%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto avertissement_extpack +set "ETAT_EXTPACK=OK" +goto extpack_fin +:avertissement_extpack +set "ETAT_EXTPACK=ECHEC (non bloquant, voir ci-dessus)" +set /a AVERTISSEMENTS+=1 +echo. +echo [ATTENTION] L'Extension Pack ne s'est pas installe (code %RC%). +echo Cause probable : Oracle a modifie la licence, le hash du script +echo n'est plus le bon. Pour l'installer a la main, double-cliquer sur +echo le fichier .vbox-extpack, ou taper dans une invite de commandes : +echo. +echo "%VBOXMANAGE%" extpack install --replace "%FICHIER_EXTPACK%" +echo. +echo puis accepter la licence en tapant y. Si Windows demande une +echo autorisation, repondre Oui. +:extpack_fin +echo %ETAT_EXTPACK% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 5 : dossier des machines virtuelles +rem Le reglage est stocke dans le profil Windows (sur C:, efface a chaque +rem redemarrage) : on le refait a chaque fois. +rem ---------------------------------------------------------------------- + +echo [4/5] Reglage du dossier des machines : %DOSSIER_VMS% +if not exist "%DOSSIER_VMS%\" mkdir "%DOSSIER_VMS%" +"%VBOXMANAGE%" setproperty machinefolder "%DOSSIER_VMS%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto erreur_dossier +set "ETAT_DOSSIER=OK" +echo %ETAT_DOSSIER% +echo. + +rem ---------------------------------------------------------------------- +rem Etape 6 : reenregistrement de la VM +rem - VM absente (premiere seance) : rien a faire, on la creera ; +rem - VM deja connue de VirtualBox : rien a faire ; +rem - sinon : VBoxManage registervm. +rem ---------------------------------------------------------------------- + +echo [5/5] Recherche de la VM %NOM_VM%... +if not exist "%FICHIER_VM%" goto vm_absente +"%VBOXMANAGE%" showvminfo "%NOM_VM%" >nul 2>&1 +if not errorlevel 1 goto vm_deja_enregistree +"%VBOXMANAGE%" registervm "%FICHIER_VM%" +set "RC=%ERRORLEVEL%" +if not "%RC%"=="0" goto avertissement_vm +set "ETAT_VM=OK (VM reenregistree)" +goto vm_fin +:vm_absente +set "ETAT_VM=pas encore de VM (normal a la premiere seance)" +goto vm_fin +:vm_deja_enregistree +set "ETAT_VM=OK (VM deja enregistree)" +goto vm_fin +:avertissement_vm +set "ETAT_VM=ECHEC (non bloquant, voir ci-dessus)" +set /a AVERTISSEMENTS+=1 +echo. +echo [ATTENTION] La VM n'a pas pu etre enregistree (code %RC%). +echo Dans VirtualBox : menu Machine, Ajouter..., puis choisir le fichier +echo %FICHIER_VM% +:vm_fin +echo %ETAT_VM% +echo. + +rem ---------------------------------------------------------------------- +rem Resume +rem ---------------------------------------------------------------------- + +call :resume +if not "%AVERTISSEMENTS%"=="0" echo Termine, mais avec des avertissements : lire les messages ci-dessus. +if "%AVERTISSEMENTS%"=="0" echo Termine. VirtualBox est pret : lancer "Oracle VirtualBox" depuis le menu Demarrer. +echo. +pause +endlocal +exit /b 0 + +rem ====================================================================== +rem Erreurs bloquantes : message, resume, pause, code retour 1 +rem ====================================================================== + +:erreur_fichiers +echo. +echo [ERREUR] Il manque au moins un fichier dans %SOURCES% +echo Fichiers attendus (le numero de version peut varier) : +echo VC_redist.x64.exe +echo VirtualBox-7.2.20-175154-Win.exe +echo Oracle_VirtualBox_Extension_Pack-7.2.20.vbox-extpack +echo Les telecharger (liens dans l'annexe d'installation), les placer +echo dans ce dossier, puis relancer le script. +goto fin_erreur + +:erreur_vcredist +echo. +echo [ERREUR] L'installation de Visual C++ a echoue (code %RC%). +echo Essayer de lancer %FICHIER_VCREDIST% par un double-clic. +set "ETAT_VCREDIST=ECHEC (code %RC%)" +goto fin_erreur + +:vcredist_autorisation +set "ETAT_VCREDIST=ECHEC (autorisation refusee, code %RC%)" +goto erreur_autorisation + +:virtualbox_autorisation +set "ETAT_VIRTUALBOX=ECHEC (autorisation refusee, code %RC%)" +goto erreur_autorisation + +:vcredist_occupe +set "ETAT_VCREDIST=ECHEC (autre installation en cours, code %RC%)" +goto erreur_occupe + +:virtualbox_occupe +set "ETAT_VIRTUALBOX=ECHEC (autre installation en cours, code %RC%)" +goto erreur_occupe + +:erreur_occupe +echo. +echo [ERREUR] Une autre installation est en cours sur ce poste (code %RC%). +echo Patienter quelques minutes (par exemple la fin des mises a jour de +echo Windows), puis relancer le script. +goto fin_erreur + +:erreur_autorisation +echo. +echo [ERREUR] Windows n'a pas donne l'autorisation d'installer (code %RC%). +echo Relancer le script et, quand Windows demande une autorisation, +echo repondre Oui. +goto fin_erreur + +:erreur_virtualbox +echo. +echo [ERREUR] L'installation de VirtualBox a echoue (code %RC%). +echo Journal detaille : %JOURNAL% +echo Essayer d'installer VirtualBox par un double-clic sur +echo %FICHIER_VIRTUALBOX% +set "ETAT_VIRTUALBOX=ECHEC (code %RC%)" +goto fin_erreur + +:erreur_vboxmanage +echo. +echo [ERREUR] VirtualBox semble installe, mais ce fichier est introuvable : +echo %VBOXMANAGE% +echo VirtualBox a peut-etre ete installe dans un autre dossier. +set "ETAT_VIRTUALBOX=ECHEC (VBoxManage introuvable)" +goto fin_erreur + +:erreur_dossier +echo. +echo [ERREUR] Impossible de regler le dossier des machines (code %RC%). +echo Dans VirtualBox : menu Fichier, Parametres, General, puis choisir +echo %DOSSIER_VMS% +set "ETAT_DOSSIER=ECHEC (code %RC%)" +goto fin_erreur + +:fin_erreur +echo. +call :resume +echo Installation interrompue. Prevenir le formateur si le probleme persiste. +echo. +pause +endlocal +exit /b 1 + +rem ====================================================================== +rem Sous-programmes +rem ====================================================================== + +rem ---------------------------------------------------------------------- +rem :trouver VARIABLE "motif" +rem Cherche le motif dans le dossier du script et met le chemin complet +rem dans VARIABLE (vide si aucun fichier). Une boucle "for" sur un motif +rem sans correspondance ne fait aucun tour : la variable reste vide. +rem Si plusieurs fichiers correspondent, le dernier trouve est garde et +rem un avertissement est affiche. +rem ---------------------------------------------------------------------- +:trouver +set "%~1=" +set "NB_TROUVES=0" +for %%F in ("%SOURCES%%~2") do ( + set "%~1=%%~fF" + set /a NB_TROUVES+=1 +) +if "%NB_TROUVES%"=="0" echo [MANQUANT] %~2 +if "%NB_TROUVES%"=="0" exit /b 1 +call echo [OK] %%%~1%% +if not "%NB_TROUVES%"=="1" echo [ATTENTION] %NB_TROUVES% fichiers correspondent a %~2 : garder une seule version. +exit /b 0 + +rem ---------------------------------------------------------------------- +rem :resume -- affiche l'etat de chaque etape +rem ---------------------------------------------------------------------- +:resume +echo ====================================================================== +echo Resume +echo ====================================================================== +echo Visual C++ Redistributable : %ETAT_VCREDIST% +echo VirtualBox : %ETAT_VIRTUALBOX% +echo Extension Pack : %ETAT_EXTPACK% +echo Dossier des machines : %ETAT_DOSSIER% +echo Machine virtuelle : %ETAT_VM% +echo ====================================================================== +echo. +exit /b 0