[ADD] run: make and ./run.sh, Electron start check, container matrix
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
2026-10-06 05:52:17 -04:00
|
|
|
#!/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
|
[FIX] matrix: UTF-8 locale for Chromium, skip ps reading when absent
On Linux, Chromium converts origin-private file names through the process
locale. The debian:13 and fedora:44 images set no LANG, so every accented
name failed with TypeMismatchError there, while Arch, which sets C.UTF-8,
passed. The container steps now run under LANG=C.UTF-8, and a browser test
names that cause when a Chromium cannot write an accented name. The long
test that reads the real ps skips with its reason when ps is missing.
Checked: debian-13 and fedora-44 test_navigateur and test_long green in
containers; 36 browser and 26 node-long tests locally.
--- FR ---
[FIX] matrice : locale UTF-8 pour Chromium, lecture ps sautée sans ps
Sous Linux, Chromium convertit les noms de fichiers de l'origine privée par
la locale du processus. Les images debian:13 et fedora:44 ne posent aucun
LANG : chaque nom accentué y échouait (TypeMismatchError), alors qu'Arch,
qui pose C.UTF-8, passait. Les étapes des conteneurs tournent sous
LANG=C.UTF-8, et une épreuve du navigateur nomme cette cause quand un
Chromium n'écrit pas un nom accentué. L'épreuve longue qui lit le vrai ps se
saute, raison dite, quand ps manque.
Vérifié : debian-13 et fedora-44 au vert en conteneur ; 36 + 26 en local.
Assisted-by: Claude Opus 5.5
2026-10-07 02:14:19 -04:00
|
|
|
# que sur une installation réussie. Elles tournent sous LANG=C.UTF-8, des
|
|
|
|
|
# paramètres régionaux UTF-8 comme ceux d'un poste de développement, que la
|
|
|
|
|
# plupart des images ne posent pas : sous le « C » qu'elles laissent,
|
|
|
|
|
# Chromium ne convertit plus un nom hors ASCII de l'OPFS, et les épreuves
|
|
|
|
|
# du navigateur n'éprouvent plus le projet. C.UTF-8 vient avec la glibc,
|
|
|
|
|
# sans paquet de langue ; une image qui ne le porterait pas retomberait sur
|
|
|
|
|
# « C », comme sans la variable.
|
[ADD] run: make and ./run.sh, Electron start check, container matrix
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
2026-10-06 05:52:17 -04:00
|
|
|
ETAPES_DEV='
|
[FIX] matrix: UTF-8 locale for Chromium, skip ps reading when absent
On Linux, Chromium converts origin-private file names through the process
locale. The debian:13 and fedora:44 images set no LANG, so every accented
name failed with TypeMismatchError there, while Arch, which sets C.UTF-8,
passed. The container steps now run under LANG=C.UTF-8, and a browser test
names that cause when a Chromium cannot write an accented name. The long
test that reads the real ps skips with its reason when ps is missing.
Checked: debian-13 and fedora-44 test_navigateur and test_long green in
containers; 36 browser and 26 node-long tests locally.
--- FR ---
[FIX] matrice : locale UTF-8 pour Chromium, lecture ps sautée sans ps
Sous Linux, Chromium convertit les noms de fichiers de l'origine privée par
la locale du processus. Les images debian:13 et fedora:44 ne posent aucun
LANG : chaque nom accentué y échouait (TypeMismatchError), alors qu'Arch,
qui pose C.UTF-8, passait. Les étapes des conteneurs tournent sous
LANG=C.UTF-8, et une épreuve du navigateur nomme cette cause quand un
Chromium n'écrit pas un nom accentué. L'épreuve longue qui lit le vrai ps se
saute, raison dite, quand ps manque.
Vérifié : debian-13 et fedora-44 au vert en conteneur ; 36 + 26 en local.
Assisted-by: Claude Opus 5.5
2026-10-07 02:14:19 -04:00
|
|
|
export LANG=C.UTF-8
|
[ADD] run: make and ./run.sh, Electron start check, container matrix
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
2026-10-06 05:52:17 -04:00
|
|
|
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"
|