4 Commits

Author SHA1 Message Date
nocode 20e0643a02 feat(anaconda): add automatic hardware detection and driver installation
Add hardware detection and automatic driver installation during system installation. This includes:

- Virtualization platform detection with automatic guest tools configuration
- Automatic GPU driver installation (NVIDIA, AMD, Intel)
- Removal of incompatible guest tools for other hypervisors
- Network-resilient installation (non-blocking if network unavailable)

The detection uses the fws-hw-detect script which provides the same hardware information shown to the user in the live environment summary screen. This ensures what was announced to the user is what gets applied during installation.

Update section numbering in kickstart file to accommodate the new hardware configuration step.
2026-07-03 03:24:28 +02:00
nocode 071cc307b4 chore(baseline): add file permission for fws-hw-detect binary 2026-07-03 03:24:20 +02:00
nocode 1917a3321b feat(xinitrc): add hardware detection summary before installer launch
Add fws-hw-detect integration to display hardware information in a dialog before launching the installer. This allows users to review detected hardware (GPU drivers, hypervisor guest tools, disk info) before proceeding.

The summary is shown via zenity if available, with options to launch the installer or power off. Also includes the hardware summary in the installation logs for debugging purposes.
2026-07-03 03:24:14 +02:00
nocode 19c16006fb feat(baseline): add fws-hw-detect script for hardware detection and configuration
Add new fws-hw-detect script that detects hypervisor type and GPU hardware, outputting either machine-readable environment variables for kickstart processing or human-readable French text summary for live environment.

The script serves as the single source of truth for hardware detection, consumed by:
- Live environment display via zenity before installation starts
- Anaconda %post chroot to apply appropriate guest tools and GPU drivers

Supported hypervisors: VMware, VirtualBox, QEMU/KVM, and physical machines
Supported GPUs: NVIDIA, AMD, Intel, and VMware/VirtualBox virtual graphics

