Files
FWS-ISO/build-offi.sh
T
nocode 0de970239f feat(build): refactor build script with interactive version and variant selection
Replace automatic version reading from VERSION file with interactive prompts for version (major.minor.build) and ISO variant name (e.g., cli, gnome, kde).

Changes:
- Remove automatic VERSION file reading; now serves as default with prompt
- Add interactive prompts for version and variant name with validation
- Simplify ISO naming from FWS-v{major}-{minor}-{build}-{date}-x86_64.iso to fws-{version}-{variant}.iso
- Update hostname from 'archiso' to 'fws'
- Add custom FWS logo for fastfetch with color configuration
- Improve error messages and user feedback

This enables flexible build variants and streamlines the build process.
2026-06-23 11:02:41 +02:00

348 lines
16 KiB
Bash
Executable File

#!/bin/bash
# ============================================================
# FWS — Build OFFICIEL versionné de l'ISO (archiso)
#
# Identique à build.sh dans sa structure, mais :
# - lit la version dans le fichier VERSION (format Major.Minor.Build)
# - nomme l'ISO FWS-vMAJOR-MINOR-BUILD-DATE-x86_64.iso
# - range l'ISO dans out/vMAJOR/vMAJOR.MINOR/
#
# Fonctionne sur :
# - Windows 10 / 11 (Git Bash → bascule auto dans WSL Arch)
# - Arch Linux natif (exécution directe)
# - WSL Arch lancé directement (exécution directe)
#
# Nom de la distro WSL : "Arch" par défaut, ou $ARCH_WSL_DISTRO,
# ou auto-détecté parmi les noms courants.
# ============================================================
set -e
trap 'rc=$?; echo -e "\e[31mÉchec (build-offi.sh) ligne $LINENO, code $rc.\e[0m" >&2; exit $rc' ERR
DISTRO_NAME="${ARCH_WSL_DISTRO:-Arch}"
# ============================================================
# Bascule Git Bash (Windows 10/11) → WSL si besoin
# ============================================================
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
echo -e "\e[36mGit Bash détecté — bascule dans WSL ($DISTRO_NAME)...\e[0m"
export MSYS_NO_PATHCONV=1
export MSYS2_ARG_CONV_EXCL="*"
# WSL doit être présent (sinon message clair plutôt que command not found).
command -v wsl.exe >/dev/null 2>&1 || {
echo -e "\e[31mWSL introuvable. Installe WSL2 puis une distro Arch :\e[0m" >&2
echo " 1) PowerShell admin : wsl --install" >&2
echo " 2) Installe Arch (ArchWSL, ou 'wsl --install -d archlinux') puis relance." >&2
exit 1
}
# Choix de la distro Arch : override $ARCH_WSL_DISTRO, sinon nom exact,
# sinon 1re distro « arch » réellement joignable. On énumère via
# `wsl -l -q` (sortie UTF-16LE → on retire NUL/CR) au lieu d'une liste
# figée, mais on garde la sonde « echo ok » (une distro listée peut
# être en cours d'install / cassée).
pick_arch_distro() {
wsl.exe -d "$DISTRO_NAME" -u root -- echo ok >/dev/null 2>&1 && return 0
local d distros=()
mapfile -t distros < <(wsl.exe -l -q 2>/dev/null | tr -d '\0\r' | sed 's/[[:space:]]*$//;/^$/d')
for d in "${distros[@]}"; do
[[ "$d" == "$DISTRO_NAME" ]] || continue
wsl.exe -d "$d" -u root -- echo ok >/dev/null 2>&1 && { DISTRO_NAME="$d"; return 0; }
done
for d in "${distros[@]}"; do
[[ "$d" =~ [Aa][Rr][Cc][Hh] ]] || continue
wsl.exe -d "$d" -u root -- echo ok >/dev/null 2>&1 && { DISTRO_NAME="$d"; return 0; }
done
return 1
}
if ! pick_arch_distro; then
echo -e "\e[31mAucune distribution WSL Arch joignable.\e[0m" >&2
echo "Distros WSL installées :" >&2
wsl.exe -l -v 2>/dev/null | tr -d '\0' >&2 || true
echo "" >&2
echo "Installe Arch dans WSL puis relance :" >&2
echo " - ArchWSL (yuk7) — s'enregistre sous 'Arch' : https://github.com/yuk7/ArchWSL/releases/latest" >&2
echo " - ou image officielle : wsl --install -d archlinux (nom 'archlinux')" >&2
echo "Distro sous un autre nom ? export ARCH_WSL_DISTRO=<nom> (cf. 'wsl -l -v' ci-dessus)." >&2
exit 1
fi
echo -e "\e[36m==> Distro WSL : $DISTRO_NAME\e[0m"
# Traduit le dossier du script en chemin WSL.
SCRIPT_DIR_WIN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -W)"
SCRIPT_PATH_WSL=$(MSYS_NO_PATHCONV=1 wsl.exe -d "$DISTRO_NAME" -u root -- \
wslpath "$(echo "$SCRIPT_DIR_WIN" | sed 's|/|\\\\|g')/build-offi.sh" \
2>/dev/null | tr -d '\r\n')
[ -n "$SCRIPT_PATH_WSL" ] || {
echo -e "\e[31mTraduction du chemin vers WSL échouée (wslpath). Vérifie ARCH_WSL_DISTRO ($DISTRO_NAME).\e[0m" >&2
exit 1
}
wsl.exe -d "$DISTRO_NAME" -u root -- test -f "$SCRIPT_PATH_WSL" || {
echo -e "\e[31mScript introuvable dans WSL : $SCRIPT_PATH_WSL\e[0m" >&2
exit 1
}
# Relance ce même script dans WSL, en root, depuis son vrai chemin
# (BASH_SOURCE → WORK_DIR correct). Les fins de ligne sont garanties LF
# par .gitattributes (*.sh text eol=lf) ; le profil copié est en plus
# passé à dos2unix plus loin.
echo "==> Script à exécuter dans WSL : $SCRIPT_PATH_WSL"
MSYS_NO_PATHCONV=1 wsl.exe -d "$DISTRO_NAME" -u root -- bash "$SCRIPT_PATH_WSL"
exit 0
fi
# ============================================================
# Bascule conteneur : hôte non-Arch (Fedora, Ubuntu, …) →
# build dans un conteneur Arch via podman.
#
# mkarchiso exige pacman + des mounts/chroot/loop privilégiés que
# seul un environnement Arch fournit. Sur un hôte non-Arch on relance
# CE script à l'identique dans docker.io/library/archlinux:latest, le
# dépôt monté au MÊME chemin absolu (→ WORK_DIR et out/ inchangés).
# podman ROOTFUL + --privileged obligatoire : rootless ne gère pas les
# mounts/chroot de mkarchiso (et --privileged relâche aussi SELinux,
# enforcing sur Fedora, sur le bind-mount sans avoir besoin de :Z).
# ============================================================
host_is_arch() {
# Fedora empaquette aussi « pacman » → sa seule présence ne suffit pas ;
# on exige en plus un ID/ID_LIKE « arch » dans /etc/os-release.
command -v pacman >/dev/null 2>&1 \
&& grep -qiE '^ID(_LIKE)?=.*arch' /etc/os-release 2>/dev/null
}
if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
echo -e "\e[36mHôte non-Arch détecté — build officiel dans un conteneur Arch (podman)...\e[0m"
command -v podman >/dev/null 2>&1 || {
echo -e "\e[31mpodman introuvable.\e[0m" >&2
echo " Fedora : sudo dnf install -y podman" >&2
echo " Debian/Ubuntu : sudo apt install -y podman" >&2
exit 1
}
# Rootful : préfixe sudo si on n'est pas déjà root.
SUDO=(); [ "$EUID" -eq 0 ] || SUDO=(sudo)
# -it seulement si on est dans un terminal (sinon podman casse en CI).
TTY=(); [ -t 0 ] && TTY=(-it)
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SELF="$(basename "${BASH_SOURCE[0]}")"
IMAGE="docker.io/library/archlinux:latest"
# pull explicite : « run » ne re-résout jamais le tag « latest » une fois
# l'image en cache → sans ça on build indéfiniment sur une image qui
# vieillit (puis partial upgrades pacman). Le -Syu côté conteneur (plus
# bas) complète la parade.
echo "==> Pull de l'image $IMAGE..."
"${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..."
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur
# 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 \
-v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \
-w "$REPO_DIR" \
"$IMAGE" \
bash "$REPO_DIR/$SELF"
# podman rootful → les fichiers produits (out/) appartiennent à root.
# On les rend au propriétaire du dépôt pour confort côté hôte.
OWNER="$(stat -c '%u:%g' "$REPO_DIR" 2>/dev/null || true)"
if [ -n "$OWNER" ]; then
for d in out local-repo; do
[ -e "$REPO_DIR/$d" ] && "${SUDO[@]}" chown -R "$OWNER" "$REPO_DIR/$d" 2>/dev/null || true
done
fi
exit 0
fi
# ============================================================
# Exécution native (Arch Linux bare-metal, WSL, OU conteneur Arch)
# ============================================================
command -v pacman >/dev/null \
|| { echo -e "\e[31mpacman introuvable — lance ce script depuis Arch / WSL.\e[0m"; exit 1; }
# mkarchiso exige root. Sur Arch natif on s'élève (sudo de préférence,
# sinon su) ; depuis la bascule Git Bash on est déjà root (-u root).
# $0 est cité pour gérer les chemins à espaces (ex. "1. FWS").
if [ "$EUID" -ne 0 ]; then
if command -v sudo >/dev/null 2>&1; then
exec sudo bash "$0" "$@"
else
exec su -c "bash \"$0\"" root
fi
fi
if grep -qi 'microsoft\|wsl' /proc/version 2>/dev/null; then
echo -e "\e[36mBuild officiel dans WSL ($DISTRO_NAME) en tant que root...\e[0m"
IS_WSL=1
else
echo -e "\e[36mBuild officiel sur Arch Linux natif en tant que root...\e[0m"
IS_WSL=0
fi
# Le dossier de travail = dossier qui contient ce script (gère les
# espaces du chemin, ex. "1. FWS").
WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "==> Dossier de travail : $WORK_DIR"
# ============================================================
# Version (major.minor.build) + nom de la variante.
# 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_DEFAULT=""
if [ -f "$VERSION_FILE" ]; then
VERSION_DEFAULT="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
[[ "$VERSION_DEFAULT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || VERSION_DEFAULT=""
fi
while :; do
if [ -n "$VERSION_DEFAULT" ]; then
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; }
[ -n "$VERSION" ] || VERSION="$VERSION_DEFAULT" # vide → on reprend le fichier VERSION
else
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
[[ "$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"
# iso_version interne (passé à mkarchiso) ; l'ISO finale est renommée plus bas.
ISO_VERSION="${VERSION}-${ISO_TAG}"
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
ISO_FILE="$OUT_DIR/fws-${VERSION}-${ISO_TAG}.iso"
echo "==> Version : ${VERSION} · variante : ${ISO_TAG}"
echo "==> ISO finale : fws-${VERSION}-${ISO_TAG}.iso"
echo "==> Dossier : $OUT_DIR"
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
# sans effet sur Arch natif.
if { [ "$IS_WSL" -eq 1 ] || [ -n "$FWS_IN_CONTAINER" ]; } \
&& ! grep -qE '^[[:space:]]*DisableSandbox' /etc/pacman.conf; then
sed -i '/^\[options\]/a DisableSandbox' /etc/pacman.conf
echo "==> WSL/conteneur détecté → DisableSandbox ajouté à /etc/pacman.conf"
fi
echo "==> Mise à jour des miroirs et installation de archiso..."
pacman -Sy --noconfirm reflector || true
reflector --verbose --latest 10 --sort rate --save /etc/pacman.d/mirrorlist || true
if [ -n "$FWS_IN_CONTAINER" ]; then
# Image de base qui vieillit → upgrade complet AVANT d'ajouter archiso,
# sinon partial upgrade (archiso tire des deps plus récentes que la base).
pacman -Syu --noconfirm archiso
else
pacman -Sy --noconfirm archiso
fi
echo "==> Préparation de l'environnement de build..."
rm -rf /tmp/fws-build
mkdir -p /tmp/fws-build/releng
# Copie configs/releng/ en base
if [ -d "$WORK_DIR/configs/releng" ]; then
cp -ar "$WORK_DIR/configs/releng/"* /tmp/fws-build/releng/
echo "==> configs/releng/ copié"
fi
# Fusionne configs/baseline/ par dessus (priorité aux fichiers baseline)
if [ -d "$WORK_DIR/configs/baseline" ]; then
cp -ar "$WORK_DIR/configs/baseline/"* /tmp/fws-build/releng/
echo "==> configs/baseline/ fusionné (prioritaire)"
fi
# ── Injection de la version dans profiledef.sh (copie temporaire) ──
# mkarchiso nomme l'ISO ${iso_name}-${iso_version}-${arch}.iso.
# On force iso_version pour obtenir vMAJOR-MINOR-BUILD-DATE.
if [ -f /tmp/fws-build/releng/profiledef.sh ]; then
sed -i "s|^iso_version=.*|iso_version=\"${ISO_VERSION}\"|" \
/tmp/fws-build/releng/profiledef.sh
echo "==> iso_version forcé à '${ISO_VERSION}' dans profiledef.sh"
else
echo -e "\e[31mprofiledef.sh introuvable dans le profil de build.\e[0m"
exit 1
fi
echo "==> Activation de NetworkManager.service sur le live (symlink réel)..."
mkdir -p /tmp/fws-build/releng/airootfs/etc/systemd/system/multi-user.target.wants
ln -sf /usr/lib/systemd/system/NetworkManager.service \
/tmp/fws-build/releng/airootfs/etc/systemd/system/multi-user.target.wants/NetworkManager.service
echo "==> Vérification des services systemd..."
ls /tmp/fws-build/releng/airootfs/etc/systemd/system/ 2>/dev/null || echo " (aucun service)"
ls /tmp/fws-build/releng/airootfs/etc/systemd/system/multi-user.target.wants/ 2>/dev/null || echo " (aucun symlink)"
# Copie le repo local (winboat-bin + deps Anaconda packagées)
if ls "$WORK_DIR/local-repo/"*.pkg.tar.zst &>/dev/null 2>&1; then
echo "==> Copie du repo local fws-local..."
mkdir -p /tmp/fws-build/local-repo
cp -a "$WORK_DIR/local-repo/"* /tmp/fws-build/local-repo/
echo "==> Injection [fws-local] dans pacman.conf..."
sed -i '/^\[fws-local\]/,/^$/d' /tmp/fws-build/releng/pacman.conf
PACMAN_TMP=$(mktemp)
printf '[fws-local]\nSigLevel = Optional TrustAll\nServer = file:///tmp/fws-build/local-repo\n\n' \
> "$PACMAN_TMP"
cat /tmp/fws-build/releng/pacman.conf >> "$PACMAN_TMP"
mv "$PACMAN_TMP" /tmp/fws-build/releng/pacman.conf
echo "==> Vérification pacman.conf :"
head -5 /tmp/fws-build/releng/pacman.conf
else
echo "==> Aucun repo local détecté."
fi
echo "==> Correction des retours à la ligne Windows (dos2unix)..."
pacman -S --needed --noconfirm dos2unix
find /tmp/fws-build/releng -type f -exec dos2unix {} + 2>/dev/null
echo "==> Lancement de mkarchiso..."
mkdir -p "$OUT_DIR"
# mkarchiso supprime l'ISO cible avant de la réécrire : si elle est VERROUILLÉE
# par Windows (VM ouverte dessus, ISO montée), ce rm casse tout le build à la
# fin. Parade : staging neuf puis déplacement ; si la cible (cette même
# version) est verrouillée, on bascule sur un nom horodaté. On ne touche
# jamais aux ISO des autres versions.
STAGE="$OUT_DIR/.staging-$$"
rm -rf "$STAGE"; mkdir -p "$STAGE"
mkarchiso -v -w /tmp/fws-build/work -o "$STAGE" /tmp/fws-build/releng
NEW_ISO="$(ls -1 "$STAGE/"*.iso 2>/dev/null | head -1)"
[ -n "$NEW_ISO" ] || { echo -e "\e[31mAucune ISO produite par mkarchiso.\e[0m" >&2; exit 1; }
if { chmod -f 666 "$ISO_FILE" 2>/dev/null; rm -f "$ISO_FILE" 2>/dev/null; } && mv -f "$NEW_ISO" "$ISO_FILE" 2>/dev/null; then
FINAL="$ISO_FILE"
else
FINAL="${ISO_FILE%.iso}-$(date +%H%M%S).iso"
echo -e "\e[33m[!] '$ISO_FILE' est verrouillée (une VM l'utilise ?). Écriture sous un nom unique.\e[0m" >&2
mv -f "$NEW_ISO" "$FINAL"
fi
rm -rf "$STAGE" 2>/dev/null || true
echo "==> Nettoyage..."
rm -rf /tmp/fws-build/work
echo "==> Build officiel terminé ! ISO : $FINAL"
echo -e "\e[32mScript de build officiel (fws-${VERSION}-${ISO_TAG}) terminé avec succès.\e[0m"