From df7fb212f6593cc9cd05ac3b803d7535fbcbac50 Mon Sep 17 00:00:00 2001 From: Alban Pasquelin Date: Tue, 8 Sep 2026 18:38:49 +0200 Subject: [PATCH 1/2] =?UTF-8?q?Rend=20au=20contr=C3=B4le=20des=20licences?= =?UTF-8?q?=20les=20paquets=20qu'il=20concluait=20sans=20ouvrir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `licenceDe` balayait `node_modules/.pnpm` à `-maxdepth 4`. Un nom plat y tient en quatre segments, un nom scopé en cinq : 14 des 38 paquets déclarés ici rendaient `null`, et `if (licence === null) continue` les comptait comme jugés. Le script concluait « 38 paquets, tous permissifs » en en ayant ouvert 24. La lecture passe par le lien que le gestionnaire pose pour toute dépendance déclarée — alias `npm:` compris, scopes compris. Et le silence devient un échec : un paquet que ce dépôt installe et qu'on ne peut pas ouvrir rougit la porte en se nommant. Les pairs gardent la tolérance, qui avait été écrite pour elles. Fichier du socle partagé : identique octet pour octet dans panels et AI Desktop Studio, où la correction est portée telle quelle. --- scripts/check-licences.mjs | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scripts/check-licences.mjs b/scripts/check-licences.mjs index 00e89c7..d5b2665 100644 --- a/scripts/check-licences.mjs +++ b/scripts/check-licences.mjs @@ -6,11 +6,10 @@ * Écrit parce que ce fichier est une affirmation juridique, et qu'une affirmation que * personne ne revérifie dérive à la première montée de version. * - * CE FICHIER EST PARTAGÉ À L'IDENTIQUE par map3D, panels et IA Studio. La liste blanche, + * CE FICHIER EST PARTAGÉ À L'IDENTIQUE par map3D, panels et AI Desktop Studio. La liste blanche, * les paquets embarqués et la nature du dépôt vivent dans `repo.config.json`. */ import { readFileSync, existsSync } from 'node:fs' -import { execFileSync } from 'node:child_process' import { resolve } from 'node:path' const root = resolve(import.meta.dirname, '..') @@ -54,37 +53,35 @@ for (const nom of EMBARQUES) { if (!propre.devDependencies?.[nom]) problemes.push(`${nom} est déclaré embarqué mais n'est pas installé.`) } -/** La licence qu'un paquet déclare, lue dans l'arbre installé. */ +/** + * La licence qu'un paquet déclare, par le lien que le gestionnaire pose pour toute dépendance + * déclarée — alias `npm:` compris. Le balayage de `node_modules/.pnpm` qui la précédait s'arrêtait + * un segment trop haut, et ne voyait aucun paquet scopé. + */ function licenceDe(nom) { try { - const trouves = execFileSync('find', [ - 'node_modules/.pnpm', - '-maxdepth', - '4', - '-path', - `*/node_modules/${nom}/package.json`, - ]) - .toString() - .split('\n') - .filter(Boolean) - - if (trouves.length === 0) return null - return JSON.parse(readFileSync(trouves[0], 'utf8')).license ?? null + return JSON.parse(readFileSync(resolve(root, 'node_modules', nom, 'package.json'), 'utf8')) + .license ?? null } catch { return null } } -const declarees = [ +const installees = [ ...Object.keys(propre.dependencies ?? {}), ...Object.keys(propre.devDependencies ?? {}), - ...Object.keys(propre.peerDependencies ?? {}), ] +// Une pair est déclarée pour le consommateur, et le gestionnaire n'installe pas celles qui sont +// optionnelles : absente ici, elle n'a rien à juger. Ce que CE dépôt installe, si. +const declarees = [...installees, ...Object.keys(propre.peerDependencies ?? {})] +const lues = declarees.map(nom => [nom, licenceDe(nom)]) -for (const nom of declarees) { - const licence = licenceDe(nom) - if (licence === null) continue // pas installé ici ; rien à juger - if (!AUTORISEES.has(licence)) problemes.push(`${nom} est en ${licence}, absent de la liste blanche.`) +for (const [nom, licence] of lues) { + // Taire l'échec de lecture est ce qui faisait passer pour jugé tout paquet que la lecture ratait. + if (licence === null && installees.includes(nom)) + problemes.push(`${nom} est déclaré mais sa licence est illisible : paquet absent, ou sans champ « license ».`) + else if (licence !== null && !AUTORISEES.has(licence)) + problemes.push(`${nom} est en ${licence}, absent de la liste blanche.`) } if (problemes.length > 0) { @@ -93,4 +90,7 @@ if (problemes.length > 0) { process.exit(1) } -console.log(`Licences contrôlées — ${declarees.length} paquets, tous permissifs, mentions présentes.`) +const jugees = lues.filter(([, licence]) => licence !== null).length +console.log( + `Licences contrôlées — ${jugees} paquets lus sur ${declarees.length} déclarés, tous permissifs, mentions présentes.`, +) From 1c680864f1d168961f99921c3cc0edba699e12ae Mon Sep 17 00:00:00 2001 From: Alban Pasquelin Date: Wed, 23 Sep 2026 22:38:29 +0200 Subject: [PATCH 2/2] =?UTF-8?q?Mesure=20la=20fr=C3=A9quentation=20de=20pas?= =?UTF-8?q?quelin.github.io/map3D,=20avec=20consentement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Une ligne dans le gabarit, donc dans les quinze langues à la fois : le bandeau de choix et le chargement de Google Analytics vivent dans un seul fichier partagé, servi par www.pasquelin.com et commun aux quatre sites. Une correction s'y fait une fois et non quatre ; son code et ses tests sont dans le dépôt `pasquelin/site`, sous `public/shared/`. Rien n'est demandé à Google avant un accord explicite du visiteur, et le panneau emprunte les couleurs, la police et les arrondis de cette page plutôt que d'imposer les siens. Propriété GA4 : G-X64BG5H958 — un identifiant de mesure n'est pas un secret, toute page qui le porte le montre à qui lit sa source. --- site/template.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/site/template.html b/site/template.html index 0d1a74e..bc37d6b 100644 --- a/site/template.html +++ b/site/template.html @@ -35,6 +35,14 @@ + + +