Dos cosas: una mejora de calidad y un fallo mío que hay que contar entero.
═══ EL FALLO: escribí un error JSON dentro de 5 recetas y las commiteé rotas ═══
En `a6a9d59`, cinco recetas quedaron con
license = "{"message":"Not Found","documentation_url":"...","status":"404"}"
Causa: cuando el repo da 404 (renombrado, borrado, privado), `gh api` escribe el CUERPO DEL
ERROR en stdout y el filtro `--jq` falla, así que la variable queda valiendo el JSON. Mi
`case` sólo descartaba ""/null/NOASSERTION/other ⇒ el JSON pasó como si fuera una licencia.
Las comillas ROMPEN el TOML, y esas 5 recetas dejaron de parsear y de tener ArtifactHash.
No lo vio nadie leyendo: lo cazó la comparación de hashes, al detectar que `cargo-sort`
había cambiado. Afectadas: cargo-sort, cargo-audit, cargo-binstall, waybackurls, usbutils.
La lección no es «se me pasó un caso», es que **validé los «no sé» conocidos en vez de la
FORMA del dato**. Ahora se acepta sólo lo que tiene forma de expresión SPDX, y la barrera
está en los DOS sitios que escriben (detectar y sembrar), más un GUARDIÁN en el informe que
sale ≠0 si alguna licencia tiene forma imposible. Un fallo aguas arriba disfrazado de dato
hay que verlo sin buscarlo.
Y de paso normalizadas 11 licencias con la barra antigua de Cargo (`MIT/Apache-2.0`), que
no es SPDX válido. Traducir la barra a OR no es inferir: Cargo documentó esa equivalencia.
═══ LA MEJORA: `scripts/licencias-cargo.sh` ═══
Lee la licencia que el AUTOR declara en su Cargo.toml, pidiéndola AL TAG QUE LA RECETA
PINEA (`?ref=v<version>`, con HEAD como último recurso y anotando cuál se usó). 176
obtenidas, 167 al tag exacto. Es la fuente más autoritativa de las que usamos y corrige los
dos defectos de la detección por API de una vez:
· las licencias DOBLES dejan de colapsar: `fd` pasa de `Apache-2.0` a `MIT OR Apache-2.0`,
`ripgrep` a `Unlicense OR MIT`, `bat` a `MIT OR Apache-2.0`;
· los SPDX AMBIGUOS caen de 71 a 55, porque el autor sí escribe -only/-or-later.
66 corregidas, 7 nuevas. Y aparecieron cosas que la detección había perdido: `eza` es
EUPL-1.2, una copyleft europea que se habría empaquetado creyendo otra cosa.
Jerarquía de evidencia, escrita en la cabecera del script: nombre (prohibido) < familia/URL
de fuente < API de licencias de GitHub < Cargo.toml del autor.
997 de 1141 (87%). Verificación COMPLETA sobre las 77 recetas tocadas —no una muestra—:
0 hashes cambiados, y 5 que ahora parsean y en HEAD no.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
93 lines
5.6 KiB
Bash
Executable File
93 lines
5.6 KiB
Bash
Executable File
#!/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)
|
|
# ⚠ VALIDAR LA FORMA, NO SÓLO LOS «NO SÉ» CONOCIDOS. Cuando el repo da 404 (renombrado, borrado,
|
|
# privado), `gh api` escribe el CUERPO DEL ERROR JSON en stdout y el filtro --jq falla ⇒ `lic`
|
|
# queda valiendo `{"message":"Not Found",...}`. Filtrando sólo ""/null/NOASSERTION/other, ese JSON
|
|
# pasaba como si fuera una licencia y se escribía en la receta: comillas incluidas, lo que además
|
|
# ROMPE el TOML y hace que el ArtifactHash deje de calcularse. Pasó de verdad en 5 recetas el
|
|
# 2026-08-07 y lo cazó la verificación de hashes, no la vista.
|
|
# ⇒ Regla: aceptar sólo lo que TIENE FORMA de expresión SPDX (letras, dígitos, . - + y los
|
|
# operadores OR/AND/WITH). Todo lo demás es un fallo disfrazado de dato.
|
|
case "$lic" in
|
|
""|null|NOASSERTION|other) printf "%s\t%s\n" "$n" "$slug" >> "$TMP/sin.tsv" ;;
|
|
*[!A-Za-z0-9.+\ -]*) 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"
|