feat(infra): auto-commit con 56 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 14:22:55 +02:00
parent c1071a82b3
commit 32c7336bf6
56 changed files with 5307 additions and 100 deletions
@@ -0,0 +1,112 @@
---
name: render_glance_config
kind: function
lang: py
domain: infra
version: "1.1.0"
purity: pure
signature: "render_glance_config(services: list[dict], title: str = \"Procesos locales\", host_suffix: str = \"localhost\") -> str"
description: "Transforma una lista de servicios normalizados en el YAML de configuración de Glance (dashboard self-hosted). Genera una página con un widget monitor por categoría que hace health-check de cada servicio y lo pinta verde/rojo. Función pura y determinista. Parte del sistema local_hub."
tags: [local-hub, infra, glance, dashboard, yaml, config]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [pyyaml]
params:
- name: services
desc: "Lista de dicts de servicio normalizados. Cada uno requiere 'subdomain' (si falta, el servicio se ignora sin lanzar) y 'title'; opcional 'category' (default 'General'), 'icon' (se omite del site si está vacío o ausente) y 'health_path' (ruta de salud del servicio: si es distinta de '/', el site emite 'check-url' = url+health_path para que Glance haga el health-check ahí; si es '/' o falta, no se emite check-url)."
- name: title
desc: "Nombre de la página de Glance (campo 'name' de la página). Default 'Procesos locales'."
- name: host_suffix
desc: "Sufijo de host para las URLs de los sites. Default 'localhost' -> 'http://<subdomain>.localhost'."
output: "String con el YAML completo de configuración de Glance (cabecera de comentario + pages/columns/widgets), terminado en '\\n'. Parseable con yaml.safe_load. Cada site lleva 'title' y 'url' (raíz del subdominio); además 'check-url' (url+health_path) cuando el servicio trae un health_path distinto de '/', e 'icon' cuando no está vacío."
tested: true
tests:
- "test_golden_dos_categorias_dos_widgets"
- "test_yaml_parseable_y_estructura"
- "test_icon_omitido_cuando_vacio"
- "test_host_suffix_custom"
- "test_title_es_name_de_pagina"
- "test_servicios_sin_subdomain_se_ignoran"
- "test_determinismo"
- "test_orden_sites_por_title"
- "test_categoria_default_general"
- "test_check_url_se_emite_cuando_health_path_no_es_raiz"
test_file_path: "python/functions/infra/render_glance_config_test.py"
file_path: "python/functions/infra/render_glance_config.py"
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra.render_glance_config import render_glance_config
services = [
{"subdomain": "metabase", "title": "Metabase", "icon": "si:metabase", "category": "Datos"},
{"subdomain": "jupyter", "title": "Jupyter Lab", "icon": "si:jupyter", "category": "Datos"},
{"subdomain": "portainer","title": "Portainer", "icon": "si:portainer", "category": "Infra"},
]
yaml_text = render_glance_config(services, title="Inicio")
print(yaml_text)
# pages:
# - name: Inicio
# columns:
# - size: full
# widgets:
# - type: monitor
# title: Datos
# cache: 1m
# sites:
# - title: Jupyter Lab # ordenado por title dentro de la categoría
# url: http://jupyter.localhost
# icon: si:jupyter
# - title: Metabase
# url: http://metabase.localhost
# icon: si:metabase
# - type: monitor
# title: Infra
# cache: 1m
# sites:
# - title: Portainer
# url: http://portainer.localhost
# icon: si:portainer
```
## Cuando usarla
Cuando necesites regenerar el `glance.yml` del dashboard local a partir de
`apps/local_hub/local_services.yaml`: tras añadir/quitar un servicio local, o
en el pipeline `refresh_local_hub` que corre diario via dag_engine. La salida se
escribe al archivo de config de Glance (el borde impuro: I/O lo hace el caller).
## Notas
- **Decisión `title` -> `name`:** el parámetro `title` se usa como el campo `name`
de la (única) página de Glance. No es un comentario de cabecera ni se ignora.
Default `"Procesos locales"`. Así la firma queda útil sin añadir un parámetro
extra para el nombre de página.
- **Determinismo:** las categorías se ordenan alfabéticamente y los servicios de
cada categoría por `title` (desempate por `subdomain`). Se serializa con
`yaml.safe_dump(sort_keys=False)` sobre estructuras ya ordenadas, por lo que la
misma entrada (en cualquier orden) produce siempre la misma salida byte a byte.
- **Robustez:** los servicios sin `subdomain` se ignoran silenciosamente (no se
lanza). El `icon` se omite del site cuando está vacío o ausente. Cada categoría
produce un widget `type: monitor` con `cache: 1m`; todos los widgets van en una
sola columna `size: full`.
- **Función pura:** sin I/O, sin estado, determinista. El health-check real lo
hace Glance en runtime (GET a `check-url` si existe, si no a `url`); esta
función solo genera el texto.
- **`check-url` vs `url`:** `url` es siempre la raíz del subdominio (lo que abre
el usuario al clicar). `check-url` solo aparece cuando el servicio trae un
`health_path` distinto de `/`, y vale `url + health_path`. Sirve para APIs que
devuelven 404 en `/` pero 200 en su ruta de salud (ej. `/api/health`), de modo
que Glance las pinta verde sin cambiar el enlace navegable.
## Capability growth log
- v1.1.0 (2026-06-20) — añade check-url (health_path) por site para health-check preciso de APIs sin ruta raíz