Files
fn_registry/python/functions/pipelines/regenerate_app_icons.py
T
egutierrez b9716a7cd6 chore: snapshot WIP previo + flow 0008 + 7 sub-issues (0112-0119)
Snapshot de WIP acumulado de sesiones previas antes de merge wave 1
del flow 0008 (kanban_cpp + agent_runner_api + DoD schema).

Incluye:
- dev/flows/0008-kanban-cpp-and-agent-workflows.md
- dev/issues/0112-0119*.md (7 sub-issues)
- WIP previo en cmd/fn/doctor.go, registry/*, modules/, cpp/, etc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:17:08 +02:00

117 lines
3.6 KiB
Python

"""Regenera el appicon.ico de todas las apps C++ que declaren bloque icon: en su app.md."""
import os
import sys
from pathlib import Path
from typing import Optional
import yaml
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from infra.generate_app_icon import generate_app_icon
def _find_registry_root() -> Path:
env_root = os.environ.get("FN_REGISTRY_ROOT")
if env_root:
return Path(env_root).resolve()
current = Path(__file__).resolve()
for parent in current.parents:
if (parent / "registry.db").exists():
return parent
raise FileNotFoundError("registry.db no encontrado; define FN_REGISTRY_ROOT")
def _read_frontmatter(md_path: Path) -> Optional[dict]:
text = md_path.read_text(encoding="utf-8")
if not text.startswith("---"):
return None
end = text.find("\n---", 3)
if end < 0:
return None
try:
return yaml.safe_load(text[3:end])
except yaml.YAMLError:
return None
def _iter_cpp_app_mds(root: Path):
for pattern in ("apps/*/app.md", "projects/*/apps/*/app.md"):
for md in sorted(root.glob(pattern)):
fm = _read_frontmatter(md)
if not fm or fm.get("lang") != "cpp":
continue
yield md, fm
def regenerate_app_icons(
only: Optional[list[str]] = None,
style: str = "fill_white",
) -> dict:
"""Recorre apps C++ con bloque icon: en su frontmatter y regenera appicon.ico.
Args:
only: Lista opcional de nombres de app a filtrar (campo `name`). Si None,
procesa todas las apps C++ con `icon:` declarado.
style: Estilo del icono. "fill_white" (default, glyph blanco) o
"adaptive_duotone" (glyph duotone con tono claro/oscuro segun
luminancia del accent). Ver generate_app_icon.
Returns:
dict con keys: ok (list[str]), skipped (list[dict]), failed (list[dict]).
"""
root = _find_registry_root()
ok, skipped, failed = [], [], []
for md, fm in _iter_cpp_app_mds(root):
name = fm.get("name", md.parent.name)
if only and name not in only:
continue
icon = fm.get("icon")
if not icon or not isinstance(icon, dict):
skipped.append({"name": name, "reason": "no icon: block"})
continue
phosphor = icon.get("phosphor")
accent = icon.get("accent")
if not phosphor or not accent:
skipped.append({"name": name, "reason": "icon: missing phosphor/accent"})
continue
out_ico = md.parent / "appicon.ico"
try:
generate_app_icon(
phosphor_icon_name=phosphor,
accent_hex=accent,
out_ico_path=str(out_ico),
style=style,
)
ok.append(name)
except Exception as e:
failed.append({"name": name, "error": str(e)})
return {"ok": ok, "skipped": skipped, "failed": failed}
if __name__ == "__main__":
args = sys.argv[1:]
style = "fill_white"
only = []
for a in args:
if a.startswith("--style="):
style = a.split("=", 1)[1]
elif a == "--adaptive":
style = "adaptive_duotone"
elif a == "--white":
style = "white_duotone"
else:
only.append(a)
only = only or None
result = regenerate_app_icons(only=only, style=style)
for name in result["ok"]:
print(f"OK {name}")
for s in result["skipped"]:
print(f"SKIP {s['name']}: {s['reason']}")
for f in result["failed"]:
print(f"FAIL {f['name']}: {f['error']}")
sys.exit(1 if result["failed"] else 0)