Volver a la wiki

Tabla D1 deps_freshness — snapshot de frescura de dependencias

Propósito

Tabla de datos centralizada que almacena el estado actual (snapshot) de frescura de las dependencias de CreaRack-Pro y CreaRackSL-workspace: versión pineada, instalada y última disponible en PyPI/npm, el desfase semver, los avisos de seguridad OSV.dev y — desde el “motor fiable” (task #275) — de dónde sale realmente la versión “en uso”, si la horquilla del manifiesto pone techo, y si la fila está retenida a propósito.

No es histórica: cada POST /api/tools/deps borra el snapshot anterior y publica uno nuevo. Sirve para responder “¿qué hay viejo hoy, y es de fiar el dato?” en la tool /tools/deps.

Migraciones

MigraciónFechaQué añade
0042_create_deps_freshness.sql2026-05-28Tabla base: repo, ecosystem, manifest, package, pinned, installed, latest, lag_kind, is_dev, checked_at.
0048_deps_freshness_vulns.sql2026-07-11Eje de seguridad OSV.dev: vuln_count, vuln_ids, vuln_severity.
0052_deps_freshness_motor_fiable.sql2026-08-28Motor fiable (task #275): manifests, resolved, installed_source, unbounded, unbounded_major, fix_within_spec.
0053_deps_freshness_holds.sql2026-08-28Retenciones deliberadas (task #275): hold_reason, hold_since, hold_ref, hold_until.
wrangler d1 execute crearacksl-workspace-db --remote --file=./migrations/<archivo>.sql

Schema (estado actual, tras 0042 + 0048 + 0052 + 0053)

CREATE TABLE deps_freshness (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    repo TEXT NOT NULL,                             -- 'crearack-pro' | 'workspace'
    ecosystem TEXT NOT NULL,                        -- 'pypi' | 'npm'
    manifest TEXT NOT NULL,                         -- manifiesto ganador tras deduplicar (el más restrictivo)
    package TEXT NOT NULL,
    pinned TEXT,
    installed TEXT,
    latest TEXT,
    lag_kind TEXT NOT NULL DEFAULT 'unknown' CHECK (lag_kind IN (
        'major', 'minor', 'patch', 'uptodate', 'unknown'
    )),
    is_dev INTEGER NOT NULL DEFAULT 0,
    checked_at TEXT NOT NULL DEFAULT (datetime('now')),
    -- 0048 — eje de seguridad OSV.dev
    vuln_count INTEGER NOT NULL DEFAULT 0,
    vuln_ids TEXT,                                  -- ids OSV separados por coma (GHSA-..., PYSEC-...)
    vuln_severity TEXT,                             -- peor severidad: critical|high|moderate|low|unknown
    -- 0052 — motor fiable (task #275)
    manifests TEXT,                                 -- TODOS los manifiestos donde aparece el paquete, separados por coma
    resolved TEXT,                                  -- mayor versión que la horquilla `pinned` admite hoy
    installed_source TEXT,                          -- 'prod' | 'runner' | 'lockfile' — de dónde sale `installed`
    unbounded INTEGER NOT NULL DEFAULT 0,            -- 1 = la horquilla no pone techo superior
    unbounded_major INTEGER NOT NULL DEFAULT 0,      -- 1 = sin techo Y ya corre en major distinto al declarado
    fix_within_spec INTEGER,                         -- tri-estado: 1 basta redeploy · 0 cambiar pin · NULL sin fix publicado
    -- 0053 — retenciones deliberadas (task #275)
    hold_reason TEXT,                                -- por qué NO se actualiza, en llano. No nulo = fila retenida
    hold_since TEXT,                                 -- desde cuándo (YYYY-MM-DD; el handler descarta otro formato)
    hold_ref TEXT,                                   -- dónde vive la decisión ("task #204 · redis-py #2807")
    hold_until TEXT                                  -- cuándo revisarla (fecha o condición, texto libre)
);

CREATE INDEX idx_deps_freshness_lag ON deps_freshness(lag_kind);
CREATE INDEX idx_deps_freshness_repo ON deps_freshness(repo, ecosystem);

installed_source no lleva CHECK (SQLite/D1 no lo valida de forma fiable en un ADD COLUMN): el enum 'prod' | 'runner' | 'lockfile' se filtra en el handler (functions/api/tools/deps/index.ts), no en el schema.

Columnas detalladas

NombreTipoNulabilidadDescripción
idINTEGERNOPrimary key autoincrementado.
repoTEXTNO'crearack-pro' | 'workspace'.
ecosystemTEXTNO'pypi' | 'npm'.
manifestTEXTNOManifiesto ganador (el más restrictivo) tras deduplicar entre los de manifests.
packageTEXTNONombre del paquete.
pinnedTEXTSÍEspecificación pineada tal como está en el manifiesto ('==6.0.4', '^9.3.0'…).
installedTEXTSÍVersión resuelta/instalada — ver installed_source para saber de dónde sale.
latestTEXTSÍÚltima disponible en PyPI/npm.
lag_kindTEXTNO'major' | 'minor' | 'patch' | 'uptodate' | 'unknown'. Default 'unknown'.
is_devINTEGERNO1 = devDependency/herramienta de dev.
checked_atTEXTNOTimestamp ISO 8601 del snapshot.
vuln_countINTEGERNONº de avisos OSV.dev abiertos para la versión installed. Default 0.
vuln_idsTEXTSÍIds OSV separados por coma (GHSA-..., PYSEC-...).
vuln_severityTEXTSÍPeor severidad entre los avisos: critical|high|moderate|low|unknown.
manifestsTEXTSÍTodos los manifiestos donde aparece el paquete, separados por coma (puede haber más de uno con horquillas distintas).
resolvedTEXTSÍMayor versión publicada que la horquilla de pinned admite — la que se instalaría hoy con un despliegue limpio.
installed_sourceTEXTSÍ'prod' (contenedor de producción vía [[entity—api—endpoint—installed-packages]], fuente de verdad) | 'runner' (resuelto por el runner de CI) | 'lockfile'.
unboundedINTEGERNO1 = la horquilla no pone techo superior (>=X, ^X en npm…). Default 0.
unbounded_majorINTEGERNO1 = sin techo y ya corre en un major distinto al declarado (bajo 0.x el minor cuenta como major). Default 0.
fix_within_specINTEGERSÍ (tri-estado)Solo con avisos de seguridad: 1 = hay versión con fix dentro de la horquilla (basta redeploy) · 0 = hay que cambiar el pin · NULL = OSV no publica versión con fix.
hold_reasonTEXTSÍPor qué la fila está parada A PROPÓSITO, en llano (1-2 frases). No nulo = fila retenida. Declarado en deps_holds.json (CreaRack-Pro).
hold_sinceTEXTSÍDesde cuándo está retenida, YYYY-MM-DD. El handler (functions/api/tools/deps/index.ts) descarta cualquier otro formato → NULL.
hold_refTEXTSÍDónde vive la decisión: tarea, issue del upstream, PR ("task #204 · redis-py #2807").
hold_untilTEXTSÍCuándo toca revisarla: fecha o condición en texto libre ("cuando salga redis-py 6").

Índices

Semántica

pinned vs. resolved vs. installed vs. latest

Cuando resolved e installed difieren, la UI muestra la línea “Se instalaría” en la ficha del paquete.

installed_source

Antes del motor fiable, “instalada” era siempre lo que resolvía el runner de CI (un entorno limpio, sin relación con lo que corre en PROD). Ahora el colector intenta primero GET /api/installed-packages en PROD ([[entity—api—endpoint—installed-packages]]); si no responde, cae a runner; si tampoco, a lockfile.

hold_reason y el resto de hold_*

Una fila con hold_reason no nulo está retenida a propósito: alguien decidió no subirla y dejó escrito el motivo. scripts/deps_holds.py (CreaRack-Pro) cruza el catálogo deps_holds.json contra las filas del colector y rellena estos cuatro campos antes del POST. El desfase (lag_kind) se sigue calculando igual, pero la fila no suma en los contadores major/minor del resumen ni en la cifra roja de la tarjeta de /tools — cuenta aparte como «retenida a propósito». DepsBadges.tsx la pinta en ámbar en vez de rojo/naranja.

Consumidores

Ciclo de vida

  1. Creación: migración 0042 (2026-05-28).
  2. Ampliaciones: 0048 (2026-07-11, seguridad OSV), 0052 (2026-08-28, motor fiable) y 0053 (2026-08-28, retenciones) — todas ALTER TABLE ADD COLUMN, sin migrar filas existentes (payloads viejos entran con las columnas nuevas a NULL/0).
  3. Actualización: POST /api/tools/deps (semanal vía GitHub Actions, o a mano). DELETE → INSERT batch, snapshot atómico sin histórico.
  4. Lectura: GET /api/tools/deps (navegador, miles de veces).

Tamaño estimado


Véase también

Subir