Volver a la wiki

Tool Dependencias — arquitectura, operación y troubleshooting

Para qué sirve esta página

Documenta cómo está construida y cómo se opera la tool Dependencias (/tools/deps): el chequeo de frescura de dependencias de CreaRack-Pro y CreaRackSL-workspace. Versión para usuario (qué hace, cómo leerla): [[feature—workspace—tool-dependencias]].

La tool no sustituye a Dependabot ni a pip-audit. Es un mirador unificado de frescura + seguridad (¿hay versión nueva, de qué tamaño es el salto, y tiene la versión en uso alguna vulnerabilidad conocida?) de los dos repos a la vez, en el sitio donde el equipo ya mira. Desde el 11-07-2026 el eje de CVEs lo cubre la propia tool vía OSV.dev (antes solo pip-audit sobre el lockfile del Local Agent).

Arquitectura (3 piezas)

GitHub Actions (CreaRack-Pro)            Cloudflare (Workspace)
┌─────────────────────────────┐         ┌──────────────────────────────┐
│ deps-freshness.yml (cron L)  │  POST   │ /api/tools/deps  (Pages Fn)   │
│  └─ scripts/deps_freshness.py│ ──────► │  └─ D1: deps_freshness        │
│     + deps_specs.py          │ Bearer  │     (DELETE + INSERT batch)   │
│     + deps_sources.py        │         └──────────────┬───────────────┘
│     + deps_holds.py          │                        │ GET
│     • parsea manifiestos     │         ┌──────────────▼───────────────┐
│     • resuelve horquillas    │         │ /tools/deps (Astro + React)   │
│     • aplica retenciones     │         │  DepsPanel.tsx → tabla        │
│     • "instalada": PROD →    │         └───────────────────────────────┘
│        runner → lockfile     │
│     • consulta PyPI / npm    │
│     • calcula desfase semver │
│     • cruza con OSV.dev (CVE)│
└─────────────────────────────┘
        │ GET + Bearer (INTERNAL_TOOLS_TOKEN)
        ▼
 https://crearack.com/api/installed-packages   (Django · PROD)

Es el mismo patrón que ai-eval / dashboard: colector en CI → endpoint Pages Function → D1 → React island en Astro.

Nota (28-08-2026): los campos hold_* (ver «Retenciones deliberadas» abajo) ya viajan en el POST /api/tools/deps, tienen columna D1 dedicada (migración 0053) y se pintan en la UI — badge ámbar, tarjeta y pestaña propias. El desplegable «¿Cómo leo esta tabla?» vive ahora en DepsExplainer.tsx, separado de DepsPanel.tsx (Regla 5: el panel queda en 464 líneas).

Archivos

