Files
promptkey/scripts/check_pack.py
T
Spark 258287b2ea
ci / verifier-pack (push) Successful in 1m29s
feat(pack,docs): pack de démarrage 20 prompts, README, tests, CI, build
- packs/essentiels : 20 prompts en 4 catégories (Général, Écriture, Code,
  Analyse), en-têtes conformes au parseur, variables déclarées = utilisées
- tests/run_tests.ahk : suites Frontmatter, Variables, JSON, Recherche,
  encodage UTF-8 (à exécuter sous Windows avec AutoHotkey v2)
- scripts/check_pack.py + .gitea/workflows/ci.yml : cohérence du pack
- build.ps1 : tests puis compilation Ahk2Exe et zip portable
- docs : document d'architecture et de conception v1.0
2026-10-11 10:17:30 -04:00

112 lines
4.2 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Contrôle de cohérence du projet PromptKey (utilisé par la CI Gitea Actions).
Vérifie :
1. la présence des fichiers attendus à la racine du dépôt ;
2. le pack de démarrage : 20 prompts, 5 par catégorie (Général, Écriture,
Code, Analyse) sous packs/essentiels/ ;
3. chaque prompt : en-tête délimité par ---, contenant au minimum `id`
(GUID valide et unique) et `title` ;
4. les noms de fichiers : minuscules, tirets, sans accents ;
5. toute variable {{...}} du corps est soit automatique (selection,
clipboard, date, datetime, time, app, cursor), soit déclarée dans le
bloc `variables:` de l'en-tête.
Usage : python3 scripts/check_pack.py [racine_du_depot]
Code de sortie : 0 si tout est conforme, 1 sinon (liste des erreurs).
"""
import re
import sys
import uuid
from pathlib import Path
RACINE = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path(__file__).resolve().parent.parent
FICHIERS_ATTENDUS = ["README.md", "CHANGELOG.md", "LICENSE", ".gitignore", "build.ps1"]
CATEGORIES = {"Général": 5, "Écriture": 5, "Code": 5, "Analyse": 5}
VARIABLES_AUTO = {"selection", "clipboard", "date", "datetime", "time", "app", "cursor"}
MOTIF_NOM_FICHIER = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*\.md$")
MOTIF_VARIABLE = re.compile(r"\{\{\s*([A-Za-z0-9_]+)\s*\}\}")
erreurs = []
def erreur(message):
erreurs.append(message)
print(f"ERREUR : {message}")
# --- 1. Fichiers attendus ---------------------------------------------------
for nom in FICHIERS_ATTENDUS:
if not (RACINE / nom).is_file():
erreur(f"fichier attendu manquant : {nom}")
# --- 2 à 5. Pack de démarrage ------------------------------------------------
pack = RACINE / "packs" / "essentiels"
if not pack.is_dir():
erreur(f"dossier du pack introuvable : {pack}")
else:
ids_vus = {}
total = 0
for categorie, attendu in CATEGORIES.items():
dossier = pack / categorie
fichiers = sorted(dossier.glob("*.md")) if dossier.is_dir() else []
if len(fichiers) != attendu:
erreur(f"catégorie {categorie} : {len(fichiers)} prompt(s), {attendu} attendus")
for fichier in fichiers:
total += 1
rel = fichier.relative_to(RACINE)
if not MOTIF_NOM_FICHIER.match(fichier.name):
erreur(f"{rel} : nom de fichier non conforme (minuscules, tirets, sans accents)")
texte = fichier.read_text(encoding="utf-8")
lignes = texte.splitlines()
if not lignes or lignes[0].strip() != "---" or "---" not in lignes[1:]:
erreur(f"{rel} : en-tête --- absent ou non fermé")
continue
fin = lignes[1:].index("---") + 1
entete = lignes[1:fin]
corps = "\n".join(lignes[fin + 1:])
champs = {}
declarees = set()
for ligne in entete:
m = re.match(r"^([a-z_]+):\s*(.*)$", ligne)
if m:
champs[m.group(1)] = m.group(2).strip()
m2 = re.match(r"^\s*- name:\s*(\S+)\s*$", ligne)
if m2:
declarees.add(m2.group(1).lower())
for champ in ("id", "title"):
if not champs.get(champ):
erreur(f"{rel} : champ obligatoire absent dans l'en-tête : {champ}")
guid = champs.get("id", "")
try:
uuid.UUID(guid)
except (ValueError, AttributeError):
erreur(f"{rel} : id n'est pas un GUID valide : {guid!r}")
if guid in ids_vus:
erreur(f"{rel} : id dupliqué (déjà dans {ids_vus[guid]})")
ids_vus[guid] = str(rel)
for nom_var in MOTIF_VARIABLE.findall(corps):
if nom_var.lower() not in VARIABLES_AUTO and nom_var.lower() not in declarees:
erreur(f"{rel} : variable {{{{{nom_var}}}}} utilisée mais non déclarée")
if total != 20:
erreur(f"pack : {total} prompt(s) au total, 20 attendus")
else:
print(f"Pack : {total} prompts, {len(ids_vus)} id uniques.")
if erreurs:
print(f"\nÉCHEC : {len(erreurs)} erreur(s).")
sys.exit(1)
print("OK : fichiers attendus présents, pack cohérent.")