No hard dependencies; gracefully degrades when lspci and ip are unavailable.
2026-07-03 03:21:31 +02:00
12 changed files with 264 additions and 190 deletions
+1 -1
View File
@@ -1 +1 @@
0.5.4 0.5.3
+17 -42
View File
@@ -138,12 +138,7 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..." echo "==> Relance dans le conteneur ($SELF)..."
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur "${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \
@@ -193,49 +188,29 @@ WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "==> Dossier de travail : $WORK_DIR" echo "==> Dossier de travail : $WORK_DIR"
# ============================================================ # ============================================================
# Version (major.minor.build) + nom de la variante. # Lecture et validation de la version (fichier VERSION)
# La version est DEMANDÉE ; le fichier VERSION sert de DÉFAUT (taper Entrée =
# reprendre sa valeur). Produit fws-<major.minor.build>-<nom>.iso
# (ex. fws-1.5.0-cli.iso). Nécessite un terminal (read).
# ============================================================ # ============================================================
# Défaut éventuel lu dans le fichier VERSION (doit être major.minor.build).
VERSION_FILE="$WORK_DIR/VERSION" VERSION_FILE="$WORK_DIR/VERSION"
VERSION_DEFAULT="" [ -f "$VERSION_FILE" ] \
if [ -f "$VERSION_FILE" ]; then || { echo -e "\e[31mFichier VERSION introuvable : $VERSION_FILE\e[0m"; exit 1; }
VERSION_DEFAULT="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
[[ "$VERSION_DEFAULT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || VERSION_DEFAULT=""
fi
while :; do # Trim espaces / tabulations / CR / LF
if [ -n "$VERSION_DEFAULT" ]; then VERSION="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
read -rp "==> Version (major.minor.build) [Entrée = $VERSION_DEFAULT] : " VERSION \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; } if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
[ -n "$VERSION" ] || VERSION="$VERSION_DEFAULT" # vide → on reprend le fichier VERSION echo -e "\e[31mVERSION invalide : '$VERSION' (attendu Major.Minor.Build, ex. 1.2.3)\e[0m"
else exit 1
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " VERSION \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
fi fi
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && break
echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
done
while :; do
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " ISO_TAG \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
# normalise : minuscules, lettres / chiffres / tirets uniquement
ISO_TAG="$(printf '%s' "$ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
[ -n "$ISO_TAG" ] && break
echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
done
IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION" IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION"
# iso_version interne (passé à mkarchiso) ; l'ISO finale est renommée plus bas. BUILD_DATE="$(date +%Y.%m.%d)"
ISO_VERSION="${VERSION}-${ISO_TAG}" ISO_VERSION="v${MAJOR}-${MINOR}-${BUILD}-${BUILD_DATE}"
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}" OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
ISO_FILE="$OUT_DIR/fws-${VERSION}-${ISO_TAG}.iso" ISO_FILE="$OUT_DIR/FWS-${ISO_VERSION}-x86_64.iso"
echo "==> Version : ${VERSION} · variante : ${ISO_TAG}" echo "==> Version officielle : ${MAJOR}.${MINOR}.${BUILD}"
echo "==> ISO finale : fws-${VERSION}-${ISO_TAG}.iso" echo "==> Dossier de sortie : $OUT_DIR"
echo "==> Dossier : $OUT_DIR" echo "==> ISO finale : FWS-${ISO_VERSION}-x86_64.iso"
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel # pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent, # WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
@@ -344,4 +319,4 @@ echo "==> Nettoyage..."
rm -rf /tmp/fws-build/work rm -rf /tmp/fws-build/work
echo "==> Build officiel terminé ! ISO : $FINAL" echo "==> Build officiel terminé ! ISO : $FINAL"
echo -e "\e[32mScript de build officiel (fws-${VERSION}-${ISO_TAG}) terminé avec succès.\e[0m" echo -e "\e[32mScript de build officiel (v${MAJOR}.${MINOR}.${BUILD}) terminé avec succès.\e[0m"
+1 -6
View File
@@ -133,12 +133,7 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..." echo "==> Relance dans le conteneur ($SELF)..."
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur "${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \
+23
View File
@@ -20,6 +20,26 @@ _OB_PID=$!
# boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main # boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main
# à startx : on reste sur Openbox pour lire les logs / rebooter proprement. # à startx : on reste sur Openbox pour lire les logs / rebooter proprement.
if command -v liveinst >/dev/null 2>&1; then if command -v liveinst >/dev/null 2>&1; then
# Chantier D : RÉCAPITULATIF matériel AVANT de lancer l'installateur.
# fws-hw-detect est la MÊME source de vérité que le %post du kickstart
# (mode --env) → ce que cet écran annonce = ce que l'install appliquera
# (drivers GPU, guest tools de l'hyperviseur, effacement des disques…).
# « Éteindre » est le seul moyen propre d'annuler : on ne rend jamais la
# main à startx (cf. plus bas), et liveinst n'a pas encore touché au disque.
# Tout est best-effort : sans script ou sans zenity, on installe comme avant.
if command -v fws-hw-detect >/dev/null 2>&1; then
fws-hw-detect --text >/tmp/fws-hw-summary.txt 2>/dev/null || true
if command -v zenity >/dev/null 2>&1 && [ -s /tmp/fws-hw-summary.txt ]; then
if ! zenity --text-info --title="FWS — Récapitulatif avant installation" \
--filename=/tmp/fws-hw-summary.txt \
--ok-label="Lancer l'installateur" --cancel-label="Éteindre" \
--width=800 --height=700 2>/dev/null; then
systemctl poweroff
exit 0
fi
fi
fi
LIVEINST_LOG=/tmp/fws-liveinst.log LIVEINST_LOG=/tmp/fws-liveinst.log
liveinst >"$LIVEINST_LOG" 2>&1 liveinst >"$LIVEINST_LOG" 2>&1
rc=$? rc=$?
@@ -66,6 +86,9 @@ if command -v liveinst >/dev/null 2>&1; then
echo echo
echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########" echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########"
cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)" cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)"
echo
echo "########## Récapitulatif matériel montré avant l'install ##########"
cat /tmp/fws-hw-summary.txt 2>/dev/null || echo "(absent)"
for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \ for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \
/tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do /tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do
echo echo
@@ -0,0 +1,173 @@
#!/bin/bash
# ============================================================================
# fws-hw-detect — détection matériel FWS (hyperviseur + GPU).
#
# SOURCE UNIQUE de vérité, consommée par DEUX endroits :
# • l'écran « Récapitulatif avant installation » du live (--text, affiché
# par /root/.xinitrc via zenity juste avant de lancer liveinst) ;
# • le %post chrooté du kickstart Anaconda (--env), qui APPLIQUE :
# activation des guest tools du bon hyperviseur, purge des autres,
# installation des drivers GPU.
# → ce que l'écran ANNONCE = ce que l'install FERA (aucune divergence
# possible, la détection vit ici et nulle part ailleurs).
#
# Fonctionne dans le LIVE comme dans le CHROOT d'Anaconda : celui-ci
# bind-monte /sys et /proc du live → même matériel visible. Le script est
# livré par l'airootfs (/usr/local/bin) → recopié dans la cible par le
# payload liveimg → présent dans le chroot du %post. Aucune dépendance
# dure : lspci (pciutils) et ip (iproute2) ne servent qu'aux libellés
# humains de --text, avec repli si absents.
#
# Usage : fws-hw-detect --env lignes FWS_*='…' à eval par le kickstart
# fws-hw-detect --text récapitulatif humain en français (zenity)
# ============================================================================
set -u
MODE="${1:---text}"
# --- Hyperviseur --------------------------------------------------------------
# systemd-detect-virt lit CPUID/DMI → OK en chroot, sans systemd actif.
# ATTENTION : hors VM il AFFICHE « none » ET sort en code ≠ 0 → surtout pas de
# « || echo none » DANS le $() (on récolterait « none » en double).
VIRT="$(systemd-detect-virt 2>/dev/null | head -n1)"
[ -n "$VIRT" ] || VIRT=none
# Les guest tools des 3 hyperviseurs sont DÉJÀ dans l'image live
# (packages.x86_64 : open-vm-tools, virtualbox-guest-utils-nox,
# qemu-guest-agent) → activer un service marche HORS LIGNE ; on retire ceux
# des autres hyperviseurs (inutiles sur cette machine).
VIRT_SVC="" VIRT_RM="" VIRT_LABEL=""
case "$VIRT" in
vmware)
VIRT_LABEL="VMware"
VIRT_SVC="vmtoolsd.service vmware-vmblock-fuse.service"
VIRT_RM="virtualbox-guest-utils-nox qemu-guest-agent" ;;
oracle) # = VirtualBox (nom renvoyé par systemd-detect-virt)
VIRT_LABEL="VirtualBox"
VIRT_SVC="vboxservice.service"
VIRT_RM="open-vm-tools qemu-guest-agent" ;;
kvm|qemu)
VIRT_LABEL="QEMU/KVM"
VIRT_SVC="qemu-guest-agent.service"
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox" ;;
none)
VIRT_LABEL="aucune (machine physique)"
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox qemu-guest-agent" ;;
*) # Hyperviseur non géré (hyper-v…) : aucun de nos guest tools ne sert.
VIRT_LABEL="$VIRT (intégration non gérée)"
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox qemu-guest-agent" ;;
esac
# --- GPU ----------------------------------------------------------------------
# Classe PCI 0x03xxxx = contrôleur d'affichage ; lecture DIRECTE de /sys, pas
# besoin de lspci. Vendors : 0x10de NVIDIA, 0x1002 AMD, 0x8086 Intel,
# 0x15ad VMware (le VMSVGA de VirtualBox porte AUSSI 0x15ad → couvert).
# QEMU virtio-gpu/QXL et VBoxVGA : modesetting + mesa (déjà tirés par
# xorg-server dans l'image) suffisent → rien à installer.
DRV_PKGS="" GPU_TEXT=""
for _dev in /sys/bus/pci/devices/*; do
case "$(cat "$_dev/class" 2>/dev/null)" in 0x03*) ;; *) continue ;; esac
_vendor="$(cat "$_dev/vendor" 2>/dev/null)"
_pkgs=""
case "$_vendor" in
# NVIDIA : le module des dépôts est bâti pour le NOYAU des dépôts →
# « linux » DOIT partir dans la même transaction pacman (le noyau
# installé est celui du live, plus vieux), sinon module ≠ noyau au
# 1er boot. Les chemins /boot/* sont stables → grub.cfg reste bon.
0x10de) _pkgs="linux nvidia nvidia-utils" ;;
0x1002) _pkgs="mesa vulkan-radeon xf86-video-amdgpu" ;;
0x8086) _pkgs="mesa vulkan-intel" ;;
0x15ad) _pkgs="mesa xf86-video-vmware" ;;
esac
# Libellé humain : lspci si dispo, sinon adresse PCI + vendor brut.
_name=""
if command -v lspci >/dev/null 2>&1; then
_name="$(lspci -s "${_dev##*/}" 2>/dev/null | head -n1 \
| cut -d: -f3- | sed 's/^ //')"
fi
[ -n "$_name" ] || _name="GPU PCI ${_dev##*/} (vendor $_vendor)"
if [ -n "$_pkgs" ]; then
GPU_TEXT="${GPU_TEXT} • ${_name}\n → paquets : ${_pkgs}\n"
DRV_PKGS="$DRV_PKGS $_pkgs"
else
GPU_TEXT="${GPU_TEXT} • ${_name}\n → rien à installer (modesetting + mesa, déjà dans l'image)\n"
fi
done
# Dédoublonnage (machine multi-GPU : « mesa » listé 2× ferait échouer pacman
# sur « duplicate target »).
if [ -n "$DRV_PKGS" ]; then
DRV_PKGS="$(printf '%s\n' $DRV_PKGS | sort -u | tr '\n' ' ')"
DRV_PKGS="${DRV_PKGS% }"
fi
# --- Mode machine : sortie shell pour le kickstart ----------------------------
if [ "$MODE" = "--env" ]; then
printf "FWS_VIRT='%s'\n" "$VIRT"
printf "FWS_VIRT_SVC='%s'\n" "$VIRT_SVC"
printf "FWS_VIRT_RM='%s'\n" "$VIRT_RM"
printf "FWS_DRV_PKGS='%s'\n" "$DRV_PKGS"
exit 0
fi
# --- Mode humain : récapitulatif --text (zenity, avant liveinst) ---------------
CPU="$(awk -F': +' '/^model name/{print $2; exit}' /proc/cpuinfo)"
[ -n "$CPU" ] || CPU="inconnu"
case "$(awk -F': +' '/^vendor_id/{print $2; exit}' /proc/cpuinfo)" in
GenuineIntel) UCODE="intel-ucode" ;;
AuthenticAMD) UCODE="amd-ucode" ;;
*) UCODE="" ;;
esac
RAM="$(awk '/^MemTotal/{printf "%.1f Gio", $2/1048576}' /proc/meminfo)"
if [ -d /sys/firmware/efi ]; then
BOOT="UEFI — GRUB x86_64-efi + entrée de secours EFI/BOOT/BOOTX64.EFI"
else
BOOT="BIOS hérité — GRUB i386-pc, table de partitions MBR forcée"
fi
if command -v ip >/dev/null 2>&1 && ip route get 1.1.1.1 >/dev/null 2>&1; then
NET="disponible ✔"
else
NET="INDISPONIBLE ✖ — drivers GPU et bureau ne s'installeront PAS (rattrapable au 1er boot)"
fi
# Type « disk » uniquement (exclut lecteur ISO sr0/loop). La clé USB du live,
# elle, EST un disque → il est juste qu'elle apparaisse sous l'avertissement
# d'effacement (clearpart --all du kickstart efface bien TOUS les disques).
DISKS="$(lsblk -dno NAME,SIZE,MODEL,TYPE 2>/dev/null \
| awk '$NF=="disk"{NF--; print " • " $0}')"
[ -n "$DISKS" ] || DISKS=" (aucun disque détecté)"
[ -n "$GPU_TEXT" ] || GPU_TEXT=" (aucune carte graphique détectée)\n"
printf '%b' \
"══════════════════════════════════════════════════════════
FWS — RÉCAPITULATIF AVANT INSTALLATION
══════════════════════════════════════════════════════════
MACHINE
• Démarrage : ${BOOT}
• Virtualisation : ${VIRT_LABEL}
• Processeur : ${CPU}"
[ -n "$UCODE" ] && printf '%b' "
• Microcode : ${UCODE} — déjà intégré à l'image, rien à faire"
printf '%b' "
• Mémoire : ${RAM}
• Réseau : ${NET}
DISQUES DÉTECTÉS
⚠ Par défaut, TOUS les disques ci-dessous seront ENTIÈREMENT
EFFACÉS (partitionnement automatique). Le partitionnement
manuel reste possible dans l'installateur.
${DISKS}
CARTES GRAPHIQUES → drivers installés AUTOMATIQUEMENT en fin d'install
${GPU_TEXT}
INTÉGRATION HYPERVISEUR (automatique, fonctionne hors ligne)
• Services activés : ${VIRT_SVC:-aucun}
• Guest tools retirés (inutiles) : ${VIRT_RM:-aucun}
DÉROULEMENT DE L'INSTALLATION
1. Réglages dans Anaconda : langue, clavier, fuseau, disque, utilisateur
2. Copie du système live FWS sur le disque (payload liveimg)
3. Noyau + initramfs régénérés (mkinitcpio), GRUB installé
4. Drivers GPU + intégration hyperviseur appliqués (voir ci-dessus)
5. Bureau choisi installé (réseau requis)
« Lancer l'installateur » pour continuer — « Éteindre » pour annuler.
"
+1
View File
@@ -24,6 +24,7 @@ file_permissions=(
["/root/.automated_script.sh"]="0:0:755" ["/root/.automated_script.sh"]="0:0:755"
["/root/.gnupg"]="0:0:700" ["/root/.gnupg"]="0:0:700"
["/usr/local/bin/choose-mirror"]="0:0:755" ["/usr/local/bin/choose-mirror"]="0:0:755"
["/usr/local/bin/fws-hw-detect"]="0:0:755"
["/usr/local/bin/Installation_guide"]="0:0:755" ["/usr/local/bin/Installation_guide"]="0:0:755"
["/usr/local/bin/livecd-sound"]="0:0:755" ["/usr/local/bin/livecd-sound"]="0:0:755"
) )
+1 -1
View File
@@ -1 +1 @@
fws archiso
@@ -5,14 +5,5 @@
# On utilise donc un autre nom. # On utilise donc un autre nom.
_fastfetch_rc=$? _fastfetch_rc=$?
if [ "${_fastfetch_rc}" -eq 0 ]; then if [ "${_fastfetch_rc}" -eq 0 ]; then
# Logo FWS maison (4 carrés + Tux) au lieu du logo Arch détecté par défaut. fastfetch
fastfetch \
--logo-type file \
--logo /usr/local/share/fws/logo.txt \
--logo-color-1 '38;2;30;160;100' \
--logo-color-2 '38;2;45;125;210' \
--logo-color-3 '38;2;240;180;40' \
--logo-color-4 '38;2;25;85;145' \
--logo-color-5 '38;2;28;28;34' \
--logo-color-6 '38;2;245;165;35'
fi fi
@@ -1,9 +0,0 @@
$1████████ $2██$5████$2██
$1████████ $2█$5██$2██$5██$2█
$1████████ $2█$5██████$2█
$1████████ $2██$6█$2██$6█$2██
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
@@ -89,17 +89,6 @@ SYSROOT=/mnt/sysroot
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot. # DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
# --- Hostname : valeur saisie dans le champ « Nom de machine » du spoke User --
# Anaconda ne capte PAS le hostname du spoke Réseau (→ /etc/hostname vide). On a
# donc ajouté un champ Hostname au spoke Utilisateur (cf. patch du paquet
# anaconda) : user.py écrit la valeur dans /tmp/fws-hostname (live). On la
# nettoie (caractères de hostname valides) et on l'écrit dans la cible ; défaut
# « fws » si vide/absent → plus jamais de hostname vide ni « archiso ».
HN=""
[ -f /tmp/fws-hostname ] && HN="$(tr -cd 'a-zA-Z0-9._-' < /tmp/fws-hostname)"
[ -n "$HN" ] || HN="fws"
echo "$HN" > "$SYSROOT/etc/hostname"
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue # Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
# par le %post chrooté ci-dessous). « cli » par défaut si absent. # par le %post chrooté ci-dessous). « cli » par défaut si absent.
if [ -f /tmp/fws-desktop ]; then if [ -f /tmp/fws-desktop ]; then
@@ -205,7 +194,47 @@ else
echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main" echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main"
fi fi
# --- 4) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) ------- # --- 4) Drivers matériels : détection + activation/installation AUTO ----------
# Toute la DÉTECTION vit dans /usr/local/bin/fws-hw-detect : livré par
# l'airootfs → recopié dans la cible par le payload liveimg → présent dans ce
# chroot. C'est la MÊME source de vérité que l'écran « Récapitulatif avant
# installation » du live (.xinitrc, mode --text) : ce qui a été ANNONCÉ à
# l'utilisateur = ce qu'on APPLIQUE ici. La détection marche en chroot car
# Anaconda bind-monte le /sys et le /proc du LIVE (le code plus haut s'appuie
# déjà dessus : /sys/firmware/efi, findmnt) → on voit le VRAI matériel.
# Variables produites par --env :
# FWS_VIRT hyperviseur (vmware/oracle/kvm/qemu/none…)
# FWS_VIRT_SVC services invité à activer (paquets DÉJÀ dans l'image → OK
# hors ligne ; c'était le chaînon manquant, rien n'activait
# vmtoolsd & co sur le système installé)
# FWS_VIRT_RM guest tools des AUTRES hyperviseurs, à purger
# FWS_DRV_PKGS paquets GPU à installer, dédoublonnés. NVIDIA embarque
# « linux » dans la même transaction : le module des dépôts
# est bâti pour le noyau des dépôts, pas celui du live ; les
# chemins /boot/* étant stables, le grub.cfg de l'étape 3
# reste valable (le hook pacman de nvidia relance mkinitcpio).
# NB : microcode CPU (amd-ucode/intel-ucode) et linux-firmware sont DÉJÀ dans
# l'image → recopiés, et grub-mkconfig (étape 3) a généré les initrd qui vont
# bien. Rien à faire pour eux.
if [ -x /usr/local/bin/fws-hw-detect ]; then
eval "$(/usr/local/bin/fws-hw-detect --env)"
# (a) Hyperviseur : activer le bon service invité, purger les autres.
[ -n "$FWS_VIRT_SVC" ] && { systemctl enable $FWS_VIRT_SVC || true; }
[ -n "$FWS_VIRT_RM" ] && { pacman -Rns --noconfirm $FWS_VIRT_RM || true; }
# (b) GPU : install réseau, NON bloquante (même philosophie que le
# bureau). --needed : ne réinstalle pas ce que l'image a déjà (mesa…).
if [ -n "$FWS_DRV_PKGS" ]; then
pacman-key --init &>/dev/null || true
pacman-key --populate archlinux &>/dev/null || true
if ! pacman -Sy --noconfirm --needed $FWS_DRV_PKGS; then
echo "[FWS] Drivers non installés ($FWS_DRV_PKGS) — réseau ? À refaire au 1er boot."
fi
fi
else
echo "[FWS] fws-hw-detect introuvable — drivers/guest tools NON configurés."
fi
# --- 5) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) -------
DESK="cli" DESK="cli"
[ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)" [ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)"
DM="" DM=""
@@ -227,14 +256,14 @@ if [ -n "$PKGS" ]; then
fi fi
fi fi
# --- 5) Sécurité : verrouiller root s'il est resté SANS mot de passe ---------- # --- 6) Sécurité : verrouiller root s'il est resté SANS mot de passe ----------
# L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a # L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a
# pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert. # pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert.
if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then
passwd -l root || true passwd -l root || true
fi fi
# --- 6) Le resolv.conf du live ne doit pas rester figé ----------------------- # --- 7) Le resolv.conf du live ne doit pas rester figé -----------------------
# NetworkManager (activé sur le système installé) régénère /etc/resolv.conf. # NetworkManager (activé sur le système installé) régénère /etc/resolv.conf.
rm -f /tmp/fws-desktop /etc/resolv.conf 2>/dev/null || true rm -f /tmp/fws-desktop /etc/resolv.conf 2>/dev/null || true
%end %end
+1 -102
View File
@@ -25,7 +25,7 @@
# ============================================================================ # ============================================================================
pkgname=anaconda pkgname=anaconda
pkgver=45.8 pkgver=45.8
pkgrel=4 pkgrel=2
pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)" pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)"
arch=('x86_64') arch=('x86_64')
url="https://github.com/rhinstaller/anaconda" url="https://github.com/rhinstaller/anaconda"
@@ -57,107 +57,6 @@ source=("anaconda-$pkgver.tar.gz::https://github.com/rhinstaller/anaconda/archiv
sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed') sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed')
_srcdir="anaconda-anaconda-$pkgver" _srcdir="anaconda-anaconda-$pkgver"
prepare() {
cd "$srcdir/$_srcdir"
# ── FWS : clavier par défaut selon le TERRITOIRE de la locale ──────────────
# set_layouts() amont fait « locale = get_language_id(self.language) » →
# get_language_id JETTE le territoire (fr_CH.UTF-8 → 'fr') → list_keyboards('fr')
# = 'fr(oss)' (France) au lieu de 'ch(fr)' (Suisse). On passe la LOCALE COMPLÈTE
# (self.language) : langtable.list_keyboards('fr_CH.UTF-8') = 'ch(fr)'. La
# validation l'accepte (is_valid_langcode → parse_locale().language non vide).
# → le clavier du GUI ET du système installé suivent enfin la locale choisie.
sed -i \
's/^\(\s*\)locale = get_language_id(self\.language)/\1locale = self.language # FWS: garder le territoire (fr_CH -> ch(fr))/' \
pyanaconda/modules/localization/localization.py
# Garde-fou : casse le build si le motif amont a changé (le patch silencieux).
grep -q 'locale = self.language # FWS' \
pyanaconda/modules/localization/localization.py
# ── FWS : champ « Nom de machine » dans le spoke Utilisateur ───────────────
# Anaconda ne propage PAS le hostname (le champ du spoke Réseau est perdu chez
# nous → /etc/hostname vide). On ajoute donc un champ Hostname à l'onglet de
# création de compte (ligne 2 de la grille, libre) ; user.py écrit la valeur
# dans /tmp/fws-hostname, que le %post du kickstart applique à /etc/hostname.
python3 - <<'PYEOF'
import glob
# 1) user.glade : insérer label + entry "Host name" APRÈS le bloc username_entry.
glades = [p for p in glob.glob("pyanaconda/**/user.glade", recursive=True)
if 'username_entry' in open(p, encoding='utf-8').read()]
assert glades, "FWS: user.glade introuvable"
g = glades[0]
s = open(g, encoding='utf-8').read()
assert 'id="hostname_entry"' not in s, "FWS: glade déjà patché"
i = s.index('id="username_entry"')
k = s.index('</packing>', i) # fin du packing de username_entry
j = s.index('</child>', k) + len('</child>') # fin du <child> EXTERNE (pas l'accessible)
block = '''
<child>
<object class="GtkLabel" id="hostname_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xpad">10</property>
<property name="label" translatable="yes" context="GUI|User">_Host name</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">hostname_entry</property>
<property name="xalign">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="hostname_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="width-chars">50</property>
<child internal-child="accessible">
<object class="AtkObject" id="hostname_entry-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Host Name</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>'''
open(g, 'w', encoding='utf-8').write(s[:j] + block + s[j:])
# 2) user.py (GUI) : récupérer le widget + écrire /tmp/fws-hostname dans apply().
pys = glob.glob("pyanaconda/ui/gui/spokes/user.py")
assert pys, "FWS: user.py introuvable"
p = pys[0]
s = open(p, encoding='utf-8').read()
anchor = 'self._username_entry = self.builder.get_object("username_entry")'
assert anchor in s, "FWS: ancre username_entry absente"
s = s.replace(anchor,
anchor + '\n self._hostname_entry = self.builder.get_object("hostname_entry")',
1)
ap = 'set_user_list(self._users_module, self._user_list, remove_unset=True)'
assert ap in s, "FWS: ancre apply() absente"
s = s.replace(ap, ap + '''
# FWS : mémorise le nom de machine saisi (cible pas encore montée) pour
# le %post du kickstart, qui l'écrira dans /etc/hostname.
try:
_hn = self._hostname_entry.get_text().strip()
except Exception:
_hn = ""
try:
with open("/tmp/fws-hostname", "w") as _f:
_f.write(_hn)
except OSError:
pass''', 1)
open(p, 'w', encoding='utf-8').write(s)
print("FWS: champ Hostname ajouté au spoke Utilisateur (glade + user.py)")
PYEOF
}
build() { build() {
cd "$srcdir/$_srcdir" cd "$srcdir/$_srcdir"
# Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade. # Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade.
+1 -4
View File
@@ -114,10 +114,7 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
info "Relance dans le conteneur (setup-aur.sh)…" info "Relance dans le conteneur (setup-aur.sh)…"
# --network=host : le bridge podman ne route plus vers l'extérieur sur "${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
# certains réseaux (firewalld/NAT, → « Resolving timed out »). On partage la
# pile réseau de l'hôte (internet + DNS OK) pour les pacman/makepkg/AUR.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \