215 lines
8.7 KiB
Python
215 lines
8.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# -*- coding: utf-8 -*-
|
||
|
|
"""
|
||
|
|
patch_facilitator_c4b.py — Correctif C4 (résolution des assets)
|
||
|
|
================================================================
|
||
|
|
run_render() ne transmettait jamais --assets à render_engine_v2.py,
|
||
|
|
qui retombait sur son défaut <yaml>/../assets. Ce défaut n'est correct
|
||
|
|
que si le YAML est dans projets/<slug>/outputs/ (alors ../assets =
|
||
|
|
projets/<slug>/assets/) — ce qui couvre le flux normal du pipeline,
|
||
|
|
mais PAS un YAML isolé à la racine (golden, test manuel).
|
||
|
|
|
||
|
|
Patch : run_render accepte un paramètre assets_dir optionnel. Résolu
|
||
|
|
dans l'ordre :
|
||
|
|
1. assets_dir explicitement fourni par l'appelant (le pipeline
|
||
|
|
normal le fournira désormais : projets/<slug>/assets/).
|
||
|
|
2. Sinon, si le YAML est sous PROJECTS_DIR, le dossier assets/ du
|
||
|
|
projet correspondant est déduit automatiquement.
|
||
|
|
3. Sinon (golden, test manuel hors projet), fallback sur le défaut
|
||
|
|
du moteur (<yaml>/../assets) — comportement inchangé pour ces cas.
|
||
|
|
|
||
|
|
Tous les appelants internes de run_render (pipeline standard, flux
|
||
|
|
libre, révisions, merge C3, --render CLI) sont mis à jour pour passer
|
||
|
|
proj.root / "assets" quand un Project est en contexte.
|
||
|
|
|
||
|
|
Usage (dossier du pipeline, single-line) :
|
||
|
|
python3 patch_facilitator_c4b.py
|
||
|
|
Compatible C1/C2/C3/C4 déjà appliqués (ancres indépendantes). Vérifie
|
||
|
|
chaque ancre, écrit .bak-c4b, compile, idempotent.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import py_compile
|
||
|
|
import shutil
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
TARGET = Path("facilitator_v9.py")
|
||
|
|
|
||
|
|
PATCHES = [
|
||
|
|
# P1 — signature + résolution + transmission --assets
|
||
|
|
(
|
||
|
|
'def run_render(yaml_path: Path) -> Optional[Path]:\n'
|
||
|
|
' section("ÉTAPE 4 — RENDER ENGINE V2")\n'
|
||
|
|
' if not Path(RENDER_ENGINE_PATH).exists():\n'
|
||
|
|
' warn(f"{RENDER_ENGINE_PATH} introuvable.")\n'
|
||
|
|
' return None\n'
|
||
|
|
' for p in [THEME_PATH, COMPONENTS_PATH, LAYOUTS_PATH]:\n'
|
||
|
|
' if not os.path.exists(p):\n'
|
||
|
|
' warn(f"Config manquante : {p}")\n'
|
||
|
|
' return None\n'
|
||
|
|
' pptx_out = yaml_path.with_suffix(".pptx")\n'
|
||
|
|
' info("Lancement de render_engine_v2.py...")\n'
|
||
|
|
' info(f"Sortie : {pptx_out}")\n'
|
||
|
|
' result = subprocess.run(\n'
|
||
|
|
' [sys.executable, RENDER_ENGINE_PATH, str(yaml_path), '
|
||
|
|
'str(pptx_out),\n'
|
||
|
|
' "--theme", THEME_PATH, "--components", '
|
||
|
|
'COMPONENTS_PATH,\n'
|
||
|
|
' "--layouts", LAYOUTS_PATH],\n'
|
||
|
|
' capture_output=True, text=True)\n',
|
||
|
|
|
||
|
|
'def run_render(yaml_path: Path,\n'
|
||
|
|
' assets_dir: Optional[Path] = None) -> '
|
||
|
|
'Optional[Path]:\n'
|
||
|
|
' section("ÉTAPE 4 — RENDER ENGINE V2")\n'
|
||
|
|
' if not Path(RENDER_ENGINE_PATH).exists():\n'
|
||
|
|
' warn(f"{RENDER_ENGINE_PATH} introuvable.")\n'
|
||
|
|
' return None\n'
|
||
|
|
' for p in [THEME_PATH, COMPONENTS_PATH, LAYOUTS_PATH]:\n'
|
||
|
|
' if not os.path.exists(p):\n'
|
||
|
|
' warn(f"Config manquante : {p}")\n'
|
||
|
|
' return None\n'
|
||
|
|
' pptx_out = yaml_path.with_suffix(".pptx")\n'
|
||
|
|
' info("Lancement de render_engine_v2.py...")\n'
|
||
|
|
' info(f"Sortie : {pptx_out}")\n'
|
||
|
|
' if assets_dir is None:\n'
|
||
|
|
' # Déduction depuis PROJECTS_DIR si le YAML y vit '
|
||
|
|
'(C4b)\n'
|
||
|
|
' try:\n'
|
||
|
|
' rel = yaml_path.resolve().relative_to(\n'
|
||
|
|
' Path(PROJECTS_DIR).resolve())\n'
|
||
|
|
' assets_dir = Path(PROJECTS_DIR) / rel.parts[0] '
|
||
|
|
'/ "assets"\n'
|
||
|
|
' except ValueError:\n'
|
||
|
|
' assets_dir = None # hors projet : défaut du '
|
||
|
|
'moteur\n'
|
||
|
|
' cmd = [sys.executable, RENDER_ENGINE_PATH, str(yaml_path),'
|
||
|
|
'\n'
|
||
|
|
' str(pptx_out),\n'
|
||
|
|
' "--theme", THEME_PATH, "--components", '
|
||
|
|
'COMPONENTS_PATH,\n'
|
||
|
|
' "--layouts", LAYOUTS_PATH]\n'
|
||
|
|
' if assets_dir is not None:\n'
|
||
|
|
' cmd += ["--assets", str(assets_dir)]\n'
|
||
|
|
' info(f"Assets : {assets_dir}")\n'
|
||
|
|
' result = subprocess.run(cmd, capture_output=True, '
|
||
|
|
'text=True)\n',
|
||
|
|
),
|
||
|
|
# P2 — CLI --render : passe le dossier assets du projet si déductible
|
||
|
|
(
|
||
|
|
' if args.render:\n'
|
||
|
|
' yaml_path = Path(args.render)\n'
|
||
|
|
' if not yaml_path.exists():\n'
|
||
|
|
' warn(f"Fichier introuvable : {yaml_path}")\n'
|
||
|
|
' sys.exit(1)\n'
|
||
|
|
' layouts = load_layouts()\n'
|
||
|
|
' is_valid, message, _ = validate_yaml(\n'
|
||
|
|
' yaml_path.read_text(encoding="utf-8"), layouts)\n'
|
||
|
|
' (ok if is_valid else warn)(f"Validation : {message}")\n'
|
||
|
|
' sys.exit(0 if run_render(yaml_path) else 1)\n',
|
||
|
|
|
||
|
|
' if args.render:\n'
|
||
|
|
' yaml_path = Path(args.render)\n'
|
||
|
|
' if not yaml_path.exists():\n'
|
||
|
|
' warn(f"Fichier introuvable : {yaml_path}")\n'
|
||
|
|
' sys.exit(1)\n'
|
||
|
|
' layouts = load_layouts()\n'
|
||
|
|
' is_valid, message, _ = validate_yaml(\n'
|
||
|
|
' yaml_path.read_text(encoding="utf-8"), layouts)\n'
|
||
|
|
' (ok if is_valid else warn)(f"Validation : {message}")\n'
|
||
|
|
' local_assets = yaml_path.parent / "assets"\n'
|
||
|
|
' assets_dir = local_assets if local_assets.is_dir() '
|
||
|
|
'else None\n'
|
||
|
|
' sys.exit(0 if run_render(yaml_path, assets_dir) '
|
||
|
|
'else 1)\n',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
|
||
|
|
# Les 3 appels internes de run_render(yaml_path) — même nom de
|
||
|
|
# variable partout, différenciés par leur contexte immédiat.
|
||
|
|
CALL_PATCHES = [
|
||
|
|
(
|
||
|
|
' info("Rendu du deck COMPLET fusionné.")\n'
|
||
|
|
'\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path)\n',
|
||
|
|
' info("Rendu du deck COMPLET fusionné.")\n'
|
||
|
|
'\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path, proj.root / '
|
||
|
|
'"assets")\n',
|
||
|
|
),
|
||
|
|
(
|
||
|
|
' if yaml_path and yaml_data:\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path)\n',
|
||
|
|
' if yaml_path and yaml_data:\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path, proj.root '
|
||
|
|
'/ "assets")\n',
|
||
|
|
),
|
||
|
|
(
|
||
|
|
' if yaml_path and yaml_data:\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path)\n',
|
||
|
|
' if yaml_path and yaml_data:\n'
|
||
|
|
' manifest.file(yaml_path)\n'
|
||
|
|
' pptx_path = run_render(yaml_path, proj.root / '
|
||
|
|
'"assets")\n',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
|
||
|
|
MARKER = "assets_dir: Optional[Path] = None"
|
||
|
|
|
||
|
|
|
||
|
|
def fail(msg):
|
||
|
|
print(" ! %s" % msg)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
if not TARGET.exists():
|
||
|
|
fail("%s introuvable — lancer depuis le dossier du pipeline."
|
||
|
|
% TARGET)
|
||
|
|
content = TARGET.read_text(encoding="utf-8")
|
||
|
|
if MARKER in content:
|
||
|
|
fail("Déjà patché (assets_dir présent) — rien à faire.")
|
||
|
|
if "def merge_revision" not in content:
|
||
|
|
fail("Prérequis manquant : patch_facilitator_c3.py doit être "
|
||
|
|
"appliqué avant celui-ci (C4b s'ancre sur le message de "
|
||
|
|
"fusion introduit par C3).")
|
||
|
|
|
||
|
|
all_patches = PATCHES + CALL_PATCHES
|
||
|
|
for i, (old, _) in enumerate(all_patches, 1):
|
||
|
|
n = content.count(old)
|
||
|
|
if n == 0:
|
||
|
|
fail("Ancre du patch %d introuvable — facilitator inattendu."
|
||
|
|
" (Le fichier a peut-être déjà été modifié depuis les "
|
||
|
|
"patchs C1/C2/C3.)" % i)
|
||
|
|
if n > 1:
|
||
|
|
fail("Ancre du patch %d non unique (%d occurrences)." % (i, n))
|
||
|
|
|
||
|
|
shutil.copy2(TARGET, str(TARGET) + ".bak-c4b")
|
||
|
|
print(" + Sauvegarde : %s.bak-c4b" % TARGET)
|
||
|
|
|
||
|
|
for old, new in all_patches:
|
||
|
|
content = content.replace(old, new)
|
||
|
|
|
||
|
|
TARGET.write_text(content, encoding="utf-8")
|
||
|
|
print(" + %d patchs appliqués (2 structurels + %d appels internes)."
|
||
|
|
% (len(all_patches), len(CALL_PATCHES)))
|
||
|
|
try:
|
||
|
|
py_compile.compile(str(TARGET), doraise=True)
|
||
|
|
print(" + Compilation OK.")
|
||
|
|
except py_compile.PyCompileError as e:
|
||
|
|
shutil.copy2(str(TARGET) + ".bak-c4b", TARGET)
|
||
|
|
fail("Erreur de compilation — fichier restauré :\n%s" % e)
|
||
|
|
print("\n Pipeline normal : assets résolus automatiquement "
|
||
|
|
"(projets/<slug>/assets/).")
|
||
|
|
print(" --render CLI : assets résolus si un dossier assets/ existe"
|
||
|
|
" à côté du YAML (sinon défaut moteur <yaml>/../assets).")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|