make with no target does what ./run.sh does: Electron on a graphical session, Vite's server without one or where Electron is not published (s390x, ppc64le). Every npm, npx and node recipe goes through the project's Node. make essai_demarrage starts the real Electron through Playwright and reads its window title, on Chromium's headless Ozone platform when there is no display: no X server is needed. scripts/verifier_systemes.sh replays install and tests in podman over the QEMU catalogue of TODO, and stops only its own containers. Checked: 17 Linux images and Nix pass all six steps; pwsh parses. --- FR --- [ADD] lancement : make et ./run.sh, essai d'Electron, matrice make sans cible fait ce que fait ./run.sh : Electron sur une session graphique, le serveur de Vite sans elle ou là où Electron n'est pas publié (s390x, ppc64le). Chaque recette npm, npx ou node passe par le Node du projet. make essai_demarrage lance la vraie coquille Electron par Playwright et lit le titre de sa fenêtre, sur la plateforme Ozone headless de Chromium sans écran : aucun serveur X n'est requis. scripts/verifier_systemes.sh rejoue installation et épreuves sous podman sur le catalogue QEMU de TODO, et n'arrête que ses conteneurs. Vérifié : 17 images Linux et Nix passent les six étapes ; pwsh analyse. Assisted-by: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
244 lines
9.3 KiB
Bash
Executable file
244 lines
9.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
# Installe et éprouve le projet sur chaque système du catalogue de machines
|
|
# virtuelles de TODO (ERPLibre), chacun dans un conteneur podman : sous un
|
|
# compte ordinaire qui passe par sudo, ./install_dev.sh, puis make test,
|
|
# make test_long, make test_navigateur, l'essai de démarrage d'Electron, et
|
|
# le serveur de Vite. Les scripts PowerShell s'éprouvent sous pwsh.
|
|
#
|
|
# scripts/verifier_systemes.sh tout le catalogue
|
|
# scripts/verifier_systemes.sh debian-13 arch ces systèmes
|
|
# scripts/verifier_systemes.sh --liste les systèmes connus
|
|
#
|
|
# Ce qu'un conteneur n'éprouve pas : le bac à sable d'Electron (pas d'espaces
|
|
# de noms d'utilisateur, Electron s'y lance sans), une autre architecture
|
|
# que celle de l'hôte, NixOS lui-même (l'image nix éprouve shell.nix, sur
|
|
# une base qui n'est pas NixOS), une vraie session graphique.
|
|
#
|
|
# GTT_PARALLELES (4 par défaut) borne les conteneurs simultanés ; les
|
|
# journaux vont dans GTT_JOURNAUX, un dossier temporaire par défaut. Les
|
|
# téléchargements — npm, Electron, Chromium, Node — se partagent entre
|
|
# conteneurs par des volumes nommés gttl-cache-*.
|
|
set -euo pipefail
|
|
|
|
racine="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
# Système, image : le catalogue de script/qemu/deploy_qemu.py d'ERPLibre.
|
|
# Proxmox VE 9 est Debian 13 sous ses paquets : debian-13 le représente.
|
|
CATALOGUE='
|
|
ubuntu-24.04 docker.io/library/ubuntu:24.04
|
|
ubuntu-25.10 docker.io/library/ubuntu:25.10
|
|
ubuntu-26.04 docker.io/library/ubuntu:26.04
|
|
debian-12 docker.io/library/debian:12
|
|
debian-13 docker.io/library/debian:13
|
|
fedora-41 docker.io/library/fedora:41
|
|
fedora-42 docker.io/library/fedora:42
|
|
fedora-43 docker.io/library/fedora:43
|
|
fedora-44 docker.io/library/fedora:44
|
|
almalinux-9 docker.io/library/almalinux:9
|
|
almalinux-10 docker.io/library/almalinux:10
|
|
rocky-9 docker.io/rockylinux/rockylinux:9
|
|
rocky-10 docker.io/rockylinux/rockylinux:10
|
|
opensuse-leap-16.0 registry.opensuse.org/opensuse/leap:16.0
|
|
opensuse-tumbleweed registry.opensuse.org/opensuse/tumbleweed:latest
|
|
arch docker.io/library/archlinux:latest
|
|
nix docker.io/nixos/nix:latest
|
|
pwsh mcr.microsoft.com/powershell:latest
|
|
'
|
|
|
|
usage() {
|
|
sed -n '9,12p' "$0" | sed 's/^# \{0,1\}//'
|
|
}
|
|
|
|
image_de() {
|
|
printf '%s\n' "$CATALOGUE" | awk -v s="$1" '$1 == s { print $2 }'
|
|
}
|
|
|
|
systemes=()
|
|
for argument in "$@"; do
|
|
case "$argument" in
|
|
--liste)
|
|
printf '%s\n' "$CATALOGUE" | awk 'NF { print $1 }'
|
|
exit 0
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
[ -n "$(image_de "$argument")" ] || { echo "système inconnu : $argument (--liste)" >&2; exit 2; }
|
|
systemes+=("$argument")
|
|
;;
|
|
esac
|
|
done
|
|
if [ ${#systemes[@]} -eq 0 ]; then
|
|
while read -r s _; do
|
|
[ -n "$s" ] && systemes+=("$s")
|
|
done <<< "$CATALOGUE"
|
|
fi
|
|
command -v podman > /dev/null || { echo "podman introuvable." >&2; exit 1; }
|
|
|
|
journaux="${GTT_JOURNAUX:-$(mktemp -d "${TMPDIR:-/tmp}/verifier_systemes.XXXXXX")}"
|
|
mkdir -p "$journaux"
|
|
|
|
# Étiquette des conteneurs de cette vérification : une interruption arrête
|
|
# ceux-là, et aucun autre conteneur de la machine.
|
|
ETIQUETTE="gttl-verification=$$"
|
|
arreter() {
|
|
podman ps -q --filter "label=$ETIQUETTE" | xargs -r podman stop -t 2 > /dev/null 2>&1 || true
|
|
}
|
|
trap 'arreter; exit 130' INT
|
|
trap 'arreter; exit 143' TERM
|
|
|
|
# Copie de l'arbre de travail tel qu'il est — fichiers suivis et nouveaux
|
|
# non ignorés —, sans node_modules : chaque conteneur part de zéro.
|
|
source_copie="$journaux/source"
|
|
rm -rf "$source_copie"
|
|
mkdir -p "$source_copie"
|
|
(cd "$racine" && git ls-files -co --exclude-standard -z | tar --null -cf - -T - | tar -xf - -C "$source_copie")
|
|
|
|
# Préparation sous root, par famille : sudo et un compte ordinaire « dev »
|
|
# qui l'emploie sans mot de passe, comme un poste de développement. Son uid
|
|
# est le même dans chaque conteneur — 1500, libre aussi sous Ubuntu, dont
|
|
# l'image donne 1000 à son propre compte — : les caches partagés ne
|
|
# changent jamais de propriétaire.
|
|
preparation() {
|
|
case "$1" in
|
|
ubuntu-* | debian-*) echo 'apt-get update -q && DEBIAN_FRONTEND=noninteractive apt-get install -y -q sudo' ;;
|
|
fedora-* | almalinux-* | rocky-*) echo 'dnf install -y sudo shadow-utils' ;;
|
|
opensuse-*) echo 'zypper --non-interactive install sudo shadow' ;;
|
|
arch) echo 'pacman -Syu --noconfirm sudo' ;;
|
|
esac
|
|
}
|
|
|
|
# Étapes sous le compte dev, chacune annoncée par une ligne « === étape
|
|
# statut » que le bilan relit. L'essai et le serveur de Vite ne se jouent
|
|
# que sur une installation réussie.
|
|
ETAPES_DEV='
|
|
etape() { local nom="$1"; shift; if "$@"; then echo "=== $nom ok"; else echo "=== $nom ÉCHEC"; return 1; fi; }
|
|
cd "$HOME/projet"
|
|
etape install_dev ./install_dev.sh || exit 1
|
|
etape test make test
|
|
etape test_long make test_long
|
|
etape test_navigateur make test_navigateur
|
|
etape essai_demarrage env GTT_SANS_BAC_A_SABLE=1 make essai_demarrage
|
|
web() {
|
|
./run.sh --web > "$HOME/web.log" 2>&1 &
|
|
scripts/installation/avec_node.sh node -e "
|
|
const fin = Date.now() + 60000;
|
|
(async () => {
|
|
while (Date.now() < fin) {
|
|
try {
|
|
const page = await (await fetch(\"http://localhost:5173/\")).text();
|
|
if (page.includes(\"/src/main.js\")) process.exit(0);
|
|
} catch {}
|
|
await new Promise((r) => setTimeout(r, 500));
|
|
}
|
|
process.exit(1);
|
|
})();"
|
|
}
|
|
etape web web
|
|
'
|
|
|
|
verifier_linux() {
|
|
local systeme="$1" image="$2"
|
|
podman run --rm --env-host=false --label "$ETIQUETTE" \
|
|
-v "$source_copie:/source:ro" \
|
|
-v gttl-cache-npm:/home/dev/.npm \
|
|
-v gttl-cache-outils:/home/dev/.cache \
|
|
"$image" bash -c "
|
|
set -e
|
|
$(preparation "$systeme")
|
|
useradd -m -u 1500 -s /bin/bash dev 2> /dev/null || true
|
|
echo 'dev ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/dev
|
|
cp -a /source /home/dev/projet
|
|
chown -R dev: /home/dev/projet
|
|
chown dev: /home/dev /home/dev/.npm /home/dev/.cache
|
|
sudo -u dev -H bash -c '$ETAPES_DEV'
|
|
"
|
|
}
|
|
|
|
# L'image nix tourne sous root, sans sudo ni useradd : elle éprouve
|
|
# shell.nix, que GTT_FAMILLE désigne, sur une base qui n'est pas NixOS.
|
|
# ./install.sh y entre de lui-même ; make n'existe que dans shell.nix, où
|
|
# les étapes suivantes s'exécutent, comme sous NixOS.
|
|
verifier_nix() {
|
|
podman run --rm --env-host=false --label "$ETIQUETTE" -e GTT_FAMILLE=nixos \
|
|
-v "$source_copie:/source:ro" \
|
|
"$2" sh -c "
|
|
cp -a /source /root/projet
|
|
export HOME=/root
|
|
cd /root/projet
|
|
if nix-shell -p bash --run ./install.sh; then echo '=== entree_shell_nix ok'; else echo '=== entree_shell_nix ÉCHEC'; exit 1; fi
|
|
nix-shell scripts/installation/shell.nix --run 'bash -c '\''$ETAPES_DEV'\'''
|
|
"
|
|
}
|
|
|
|
# Les scripts PowerShell sous pwsh : l'analyse syntaxique de chacun, puis la
|
|
# lecture de .node-version, le choix de l'archive dans SHASUMS256.txt, et le
|
|
# téléchargement, la vérification et l'extraction de Node pour win-x64 —
|
|
# node.exe ne s'exécute pas ici, son extraction se constate.
|
|
verifier_pwsh() {
|
|
podman run --rm --env-host=false --label "$ETIQUETTE" -v "$source_copie:/source:ro" "$2" pwsh -NoProfile -Command '
|
|
$ErrorActionPreference = "Stop"
|
|
$erreurs = 0
|
|
foreach ($f in Get-ChildItem /source/scripts/windows/*.ps1) {
|
|
$jetons = $null; $fautes = $null
|
|
[void][System.Management.Automation.Language.Parser]::ParseFile($f.FullName, [ref]$jetons, [ref]$fautes)
|
|
if ($fautes.Count -gt 0) { $erreurs++; Write-Host "$($f.Name) : $($fautes[0].Message)" }
|
|
}
|
|
if ($erreurs -eq 0) { Write-Host "=== analyse ok" } else { Write-Host "=== analyse ÉCHEC"; exit 1 }
|
|
Copy-Item -Recurse /source /tmp/projet
|
|
$env:LOCALAPPDATA = "/tmp/donnees"
|
|
$env:PROCESSOR_ARCHITECTURE = "AMD64"
|
|
try { . /tmp/projet/scripts/windows/commun.ps1 } catch { Write-Host "=== chargement ÉCHEC $_"; exit 1 }
|
|
Write-Host "=== chargement ok"
|
|
$choix = Find-GttArchiveNode @("a1 node-v26.10.0-win-x64.7z", "b2 node-v26.10.0-win-x64.zip", "c3 node-v26.10.0-win-arm64.zip") "x64"
|
|
if ("$choix" -eq "b2 node-v26.10.0-win-x64.zip" -and (Get-GttMajeure) -match "^[0-9]+$") { Write-Host "=== choix ok" } else { Write-Host "=== choix ÉCHEC $choix"; exit 1 }
|
|
try { Install-GttNode } catch { }
|
|
if (Test-Path (Join-Path (Get-GttDossierNode) "node.exe")) { Write-Host "=== node_windows ok" } else { Write-Host "=== node_windows ÉCHEC"; exit 1 }
|
|
'
|
|
}
|
|
|
|
verifier() {
|
|
local systeme="$1" image journal
|
|
image="$(image_de "$systeme")"
|
|
journal="$journaux/$systeme.log"
|
|
{
|
|
echo "### $systeme ($image)"
|
|
case "$systeme" in
|
|
nix) verifier_nix "$systeme" "$image" ;;
|
|
pwsh) verifier_pwsh "$systeme" "$image" ;;
|
|
*) verifier_linux "$systeme" "$image" ;;
|
|
esac
|
|
} > "$journal" 2>&1 || true
|
|
echo "$systeme : $(grep -c '^=== .* ok$' "$journal") ok, $(grep -c '^=== .* ÉCHEC' "$journal") échec(s)"
|
|
}
|
|
|
|
paralleles="${GTT_PARALLELES:-4}"
|
|
echo "Journaux : $journaux"
|
|
for systeme in "${systemes[@]}"; do
|
|
while [ "$(jobs -rp | wc -l)" -ge "$paralleles" ]; do
|
|
sleep 2
|
|
done
|
|
verifier "$systeme" &
|
|
done
|
|
wait
|
|
|
|
echo
|
|
echo "Bilan :"
|
|
statut=0
|
|
for systeme in "${systemes[@]}"; do
|
|
ligne="$(grep '^=== ' "$journaux/$systeme.log" | sed 's/^=== //' | tr '\n' ' ')"
|
|
case "$ligne" in
|
|
'' | *ÉCHEC*) statut=1 ;;
|
|
esac
|
|
printf ' %-22s %s\n' "$systeme" "${ligne:-aucune étape (voir $journaux/$systeme.log)}"
|
|
done
|
|
exit "$statut"
|