licencias: 936 de 1141 (82%) — detección por evidencia, con sus dos imprecisiones declaradas

Esta mañana eran 0. Tras la tabla curada (228) y este paso, 936. Ninguna receta se
re-hasheó: verificado en 30 de las 708 tocadas, 30 hashes idénticos, 0 cambiados.

DE DÓNDE SALE EL DATO, Y POR QUÉ NO ES ADIVINAR. `scripts/licencias-detectar.sh` consulta
la API /repos/{o}/{r}/license de GitHub para las 673 recetas cuya fuente vive allí. Eso
devuelve el resultado de DETECTAR el fichero LICENSE que el repo tiene de verdad
(licensee), no una etiqueta escrita a mano en una web: es la misma evidencia que veríamos
abriendo el tarball, obtenida sin bajar 673 tarballs por un enlace de 8 Mbps. 610 con SPDX
definido; las 63 que GitHub marca NOASSERTION/other se DESCARTAN — un «no sé» de la fuente
se propaga como hueco, no se redondea a una licencia plausible.

Las familias no-GitHub van curadas por la URL DE FUENTE, que es la evidencia que el nombre
no da. Y ahí cometí el error simétrico al que este mismo fichero advertía: había puesto
`knighttime` entre las «herramientas Go» POR SU NOMBRE, y su tarball sale de
download.kde.org — es un Framework de KDE, LGPL. Descartar por nombre falla igual que
aceptar por nombre. Corregido en la cabecera.

LAS DOS IMPRECISIONES, contables con `licencias.sh --revisar` en vez de escondidas:

 1. SPDX OBSOLETOS Y AMBIGUOS (71 recetas). GitHub devuelve `GPL-3.0`, `LGPL-2.1`,
    `AGPL-3.0`… identificadores que SPDX declaró obsoletos PRECISAMENTE porque no
    distinguen `-only` de `-or-later`, y esa diferencia decide con qué se puede combinar el
    paquete. NO se normalizan a ciegas: mapear GPL-3.0 → GPL-3.0-or-later sería inventar el
    dato que falta. Quedan marcadas para resolver mirando el fuente.

 2. LICENCIAS DOBLES COLAPSADAS. La API devuelve UNA sola licencia y muchos proyectos Rust
    son «MIT OR Apache-2.0» (p.ej. `fd` quedó como Apache-2.0). No es falso —cumplir una de
    las opciones concedidas basta— pero es incompleto. Lo resuelve el cierre estructural:
    leer el campo `license` del Cargo.toml en la fase de fetch.

Y un fallo que habría escrito basura en silencio: `licencias-detectadas.tsv` tiene TRES
columnas (añade el owner/repo consultado, para poder auditar) y el sembrador leía dos, así
que la licencia se habría llevado pegado el slug — `license = "MIT<TAB>owner/repo"`, sin
que nada lo validara. Arreglado antes de sembrar.