RepoArchivoRol
CreaRack-Proscripts/deps_freshness.pyColector · CLI (cross-platform, stdlib pura)
CreaRack-Proscripts/deps_specs.pyResolución de horquillas: PEP 440 (PyPI) y caret, tilde, rangos y alternativas (npm) → mayor versión publicada que el spec admite; además has_upper_bound y major_key
CreaRack-Proscripts/deps_sources.pyDe dónde salen los datos: dataclass Dep, parsers de requirements.txt / package.json y lectores de “instalada” (pip list, package-lock.json, pnpm list)
CreaRack-Proscripts/deps_holds.pyRetenciones deliberadas: carga y valida deps_holds.json, marca las filas retenidas a propósito
CreaRack-Prodeps_holds.jsonCatálogo de retenciones vigentes (raíz del repo) — no genera build, se lee en runtime del colector
CreaRack-Protests/scripts/test_deps_freshness.pyCasos sin red: resolución de horquillas, sin-techo, fix_within_spec, dedup, sonda a PROD, retenciones
CreaRack-Proconfig/urls.py → GET /api/installed-packagesEndpoint interno (Bearer INTERNAL_TOOLS_TOKEN) que devuelve las versiones Python REALES del contenedor de PROD
CreaRack-Pro.github/workflows/deps-freshness.ymlCron semanal + dispatch + push a manifiestos, a deps_holds.json o a alguno de los 4 scripts/deps_*.py
Workspacemigrations/0042_create_deps_freshness.sqlTabla D1 deps_freshness
Workspacemigrations/0048_deps_freshness_vulns.sqlColumnas del eje de seguridad (vuln_count, vuln_ids, vuln_severity)
Workspacemigrations/0052_deps_freshness_motor_fiable.sqlSeis columnas del motor fiable (manifests, resolved, installed_source, unbounded, unbounded_major, fix_within_spec)
Workspacemigrations/0053_deps_freshness_holds.sqlCuatro columnas de retenciones (hold_reason, hold_since, hold_ref, hold_until)
Workspacefunctions/api/tools/deps/index.tsAPI: GET (lee) / POST (reemplaza snapshot)
Workspacesrc/pages/tools/deps/index.astroPágina
Workspacesrc/components/tools/DepsPanel.tsxTabla + filtros + tarjetas de resumen
Workspacesrc/components/tools/DepsBadges.tsxPiezas de fila: LagCell (atraso o «Retenida a propósito»), InstalledCell (versión en uso + de dónde sale), UnboundedBadge (sin techo), FixScopeBadge (basta redeploy / cambiar pin), ManifestsNote (en N manifiestos)
Workspacesrc/components/tools/DepsExplainer.tsxDesplegable «¿Cómo leo esta tabla?», extraído de DepsPanel.tsx (Regla 5)
Workspacesrc/components/tools/DepInfoModal.tsxFicha de paquete (modal): registro en vivo + catálogo + bloque «Por qué no se actualiza» si está retenida
Workspacesrc/components/tools/depsShared.tsTipos y etiquetas compartidos (panel ↔ modal), incluidas isHeld/holdTitle
Workspacesrc/components/tools/depsCatalog.tsCatálogo curado: descripción en español de cada paquete
Workspacesrc/components/tools/ToolsHub.tsxCard en el hub /tools
Workspacesrc/styles/tools.cssEstilos .deps-*

Retenciones deliberadas (deps_holds.json, desde v1.86.10)

Una dependencia parada A PROPÓSITO (redis en 7.4.1 porque la serie 8.x corta el channel layer en vivo, Django <6.1 porque django-prometheus lo exige, ruff/mypy con pin exacto hasta auditar sus reglas nuevas) no es lo mismo que una dependencia olvidada — pero hasta este cambio salía en rojo igual que cualquier atrasada.

deps_holds.json (raíz de CreaRack-Pro) declara el catálogo. Cada entrada: package, ecosystem (pypi|npm), repo (crearack-pro|workspace), reason (llano), since (YYYY-MM-DD), ref (task/issue/PR) y opcionalmente until (condición o fecha para revisarla). scripts/deps_holds.py la carga (load), la valida (campos obligatorios, ecosystem/repo en catálogo, since con formato de fecha, sin duplicados) y la cruza contra las filas del colector (apply), rellenando hold_reason/hold_since/hold_ref/hold_until en el Dep que casa por (repo, ecosystem, paquete normalizado).

Retenciones vigentes (28-08-2026), todas task #275: redis 7.4.1 (task #204 · redis-py #2807 abierta), Django <6.1 (django-prometheus 2.5.0), ruff y mypy (pin exacto hasta septiembre 2026, con Dani y Txell).

En la UI (desde 28-08-2026): una fila retenida ya no se pinta en rojo/naranja — el distintivo de atraso pasa a ámbar apagado con el texto «Retenida a propósito» (DepsBadges.tsx → LagCell), y el tooltip trae el motivo más el «desde», la referencia y el «revisar» (holdTitle() en depsShared.ts). Los contadores «Muy atrasadas»/«Atrasadas», la frase-resumen de arriba y la cifra de la tarjeta de Dependencias en /tools excluyen las retenidas: tienen su propia tarjeta «Retenidas a propósito» (toggle-filtro) y su propia pestaña Retenidas junto a «Pendientes»/«Todas». La ficha del paquete (DepInfoModal.tsx) repite el mismo detalle, más ordenado, en el bloque «Por qué no se actualiza».

De dónde sale cada dato

La ficha de paquete (modal, desde 11-07-2026)

