Set-OPS-Public/scripts/verifier_gui.py

108 lines
4.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Verifie la syntaxe du JavaScript embarque dans inventory_gui.py.
Garde-fou : py_compile ne voit pas le JS (il est dans une chaine Python).
Une seule erreur de syntaxe JS casse toute la page. On extrait le JS et on le
passe a `node --check`. Si node est absent, la verification est sautee (signale).
"""
from __future__ import annotations
import re
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import inventory_gui as g # noqa: E402
GUI : les six registres ont un formulaire genere, et la sauvegarde aussi CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus gros, sont passes au generateur — chargement, rendu et sauvegarde. L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25 applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe un formulaire genere d un formulaire qui en a l air — un champ visible a l ecran et perdu en silence a l enregistrement serait le pire des deux mondes. test_rendu_gui.py le mesure a chaque make prouver. TROIS DEFAUTS TROUVES EN CHEMIN. Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il n existe aucun defaut fixe — deriver_ressources calcule depuis les roles portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere faux fait croire qu on connait la valeur. Le schema nomme le champ derive, et l ecran montre la valeur reelle de cet hote. L option vide d un select dit desormais ce qu elle produira : « (defaut : asgard) ». Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs : elle lisait encore data.nomenclature. Elle n avait jamais leve parce que la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne voit que les chemins vivants : verifier_gui.py fait donc aussi une verification STATIQUE, qui voit ce qui dort. La validation client s accrochait a data-v, pose a la main sur trois champs. Le formulaire genere l aurait perdu et la validation serait passee au vert sur ZERO champ. Le generateur marque chaque controle, et la sauvegarde refuse si elle n en inspecte aucun. DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la matrice des integrations montre les universelles et les exemptions, et l editeur de liens contraint le role a meta/liens.yml. Le generateur s efface plutot que de remplacer un editeur qui en sait plus que lui. LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 18:26:33 -04:00
# Les noms qui RESSEMBLENT a un global sans en etre un. `data` est le cas fondateur :
# chaque `charger*`/`sauvegarder*` en declare un LOCAL, si bien que le nom se lit partout
# comme s'il etait disponible — et ne l'est pas.
NON_GLOBALES = ("data",)
# Une fonction de premier niveau du <script> : ` function nom(args) {` ou sa variante
# `async`. Le GUI les ecrit toutes a quatre espaces d'indentation.
_FONCTION = re.compile(r"^ {4}(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)", re.M)
def variables_orphelines(js: str) -> list[str]:
"""Fonctions qui LISENT un nom surveille sans le declarer ni le recevoir.
POURQUOI CETTE GARDE (2026-09-08). `node --check` ne voit que la syntaxe. Le
formulaire genere des bases lisait `data.schema` — or `data` n'est pas un global de
cette page : c'est une const locale de `charger()`. Syntaxe parfaite,
`ReferenceError` a l'ouverture, et personne pour le dire.
`test_rendu_gui.py` l'attrape maintenant en EXECUTANT les vues — mais seulement les
chemins vivants. La preuve en est qu'une SECONDE occurrence dormait dans
`sourceDeValeurs` : la vue Serveurs, seule a emprunter cette source, avait encore un
formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l'a prise.
Cette verification-ci est statique : elle voit aussi ce qui dort.
Sa limite, dite franchement : elle raisonne par fonction de premier niveau et par
motif, pas par portee lexicale. Une fonction imbriquee qui hériterait du `data` de
sa parente serait signalee a tort — on la nommerait alors autrement, ce qui est de
toute facon plus lisible.
"""
orphelins = []
bornes = [(m.start(), m.group(1), m.group(2)) for m in _FONCTION.finditer(js)]
for i, (debut, nom, args) in enumerate(bornes):
fin = bornes[i + 1][0] if i + 1 < len(bornes) else len(js)
corps = js[debut:fin]
parametres = {a.strip().split("=")[0].strip() for a in args.split(",") if a.strip()}
for surveille in NON_GLOBALES:
if surveille in parametres:
continue
# La declaration peut etre un SECOND declarateur : `const di = …, data = …`.
# Sans ce cas, `dessinerReseau()` etait signalee a tort — et un faux positif
# dans une garde finit toujours par la faire desactiver.
if re.search(rf"\b(?:const|let|var)\b[^;\n]*?(?<![\w.$]){surveille}\s*=", corps):
continue
for ligne in corps.split("\n"):
nue = ligne.strip()
if nue.startswith("//") or nue.startswith("*"):
continue
if re.search(rf"(?<![\w.$]){surveille}\s*[.\[]", nue):
orphelins.append(f"{nom}() lit `{surveille}` : {nue[:88]}")
break
return orphelins
def main() -> int:
if not shutil.which("node"):
print("node absent : verification de la syntaxe JS du GUI sautee.")
return 0
html = g.HTML.replace("__JETON__", "TEST")
blocs = re.findall(r"<script>(.*?)</script>", html, re.S)
if not blocs:
print("erreur: aucun bloc <script> trouve dans le GUI.", file=sys.stderr)
return 1
with tempfile.NamedTemporaryFile("w", suffix=".js", delete=False, encoding="utf-8") as fichier:
fichier.write("\n".join(blocs))
chemin = fichier.name
resultat = subprocess.run(["node", "--check", chemin], capture_output=True, text=True)
if resultat.returncode != 0:
print(resultat.stderr, file=sys.stderr)
return 1
GUI : les six registres ont un formulaire genere, et la sauvegarde aussi CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus gros, sont passes au generateur — chargement, rendu et sauvegarde. L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25 applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe un formulaire genere d un formulaire qui en a l air — un champ visible a l ecran et perdu en silence a l enregistrement serait le pire des deux mondes. test_rendu_gui.py le mesure a chaque make prouver. TROIS DEFAUTS TROUVES EN CHEMIN. Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il n existe aucun defaut fixe — deriver_ressources calcule depuis les roles portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere faux fait croire qu on connait la valeur. Le schema nomme le champ derive, et l ecran montre la valeur reelle de cet hote. L option vide d un select dit desormais ce qu elle produira : « (defaut : asgard) ». Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs : elle lisait encore data.nomenclature. Elle n avait jamais leve parce que la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne voit que les chemins vivants : verifier_gui.py fait donc aussi une verification STATIQUE, qui voit ce qui dort. La validation client s accrochait a data-v, pose a la main sur trois champs. Le formulaire genere l aurait perdu et la validation serait passee au vert sur ZERO champ. Le generateur marque chaque controle, et la sauvegarde refuse si elle n en inspecte aucun. DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la matrice des integrations montre les universelles et les exemptions, et l editeur de liens contraint le role a meta/liens.yml. Le generateur s efface plutot que de remplacer un editeur qui en sait plus que lui. LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur. make prouver : CONFORME, 61 OK, 0 echec, 1 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 18:26:33 -04:00
orphelins = variables_orphelines("\n".join(blocs))
if orphelins:
print("erreur: des fonctions lisent une variable qu'elles n'ont pas :", file=sys.stderr)
for ligne in orphelins:
print(f" - {ligne}", file=sys.stderr)
print(" (declarer un global, ou passer la valeur en parametre)", file=sys.stderr)
return 1
print(f"JS du GUI : syntaxe valide (node --check), {len(NON_GLOBALES)} nom(s) surveille(s) "
f"sans reference orpheline.")
return 0
if __name__ == "__main__":
raise SystemExit(main())