7730ac7b00
Add preflight package resolution step in build-offi.sh to validate all profile packages against pacman repositories before mkarchiso execution. This prevents 'target not found' errors during ISO creation by catching missing packages early with helpful error messages and recovery instructions. Introduce fws-dnd package (D&D 5e manager for FWS with GTK4 interface) to the local package build process in setup-aur.sh. Include PKGBUILD definition and source tarball. When package resolution fails, the build now exits with clear feedback pointing users to run setup-aur.sh to build missing dependencies like fws-dnd.
349 lines
17 KiB
Bash
Executable File
349 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================
|
||
# FWS — Build des paquets AUR (winboat-bin) + PKGBUILD locaux (deps Anaconda,
|
||
# fws-dnd) et création du repo pacman local fws-local/.
|
||
#
|
||
# Rejoue-le si tu changes la liste AUR_PACKAGES ou pour
|
||
# rafraîchir le repo local.
|
||
# ============================================================
|
||
set -e
|
||
trap 'rc=$?; echo -e "\033[0;31m[ERR] Échec (setup-aur.sh) ligne $LINENO, code $rc.\033[0m" >&2; exit $rc' ERR
|
||
|
||
# Paquets AUR à construire, DANS L'ORDRE (les deps avant les dépendants ; la
|
||
# boucle plus bas installe chaque paquet après build pour résoudre les chaînes).
|
||
# - libxmlrpc, satyr : deps de libreport (deps officielles uniquement)
|
||
# - libreport : tiré par python-meh ; chaîne AUR→AUR → APRÈS satyr+libxmlrpc
|
||
# - winboat-bin : app FWS
|
||
# - python-* : deps Anaconda présentes dans l'AUR (deps officielles only)
|
||
# python-blivet est packagé en LOCAL (pkgbuilds/, patché sans libselinux).
|
||
# gnome-kiosk (WM de la session GUI) est différé au chantier C.
|
||
# NB : python-iso639 RETIRÉ de l'AUR (mauvais projet homonyme, API sans find()) →
|
||
# packagé en LOCAL (pkgbuilds/python-iso639, projet PyPI « iso639 » 0.1.4).
|
||
# NB : python-langtable RETIRÉ de l'AUR (figé 0.0.51, sans list_common_languages) →
|
||
# packagé en LOCAL (pkgbuilds/python-langtable, 0.0.71).
|
||
AUR_PACKAGES=(libxmlrpc satyr libreport winboat-bin python-pid python-xkbregistry python-requests-ftp)
|
||
DISTRO_NAME="${ARCH_WSL_DISTRO:-Arch}"
|
||
|
||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||
info() { echo -e "${BLUE}==>${NC} $1"; }
|
||
success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||
warn() { echo -e "${YELLOW}[!!]${NC} $1"; }
|
||
error() { echo -e "${RED}[ERR]${NC} $1"; exit 1; }
|
||
step() { echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${CYAN} $1${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"; }
|
||
|
||
# ============================================================
|
||
# Bascule Git Bash → WSL si besoin
|
||
# ============================================================
|
||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "$WINDIR" ]]; then
|
||
info "Git Bash détecté — bascule dans WSL ($DISTRO_NAME)..."
|
||
export MSYS_NO_PATHCONV=1
|
||
export MSYS2_ARG_CONV_EXCL="*"
|
||
|
||
# WSL doit être présent.
|
||
command -v wsl.exe >/dev/null 2>&1 \
|
||
|| error "WSL introuvable — PowerShell admin : 'wsl --install', puis installe une distro Arch et relance."
|
||
|
||
# Choix de la distro Arch : override $ARCH_WSL_DISTRO, sinon nom exact,
|
||
# sinon 1re distro « arch » réellement joignable (énum via `wsl -l -q`,
|
||
# sortie UTF-16LE → on retire NUL/CR ; on garde la sonde « echo ok »).
|
||
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
|
||
warn "Distros WSL installées :"
|
||
wsl.exe -l -v 2>/dev/null | tr -d '\0' >&2 || true
|
||
error "Aucune distro WSL Arch joignable. Installe ArchWSL (nom 'Arch') ou 'wsl --install -d archlinux', ou exporte ARCH_WSL_DISTRO=<nom>."
|
||
fi
|
||
info "Distro WSL : $DISTRO_NAME"
|
||
|
||
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')/setup-aur.sh" \
|
||
2>/dev/null | tr -d '\r\n')
|
||
[ -n "$SCRIPT_PATH_WSL" ] \
|
||
|| error "Traduction du chemin vers WSL échouée (wslpath). Vérifie ARCH_WSL_DISTRO ($DISTRO_NAME)."
|
||
wsl.exe -d "$DISTRO_NAME" -u root -- test -f "$SCRIPT_PATH_WSL" \
|
||
|| error "Script introuvable dans WSL : $SCRIPT_PATH_WSL"
|
||
|
||
# Relance en root depuis le vrai chemin ($0 → SCRIPT_DIR correct).
|
||
# Fins de ligne garanties LF par .gitattributes (*.sh text eol=lf).
|
||
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, …) →
|
||
# setup dans un conteneur Arch via podman (rootful, --privileged).
|
||
# Voir build.sh pour le détail du design ; même image, même volume de
|
||
# cache pacman, dépôt monté au MÊME chemin absolu (→ local-repo/ revient
|
||
# sur l'hôte). makepkg refuse root mais on est dans le conteneur en root :
|
||
# setup-aur.sh recrée son propre utilisateur fwsbuild, c'est transparent.
|
||
# ============================================================
|
||
host_is_arch() {
|
||
# Fedora empaquette aussi « pacman » → sa seule présence ne suffit pas.
|
||
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
|
||
info "Hôte non-Arch détecté — setup dans un conteneur Arch (podman)..."
|
||
command -v podman >/dev/null 2>&1 \
|
||
|| error "podman introuvable. Fedora : 'sudo dnf install -y podman' ; Debian/Ubuntu : 'sudo apt install -y podman'."
|
||
|
||
SUDO=(); [ "$EUID" -eq 0 ] || SUDO=(sudo)
|
||
TTY=(); [ -t 0 ] && TTY=(-it)
|
||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
IMAGE="docker.io/library/archlinux:latest"
|
||
|
||
# pull explicite : « run » ne re-résout pas « latest » une fois en cache.
|
||
info "Pull de l'image $IMAGE…"
|
||
"${SUDO[@]}" podman pull "$IMAGE"
|
||
|
||
info "Relance dans le conteneur (setup-aur.sh)…"
|
||
# --network=host : le bridge podman ne route plus vers l'extérieur sur
|
||
# 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 \
|
||
-v "$REPO_DIR":"$REPO_DIR" \
|
||
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
||
-w "$REPO_DIR" \
|
||
"$IMAGE" \
|
||
bash "$REPO_DIR/setup-aur.sh"
|
||
|
||
# podman rootful → local-repo/ appartient à root : on le rend à l'hôte.
|
||
OWNER="$(stat -c '%u:%g' "$REPO_DIR" 2>/dev/null || true)"
|
||
if [ -n "$OWNER" ] && [ -e "$REPO_DIR/local-repo" ]; then
|
||
"${SUDO[@]}" chown -R "$OWNER" "$REPO_DIR/local-repo" 2>/dev/null || true
|
||
fi
|
||
exit 0
|
||
fi
|
||
|
||
# ============================================================
|
||
# Vérifications
|
||
# ============================================================
|
||
command -v pacman >/dev/null || error "pacman introuvable (lance depuis Arch / WSL)"
|
||
# Élévation root : sudo de préférence, sinon su. $0 cité (chemins à espaces).
|
||
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
|
||
success "Arch Linux (root) ✓"
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
LOCAL_REPO="$SCRIPT_DIR/local-repo"
|
||
BUILD_DIR="/tmp/fws-aur-build"
|
||
BUILD_USER="fwsbuild"
|
||
|
||
# ============================================================
|
||
step "0/4 — Préparation pacman (sandbox WSL + keyring)"
|
||
# ============================================================
|
||
# pacman ≥ 7 utilise landlock pour sandboxer les ops fs. Ni le kernel WSL
|
||
# ni (selon le noyau) le conteneur ne le supportent → désactivation explicite.
|
||
if grep -qi 'microsoft\|wsl' /proc/version 2>/dev/null || [ -n "$FWS_IN_CONTAINER" ]; then
|
||
if ! grep -qE '^[[:space:]]*DisableSandbox' /etc/pacman.conf; then
|
||
sed -i '/^\[options\]/a DisableSandbox' /etc/pacman.conf
|
||
warn "WSL/conteneur détecté → DisableSandbox ajouté à /etc/pacman.conf"
|
||
fi
|
||
fi
|
||
|
||
# Keyring : sur une WSL Arch ancienne, archlinux-keyring est périmé et
|
||
# bloque tout install ("unknown trust"). On bypass SigLevel uniquement
|
||
# pour mettre à jour le paquet keyring, puis on rétablit.
|
||
info "Refresh du trousseau pacman…"
|
||
pacman-key --init 2>/dev/null || true
|
||
|
||
if [ -f /etc/pacman.conf ] && ! grep -qE '^[[:space:]]*SigLevel[[:space:]]*=[[:space:]]*Never' /etc/pacman.conf; then
|
||
cp -f /etc/pacman.conf /etc/pacman.conf.fws.bak
|
||
sed -i 's/^SigLevel.*/SigLevel = Never/' /etc/pacman.conf
|
||
pacman -Sy --noconfirm --needed archlinux-keyring \
|
||
|| warn "Maj archlinux-keyring échouée (sigs bypass) — on continue"
|
||
mv -f /etc/pacman.conf.fws.bak /etc/pacman.conf
|
||
fi
|
||
|
||
pacman-key --populate archlinux 2>/dev/null || true
|
||
|
||
# Maj système complète : indispensable pour gérer les "package splits"
|
||
# (ex. gcc-libs → libgcc + libstdc++) sinon makepkg plantera sur
|
||
# "conflicting files" lors de l'install des dépendances.
|
||
info "Mise à jour complète du système (peut être long la première fois)…"
|
||
pacman -Syu --noconfirm \
|
||
|| warn "pacman -Syu a partiellement échoué — on continue"
|
||
|
||
# ============================================================
|
||
step "1/4 — Outils de build"
|
||
# ============================================================
|
||
pacman -Sy --needed --noconfirm base-devel git
|
||
|
||
# ============================================================
|
||
step "2/4 — Préparation utilisateur de build (non-root pour makepkg)"
|
||
# ============================================================
|
||
rm -rf "$BUILD_DIR"
|
||
mkdir -p "$BUILD_DIR"
|
||
|
||
if ! id "$BUILD_USER" &>/dev/null; then
|
||
useradd -m "$BUILD_USER"
|
||
echo "$BUILD_USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/fwsbuild
|
||
chmod 440 /etc/sudoers.d/fwsbuild
|
||
success "Utilisateur $BUILD_USER créé"
|
||
fi
|
||
chown -R "$BUILD_USER:$BUILD_USER" "$BUILD_DIR"
|
||
|
||
# ============================================================
|
||
# Helpers — build INCRÉMENTAL (saute un paquet déjà présent dans local-repo
|
||
# à la même version pkgver-pkgrel). Gros gain quand on n'itère que sur 1 paquet.
|
||
# ============================================================
|
||
mkdir -p "$LOCAL_REPO"
|
||
|
||
# Version « [epoch:]pkgver-pkgrel » lue dans un PKGBUILD (sourcing en sous-shell).
|
||
# Vide si illisible → on (re)construit par sécurité. Nos paquets n'ont pas de
|
||
# pkgver() VCS, donc le pkgver littéral est exact.
|
||
pkgbuild_fullver() {
|
||
( cd "$1" 2>/dev/null && source ./PKGBUILD >/dev/null 2>&1 \
|
||
&& printf '%s%s-%s' "${epoch:+$epoch:}" "$pkgver" "$pkgrel" ) 2>/dev/null || true
|
||
}
|
||
|
||
# Le paquet <name> en version <fullver> est-il déjà dans local-repo ? Le motif
|
||
# « -$ver- » borne la version (évite qu'« anaconda » matche « anaconda-widgets »).
|
||
repo_has() {
|
||
[ -n "$2" ] || return 1
|
||
local f
|
||
for f in "$LOCAL_REPO/$1-$2-"*.pkg.tar.*; do [ -e "$f" ] && return 0; done
|
||
return 1
|
||
}
|
||
|
||
# Publie les .pkg d'un build vers local-repo en purgeant d'abord les anciennes
|
||
# versions de CE paquet (+ son sous-paquet -debug). Le « -[0-9] » empêche
|
||
# « anaconda » d'attraper « anaconda-widgets » / « anaconda-debug ».
|
||
# Prend la LISTE EXPLICITE des fichiers à publier (pas un glob sur le dossier :
|
||
# un .pkg d'un build précédent peut traîner dans $BUILD_DIR/<name> et serait
|
||
# republié par-dessus la purge).
|
||
publish_to_repo() {
|
||
local name="$1"; shift
|
||
rm -f "$LOCAL_REPO/$name"-[0-9]*.pkg.tar.* \
|
||
"$LOCAL_REPO/$name"-debug-[0-9]*.pkg.tar.* 2>/dev/null || true
|
||
cp -f "$@" "$LOCAL_REPO/" || error "Copie de $name vers local-repo échouée"
|
||
}
|
||
|
||
# Construit OU saute <name> (source déjà déposée dans $BUILD_DIR/<name>).
|
||
# Installe TOUJOURS le résultat (depuis local-repo si sauté) pour que les
|
||
# paquets dépendants suivants résolvent leurs deps (chaînes AUR→AUR, anaconda…).
|
||
build_or_skip() {
|
||
# NB : référencer "$1" et PAS "$name" ici — dans un `local a=… b=…` à une
|
||
# seule ligne, bash développe la 2ᵉ valeur AVANT que `name` ne soit affecté,
|
||
# donc "$name" y serait vide → d="/tmp/fws-aur-build/" (PKGBUILD introuvable).
|
||
local name="$1" d="$BUILD_DIR/$1" ver
|
||
[ -f "$d/PKGBUILD" ] || error "PKGBUILD introuvable pour $name ($d)"
|
||
ver="$(pkgbuild_fullver "$d")"
|
||
if repo_has "$name" "$ver"; then
|
||
success "$name $ver déjà construit → saut (install depuis local-repo)"
|
||
pacman -U --noconfirm --needed "$LOCAL_REPO/$name-$ver-"*.pkg.tar.* \
|
||
|| error "Install (repo) de $name échouée"
|
||
return 0
|
||
fi
|
||
info "Build $name ${ver:+($ver) }(peut prendre plusieurs minutes)…"
|
||
chown -R "$BUILD_USER:$BUILD_USER" "$d"
|
||
su - "$BUILD_USER" -c "cd '$d' && makepkg -sf --noconfirm" \
|
||
|| error "Build $name échoué"
|
||
# N'installer/publier QUE les paquets produits par CE PKGBUILD dans SA
|
||
# version courante (makepkg --packagelist), jamais un glob "$d/"* : après
|
||
# un bump de pkgrel, l'ancien .pkg traîne encore dans $d (BUILD_DIR n'est
|
||
# pas nettoyé entre les runs) → pacman -U recevait 2× le même nom de
|
||
# paquet (« erreur : cible répétée ») et la vieille version repartait
|
||
# dans local-repo. Le grep filtre tout bruit éventuel de su - (PAM…).
|
||
# NB : --packagelist liste aussi un -debug qui n'est PAS toujours produit
|
||
# (paquet pur python : rien à stripper) → ne garder que les fichiers
|
||
# réellement présents sur disque.
|
||
local -a pkgfiles=() existing=()
|
||
local f
|
||
mapfile -t pkgfiles < <(su - "$BUILD_USER" -c "cd '$d' && makepkg --packagelist" \
|
||
| grep -E '\.pkg\.tar\.')
|
||
for f in "${pkgfiles[@]}"; do [ -e "$f" ] && existing+=("$f"); done
|
||
[ "${#existing[@]}" -gt 0 ] || error "Aucun paquet produit trouvé pour $name"
|
||
pacman -U --noconfirm "${existing[@]}" || error "Install de $name échouée"
|
||
publish_to_repo "$name" "${existing[@]}"
|
||
success "$name build + install OK"
|
||
}
|
||
|
||
# ============================================================
|
||
step "3/4 — Build des paquets (saut si déjà à jour dans local-repo)"
|
||
# ============================================================
|
||
# --- Paquets AUR (ordre : deps avant dépendants ; clone shallow puis build/saut) ---
|
||
for pkg in "${AUR_PACKAGES[@]}"; do
|
||
info "Clonage $pkg (AUR)…"
|
||
rm -rf "$BUILD_DIR/$pkg"
|
||
su - "$BUILD_USER" -c "git clone --depth=1 https://aur.archlinux.org/${pkg}.git $BUILD_DIR/$pkg" \
|
||
|| error "Clonage $pkg échoué"
|
||
build_or_skip "$pkg"
|
||
done
|
||
|
||
# --- PKGBUILD locaux FWS (ordre explicite, feuilles d'abord, anaconda dernier) ---
|
||
LOCAL_PKGBUILDS_DIR="$SCRIPT_DIR/pkgbuilds"
|
||
LOCAL_BUILD_ORDER=(
|
||
fws-dnd
|
||
pykickstart python-productmd python-requests-file python-simpleline
|
||
python-crypt_r python-iso639 python-langtable python-meh python-blivet
|
||
anaconda-widgets anaconda
|
||
)
|
||
BUILT_LOCAL=()
|
||
|
||
prepare_and_build_local() {
|
||
local name="$1" dir="$LOCAL_PKGBUILDS_DIR/$1"
|
||
[ -f "$dir/PKGBUILD" ] || return 1
|
||
rm -rf "$BUILD_DIR/$name"
|
||
cp -a "$dir" "$BUILD_DIR/$name"
|
||
build_or_skip "$name"
|
||
}
|
||
|
||
if [ -d "$LOCAL_PKGBUILDS_DIR" ]; then
|
||
# 1) Ordre explicite (deps avant dépendants).
|
||
for name in "${LOCAL_BUILD_ORDER[@]}"; do
|
||
if prepare_and_build_local "$name"; then BUILT_LOCAL+=("$name"); fi
|
||
done
|
||
# 2) Repli : tout PKGBUILD présent mais non listé (futur ajout oublié).
|
||
for dir in "$LOCAL_PKGBUILDS_DIR"/*/; do
|
||
[ -f "$dir/PKGBUILD" ] || continue
|
||
name="$(basename "$dir")"
|
||
printf '%s\n' "${BUILT_LOCAL[@]}" | grep -qx "$name" && continue
|
||
prepare_and_build_local "$name" || true
|
||
done
|
||
fi
|
||
|
||
# ============================================================
|
||
step "4/4 — (Re)génération de la base du repo local fws-local"
|
||
# ============================================================
|
||
# local-repo contient déjà TOUS les paquets (construits ce run + sautés des
|
||
# runs précédents). On régénère juste la base de données pacman du dépôt.
|
||
rm -f "$LOCAL_REPO"/fws-local.db* "$LOCAL_REPO"/fws-local.files* 2>/dev/null || true
|
||
repo-add "$LOCAL_REPO/fws-local.db.tar.gz" "$LOCAL_REPO"/*.pkg.tar.* \
|
||
|| error "repo-add a échoué"
|
||
success "Repo local prêt : $LOCAL_REPO"
|
||
|
||
# ============================================================
|
||
# Nettoyage utilisateur de build
|
||
# ============================================================
|
||
userdel -r "$BUILD_USER" 2>/dev/null || true
|
||
rm -f /etc/sudoers.d/fwsbuild
|
||
|
||
echo ""
|
||
echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${GREEN}║ Setup terminé. Lance maintenant : ./build.sh ║${NC}"
|
||
echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}"
|