[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
|
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
|
|
// Les points d'entrée de l'installation et du lancement : ./install.sh,
|
[ADD] build: create_exe.sh, the Windows executable from Linux
Building the portable executable was reachable only through make windows.
create_exe.sh is the standalone entry point, next to run.sh and install.sh:
it runs scripts/construire_windows.sh through avec_node, so under the
project's Node, and refuses on Windows and macOS, where the build does not
run. make windows now calls it, so both share one path. The installation
test guards its shebang, exec bit and shared library. Checked: --help,
bad option (2), macOS refusal (1), existing-executable refusal; node 5173.
--- FR ---
[ADD] construction : create_exe.sh, l'exécutable Windows depuis Linux
L'exécutable portable ne se construisait que par make windows. create_exe.sh
en est le point d'entrée autonome, à côté de run.sh et install.sh : il lance
scripts/construire_windows.sh par avec_node, donc sous le Node du projet, et
refuse sous Windows et macOS, où la construction ne se fait pas. make
windows passe désormais par lui. L'épreuve d'installation garde son shebang,
son bit d'exécution et la bibliothèque commune. Vérifié : --help, option
inconnue (2), refus macOS (1), refus d'un exécutable existant ; node 5173.
Assisted-by: Claude Opus 5.5
2026-10-14 00:22:33 -04:00
|
|
|
|
// ./install_dev.sh, ./run.sh, ./create_exe.sh et le Makefile sous Linux et
|
|
|
|
|
|
// macOS ; install.cmd, install_dev.cmd et run.cmd sous Windows, qui passent
|
|
|
|
|
|
// la main à PowerShell ; shell.nix sous NixOS. Ces épreuves gardent ce qui
|
|
|
|
|
|
// les casse sans rien faire échouer ailleurs : un bit d'exécution perdu, un
|
|
|
|
|
|
// chemin renommé, une commande npm qui contourne le Node du projet, une fin
|
|
|
|
|
|
// de ligne d'Unix dans un fichier de commandes, une marque d'ordre d'octets
|
|
|
|
|
|
// absente.
|
[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
|
|
|
|
import assert from 'node:assert/strict';
|
|
|
|
|
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
|
|
|
|
import { join } from 'node:path';
|
|
|
|
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
|
|
import { describe, test } from './lanceur.js';
|
|
|
|
|
|
|
|
|
|
|
|
const RACINE = fileURLToPath(new URL('..', import.meta.url));
|
|
|
|
|
|
const lire = (chemin) => readFileSync(join(RACINE, chemin), 'utf8');
|
[ADD] build: create_exe.sh, the Windows executable from Linux
Building the portable executable was reachable only through make windows.
create_exe.sh is the standalone entry point, next to run.sh and install.sh:
it runs scripts/construire_windows.sh through avec_node, so under the
project's Node, and refuses on Windows and macOS, where the build does not
run. make windows now calls it, so both share one path. The installation
test guards its shebang, exec bit and shared library. Checked: --help,
bad option (2), macOS refusal (1), existing-executable refusal; node 5173.
--- FR ---
[ADD] construction : create_exe.sh, l'exécutable Windows depuis Linux
L'exécutable portable ne se construisait que par make windows. create_exe.sh
en est le point d'entrée autonome, à côté de run.sh et install.sh : il lance
scripts/construire_windows.sh par avec_node, donc sous le Node du projet, et
refuse sous Windows et macOS, où la construction ne se fait pas. make
windows passe désormais par lui. L'épreuve d'installation garde son shebang,
son bit d'exécution et la bibliothèque commune. Vérifié : --help, option
inconnue (2), refus macOS (1), refus d'un exécutable existant ; node 5173.
Assisted-by: Claude Opus 5.5
2026-10-14 00:22:33 -04:00
|
|
|
|
const SCRIPTS_BASH = ['install.sh', 'install_dev.sh', 'run.sh', 'create_exe.sh', 'scripts/installation/avec_node.sh'];
|
[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
|
|
|
|
|
|
|
|
|
|
describe('installation : les scripts bash', () => {
|
|
|
|
|
|
test('ils sont exécutables, et cherchent bash dans le PATH : /bin/bash manque sous NixOS', () => {
|
|
|
|
|
|
for (const script of SCRIPTS_BASH) {
|
|
|
|
|
|
assert.equal(lire(script).split('\n')[0], '#!/usr/bin/env bash', script);
|
|
|
|
|
|
if (process.platform !== 'win32') {
|
|
|
|
|
|
assert.equal(statSync(join(RACINE, script)).mode & 0o111, 0o111, `${script} n'est pas exécutable`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('ils sourcent la bibliothèque commune, et s’arrêtent à la première erreur', () => {
|
|
|
|
|
|
for (const script of SCRIPTS_BASH) {
|
|
|
|
|
|
const texte = lire(script);
|
|
|
|
|
|
assert.match(texte, /^set -euo pipefail$/m, script);
|
|
|
|
|
|
assert.match(texte, /commun\.sh/, script);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('installation : le Makefile', () => {
|
|
|
|
|
|
const makefile = lire('Makefile');
|
|
|
|
|
|
const recettes = makefile.split('\n').filter((ligne) => ligne.startsWith('\t'));
|
|
|
|
|
|
|
|
|
|
|
|
test('make sans cible lance l’application, comme ./run.sh', () => {
|
|
|
|
|
|
assert.match(makefile, /^\.DEFAULT_GOAL := run$/m);
|
|
|
|
|
|
assert.match(makefile, /^LANCER := \.\/run\.sh$/m);
|
|
|
|
|
|
assert.match(makefile, /^run: .*\n\t\$\(LANCER\)$/m);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('chaque script que le Makefile nomme existe', () => {
|
|
|
|
|
|
const chemins = [...makefile.matchAll(/(?:^|[\s=])(?:\.\/)?((?:scripts\/[\w/.-]+\.(?:sh|ps1|js))|[\w-]+\.sh)\b/gm)]
|
|
|
|
|
|
.map(([, chemin]) => chemin);
|
|
|
|
|
|
assert.ok(chemins.length > 0, 'aucun script relevé');
|
|
|
|
|
|
assert.deepEqual(chemins.filter((chemin) => !existsSync(join(RACINE, chemin))), []);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('npm, npx et node ne s’y lancent que par avec_node, qui pose le Node du projet', () => {
|
|
|
|
|
|
const directes = recettes.filter((ligne) => /\b(?:npm|npx|node)\b/.test(ligne) && !ligne.includes('$(AVEC_NODE)'));
|
|
|
|
|
|
assert.ok(recettes.length > 0, 'aucune recette relevée');
|
|
|
|
|
|
assert.deepEqual(directes, []);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('installation : Windows', () => {
|
|
|
|
|
|
const commandes = readdirSync(RACINE).filter((nom) => nom.endsWith('.cmd')).sort();
|
|
|
|
|
|
|
|
|
|
|
|
test('install.cmd, install_dev.cmd et run.cmd existent à la racine', () => {
|
|
|
|
|
|
assert.deepEqual(commandes, ['install.cmd', 'install_dev.cmd', 'run.cmd']);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('chaque fichier de commandes appelle un script PowerShell qui existe, avec ses arguments', () => {
|
|
|
|
|
|
for (const nom of commandes) {
|
|
|
|
|
|
const appel = lire(nom).match(/-File "%~dp0(scripts\\windows\\[\w.]+\.ps1)"[^\r\n]*%\*/);
|
|
|
|
|
|
assert.ok(appel, `${nom} n'appelle aucun script PowerShell avec %*`);
|
|
|
|
|
|
assert.ok(existsSync(join(RACINE, ...appel[1].split('\\'))), `${nom} → ${appel[1]} absent`);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('les fichiers de commandes ont des fins de ligne de Windows', () => {
|
|
|
|
|
|
for (const nom of commandes) {
|
|
|
|
|
|
const texte = lire(nom);
|
|
|
|
|
|
assert.equal(texte.replaceAll('\r\n', '').includes('\n'), false, `${nom} a une fin de ligne d'Unix`);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('les scripts PowerShell portent la marque d’ordre d’octets UTF-8 : sans elle, Windows PowerShell 5.1 défigure les accents', () => {
|
|
|
|
|
|
const scripts = readdirSync(join(RACINE, 'scripts', 'windows')).filter((nom) => nom.endsWith('.ps1'));
|
|
|
|
|
|
assert.ok(scripts.length > 0, 'aucun script PowerShell');
|
|
|
|
|
|
for (const nom of scripts) {
|
|
|
|
|
|
const octets = readFileSync(join(RACINE, 'scripts', 'windows', nom));
|
|
|
|
|
|
assert.deepEqual([...octets.subarray(0, 3)], [0xef, 0xbb, 0xbf], nom);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('installation : la majeure de Node a une seule source', () => {
|
|
|
|
|
|
test('.node-version porte une majeure entière', () => {
|
|
|
|
|
|
assert.match(lire('.node-version'), /^\d+\n$/);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('bash, PowerShell et shell.nix la lisent dans .node-version au lieu de l’écrire', () => {
|
|
|
|
|
|
assert.match(lire('scripts/installation/commun.sh'), /\$GTT_RACINE\/\.node-version/);
|
|
|
|
|
|
assert.match(lire('scripts/windows/commun.ps1'), /'\.node-version'/);
|
|
|
|
|
|
assert.match(lire('scripts/installation/shell.nix'), /readFile \.\.\/\.\.\/\.node-version/);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('shell.nix épingle nixpkgs par révision et par somme', () => {
|
|
|
|
|
|
const nix = lire('scripts/installation/shell.nix');
|
|
|
|
|
|
assert.match(nix, /archive\/[0-9a-f]{40}\.tar\.gz/);
|
|
|
|
|
|
assert.match(nix, /sha256 = "[0-9a-z]{52}"/);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|