Quedan 205 sin licencia y 71 por desambiguar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-07 13:26:22 -04:00
co-authored by Claude Opus 5
parent 6060b9bbcd
commit a6a9d59e4f
712 changed files with 1561 additions and 3 deletions
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# licencias-detectar.sh — deriva la licencia de las recetas cuya fuente vive en GitHub,
# preguntándole a GitHub por el fichero LICENSE REAL del repo. Complemento de `licencias.sh`.
#
# ── POR QUÉ ESTO NO VIOLA LA REGLA DE «NO SE ADIVINA» ───────────────────────────────────────────
# `licencias.sh` prohíbe rellenar a ojo, y con razón: declarar mal una licencia convierte un hueco
# visible en una afirmación falsa. Esto NO es adivinar. La API `/repos/{o}/{r}/license` devuelve el
# resultado de **detectar el fichero LICENSE que el repo tiene de verdad** (GitHub usa licensee), no
# una etiqueta que alguien escribió a mano en una web. Es la misma evidencia que miraríamos nosotros
# abriendo el tarball, obtenida sin descargar 686 tarballs por un enlace de 8 Mbps.
#
# ── ⚠ LA LIMITACIÓN, QUE SE REGISTRA EN VEZ DE ESCONDERSE ───────────────────────────────────────
# GitHub detecta sobre la **rama por defecto**, no sobre el tag/commit que la receta pinea. En la
# enorme mayoría de proyectos es lo mismo, pero NO es una garantía: un proyecto pudo relicenciarse
# después de la versión que empaquetamos. Por eso:
# · la salida va a un fichero APARTE (`docs/licencias-detectadas.tsv`) con procedencia y fecha,
# no directo a las recetas — se puede auditar y rehacer;
# · sólo se aceptan SPDX **definidos**: se descartan `NOASSERTION`, `null` y `other`, que es
# justamente GitHub diciendo «no estoy seguro». Un «no sé» de la fuente se propaga como hueco,
# no se redondea a una licencia plausible.
# Si algún día hace falta exactitud a nivel de tag, el cierre correcto es el estructural: capturar
# el COPYING/LICENSE en la fase de fetch, que ya descarga y extrae el tarball pineado.
#
# Uso: scripts/licencias-detectar.sh (escribe docs/licencias-detectadas.tsv)
# luego: TABLA=docs/licencias-detectadas.tsv scripts/licencias.sh --sembrar
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
SALIDA="${SALIDA:-docs/licencias-detectadas.tsv}"
JOBS="${JOBS:-8}"
command -v gh >/dev/null || { echo "!! falta el CLI 'gh'"; exit 1; }
gh auth status >/dev/null 2>&1 || { echo "!! 'gh' no está autenticado"; exit 1; }
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
# 1. Las que NO tienen licencia y cuya fuente es github.com → «nombre<TAB>owner/repo»
./scripts/licencias.sh --faltan 2>/dev/null | while IFS= read -r n; do
f=$(ls "recipes/$n.toml" 2>/dev/null | head -1)
[ -z "$f" ] && f=$(ls recipes/incoming-*/"$n.toml" 2>/dev/null | head -1)
[ -z "$f" ] && continue
u=$(grep -m1 -E '^[[:space:]]*(tarball|repo)[[:space:]]*=' "$f" 2>/dev/null)
case "$u" in *github.com*) ;; *) continue ;; esac
# Sirve para https://github.com/o/r/... y para git@github.com:o/r.git
slug=$(printf '%s\n' "$u" | sed -E 's#.*github\.com[:/]+([^/"]+)/([^/"]+).*#\1/\2#; s/\.git$//')
case "$slug" in */*) printf '%s\t%s\n' "$n" "$slug" ;; esac
done | sort -u > "$TMP/objetivos.tsv"
TOT=$(wc -l < "$TMP/objetivos.tsv")
echo "==> a consultar: $TOT recetas con fuente en GitHub"
# 2. Preguntar a GitHub. Sólo SPDX definidos.
export TMP
xargs -P "$JOBS" -I{} sh -c '
n=$(printf "%s" "{}" | cut -f1); slug=$(printf "%s" "{}" | cut -f2)
lic=$(gh api "repos/$slug/license" --jq ".license.spdx_id" 2>/dev/null)
case "$lic" in
""|null|NOASSERTION|other) printf "%s\t%s\n" "$n" "$slug" >> "$TMP/sin.tsv" ;;
*) printf "%s\t%s\t%s\n" "$n" "$lic" "$slug" >> "$TMP/con.tsv" ;;
esac
' < "$TMP/objetivos.tsv"
CON=$(wc -l < "$TMP/con.tsv" 2>/dev/null || echo 0)
SIN=$(wc -l < "$TMP/sin.tsv" 2>/dev/null || echo 0)
{
echo "# licencias-detectadas.tsv — GENERADO por scripts/licencias-detectar.sh. No editar a mano."
echo "#"
echo "# Procedencia: API de GitHub /repos/{owner}/{repo}/license, que detecta el fichero LICENSE"
echo "# REAL del repositorio (licensee). Es evidencia, no una etiqueta escrita a mano."
echo "#"
echo "# ⚠ LIMITACIÓN REGISTRADA: la detección es sobre la RAMA POR DEFECTO, no sobre el tag que la"
echo "# receta pinea. Casi siempre coinciden, pero un proyecto pudo relicenciarse después de la"
echo "# versión que empaquetamos. Los SPDX indefinidos (NOASSERTION/other/vacío) se DESCARTAN: un"
echo "# «no sé» de la fuente se propaga como hueco, no se redondea a una licencia plausible."
echo "#"
echo "# Formato: <receta><TAB><SPDX><TAB><owner/repo consultado>"
echo "# Consultadas $TOT · con SPDX definido $CON · sin SPDX utilizable $SIN"
sort "$TMP/con.tsv" 2>/dev/null
} > "$SALIDA"
echo "==> con SPDX definido: $CON · descartadas por indefinido: $SIN"
echo "==> escrito: $SALIDA"
echo "==> sembrar con: TABLA=$SALIDA ./scripts/licencias.sh --sembrar"
+34 -1
View File
@@ -64,7 +64,11 @@ case "$MODO" in
--sembrar)
[ -s "$TABLA" ] || { echo "!! falta la tabla curada: $TABLA"; exit 1; }
puestas=0; ya=0; sin_receta=0
while IFS=$'\t' read -r pkg lic; do
# TRES campos, no dos: `licencias-detectadas.tsv` añade una tercera columna con el `owner/repo`
# consultado (la procedencia, para poder auditar). Leyendo sólo dos variables, `lic` se tragaría
# también el slug y se escribiría `license = "MIT<TAB>owner/repo"` en la receta — basura silenciosa,
# porque nada valida el contenido. `_resto` la absorbe.
while IFS=$'\t' read -r pkg lic _resto; do
case "$pkg" in ''|'#'*) continue ;; esac
[ -n "${lic:-}" ] || continue
hallada=0
@@ -96,6 +100,35 @@ case "$MODO" in
for f in $(recetas); do tiene_licencia "$f" || basename "$f" .toml; done
;;
# ── revisar: las que están declaradas pero con un SPDX AMBIGUO ─────────────────────────────────
# La detección por API de GitHub deja dos imprecisiones conocidas, y esto las hace contables en vez
# de esconderlas:
#
# 1. SPDX OBSOLETOS Y AMBIGUOS. GitHub devuelve `GPL-3.0`, `GPL-2.0`, `LGPL-2.1`, `LGPL-3.0`,
# `AGPL-3.0` — identificadores que SPDX declaró obsoletos **precisamente porque son ambiguos**:
# no distinguen «sólo esta versión» (`-only`) de «esta o posterior» (`-or-later`), y esa
# diferencia tiene efectos legales reales sobre con qué se puede combinar el paquete.
# **NO se normalizan a ciegas**: mapear `GPL-3.0` → `GPL-3.0-or-later` sería inventar el dato que
# falta, que es justo lo que este fichero prohíbe. Se marcan para que un humano las resuelva
# mirando la cabecera del fuente.
#
# 2. LICENCIAS DOBLES COLAPSADAS. La API devuelve UNA licencia; muchos proyectos Rust son
# «MIT OR Apache-2.0» y sólo se recoge una de las dos (p.ej. `fd` quedó como Apache-2.0). No es
# falso —Apache-2.0 sí es una de las opciones concedidas, y cumplir una basta— pero es
# INCOMPLETO: pierde una opción que el autor otorgó. Se resuelve con el cierre estructural
# (leer el `license` del Cargo.toml en la fase de fetch), no a mano.
--revisar)
n=0
for f in $(recetas); do
l=$(licencia_de "$f")
case "$l" in
GPL-[0-9]*|LGPL-[0-9]*|AGPL-[0-9]*)
case "$l" in *-only|*-or-later|*OR*|*AND*|*WITH*) ;; *) echo "$(basename "$f" .toml) $l"; n=$((n+1));; esac ;;
esac
done
echo "==> $n recetas con SPDX ambiguo (falta -only / -or-later). Resolver mirando el fuente." >&2
;;
# ── informe (default) ───────────────────────────────────────────────────────────────────────────
*)
tot=0; con=0