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
112 lines
5.2 KiB
JavaScript
112 lines
5.2 KiB
JavaScript
// © 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,
|
||
// ./install_dev.sh, ./run.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.
|
||
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');
|
||
const SCRIPTS_BASH = ['install.sh', 'install_dev.sh', 'run.sh', 'scripts/installation/avec_node.sh'];
|
||
|
||
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}"/);
|
||
});
|
||
});
|