Al pinchar el nombre de un paquete, DepInfoModal.tsx abre una ficha con dos fuentes:

Si el paquete está retenido, la ficha añade el bloque «Por qué no se actualiza» con hold_reason/hold_since/hold_ref/hold_until.

Las tarjetas del resumen son además filtros toggle de la lista (estado cardFilter, que tiene prioridad sobre el seg «Pendientes/Todas/Retenidas»); la tarjeta «Total vigiladas» limpia filtros.

Operación

Lanzar a mano (forzar comprobación)

gh workflow run deps-freshness.yml -R CreaRackSL/CreaRack-Pro

Añadir o quitar una retención

Editar deps_holds.json (raíz de CreaRack-Pro) y añadir/borrar la entrada correspondiente — no requiere tocar código ni migraciones. Borrar la entrada en cuanto se suba la dependencia (el propio fichero lo recuerda en su campo _doc).

Probar en local (sin publicar)

python scripts/deps_freshness.py --dry-run

Imprime la tabla por consola (con las columnas RESUELTA, la fuente de la instalada y las notas «sin techo» / «en N manifiestos» / «retenida»). Frontend y Local Agent traen “instalada”; backend y workspace saldrán ”—” salvo que pases --backend-installed pip-list.json / --workspace-installed pnpm-list.json. Para ver la instalada REAL de producción:

python scripts/deps_freshness.py --dry-run `
  --prod-versions-url https://crearack.com/api/installed-packages `
  --prod-token $env:INTERNAL_TOOLS_TOKEN

Secrets (repo CreaRack-Pro → Settings → Secrets → Actions)

SecretUso¿Obligatorio?
MCP_TOKENBearer del Workspace (mismo que push-drift.yml)Sí
CF_ACCESS_CLIENT_ID / ..._SECRETService token CF AccessOpcional
WORKSPACE_REPO_TOKENPAT read-only a CreaRackSL-workspaceOpcional*
INTERNAL_TOOLS_TOKENBearer de GET https://crearack.com/api/installed-packages — mismo valor que el env var de Dokploy en PRODOpcional**

*Sin WORKSPACE_REPO_TOKEN el run cubre solo CreaRack-Pro (el checkout del Workspace se salta sin romper el job). Para cubrir también el Workspace hay que añadir el PAT. Ver [[runbook—platform-credentials-map]].

**Sin INTERNAL_TOOLS_TOKEN la columna «En uso» del backend sale de lo que resuelve el runner del CI (installed_source = runner), no de lo que corre en producción. El workflow tiene un paso previo Probe PROD installed packages (continue-on-error) que hace el GET y deja el código HTTP en el log: 200 = la instalada vendrá de PROD. El endpoint de Django exige un token de ≥32 caracteres; por debajo de eso deniega todo, aunque el valor coincida.

Estado 28-08-2026: el secret está pendiente de crear en el repo CreaRack-Pro. Hasta entonces las pasadas usan el respaldo del runner. Dueño: Edu.

Migración D1

wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/0042_create_deps_freshness.sql
wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/0048_deps_freshness_vulns.sql
wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/0052_deps_freshness_motor_fiable.sql
wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/0053_deps_freshness_holds.sql

(El POST /api/tools/deps falla hasta que la tabla existe; el GET devolverá vacío y la tool mostrará “Sin datos todavía”.)

La 0052 (aplicada el 28-08-2026) añade las seis columnas del motor fiable. Es un ALTER TABLE ADD COLUMN por campo, y va antes de que corra el colector nuevo: sin ellas el INSERT del POST revienta. installed_source no lleva CHECK a propósito — D1 no lo valida de forma fiable en un ADD COLUMN y el enum ya se filtra en el handler.

La 0053 (aplicada el 28-08-2026) añade las cuatro columnas de retenciones — hold_reason, hold_since, hold_ref, hold_until, todas TEXT nulables. Mismo patrón: ALTER TABLE ADD COLUMN no idempotente, una sola pasada, antes de que el colector nuevo publique. Con esto, los campos hold_* dejan de depender solo del JSON del POST y tienen columna propia en deps_freshness.

Contrato del endpoint

Troubleshooting

Extensiones previstas


Véase también

Subir