sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
licencias: resolver -only vs -or-later CON LA CITA — y tres «pruebas» que no probaban nada
1#!/usr/bin/env bash
2# licencias-desambiguar.sh — resuelve `-only` vs `-or-later` en los SPDX obsoletos, CON LA CITA.
3#
4# ── EL PROBLEMA QUE CIERRA ─────────────────────────────────────────────────────────────────────
5# La API de licencias de GitHub devuelve identificadores obsoletos (`GPL-2.0`, `GPL-3.0`, `LGPL-2.1`,
6# `AGPL-3.0`). SPDX los declaró obsoletos **porque son ambiguos**: no dicen si el proyecto concede
7# «sólo esta versión» o «ésta o cualquier posterior». La diferencia no es cosmética — decide con qué
8# se puede combinar el paquete y bajo qué términos puede redistribuirlo quien lo reciba.
9#
10# ── POR QUÉ NO SE PUEDE MIRAR EL COPYING (la trampa) ───────────────────────────────────────────
11# El fichero COPYING de la GPL es **idéntico** en los dos casos: es el texto de la licencia, no la
12# concesión. Y encima su apéndice «cómo aplicar la licencia a tus programas» contiene literalmente la
13# frase «or (at your option) any later version» ⇒ buscarla en el COPYING da SIEMPRE positivo y
14# parecería evidencia siendo texto de plantilla. Es la misma trampa que la regla del `.a` no-PIC,
15# donde `grep R_X86_64_32` contaba reubicaciones de `.debug_*` y mentía.
16#
17# La concesión vive en las **cabeceras de los fuentes** y en el README. Por eso acá se busca la frase
18# en TODO el repo y se **descartan explícitamente** COPYING/LICENSE/LICENCE: si sólo aparece ahí, no
19# hay evidencia y el paquete se deja para el humano.
20#
21# ── SE GUARDA LA CITA, NO SÓLO EL VEREDICTO ────────────────────────────────────────────────────
22# Cada línea de salida lleva el fichero y la frase exacta que decidió. Una licencia es una afirmación
23# legal: quien la revise tiene que poder ver POR QUÉ dice lo que dice sin repetir el trabajo. Un
24# veredicto sin su prueba obliga a confiar; con la prueba, se audita en diez segundos.
25#
26# Ejemplo real (htop): yo lo habría puesto `-only` de memoria. `htop.c` dice «Released under the GNU
27# GPLv2+» y el README «version 2 (GPL-2.0) or, at your option, any later version» ⇒ es `-or-later`.
28# La memoria se equivoca; la cita no.
29#
30# Uso: scripts/licencias-desambiguar.sh (escribe docs/licencias-desambiguadas.tsv)
31set -uo pipefail
32ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
33SALIDA="${SALIDA:-docs/licencias-desambiguadas.tsv}"
34command -v gh >/dev/null && gh auth status >/dev/null 2>&1 || { echo "!! 'gh' ausente o sin auth"; exit 1; }
35TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
36
37# Las ambiguas con fuente en GitHub. (La búsqueda de código de GitHub sólo funciona sobre GitHub;
38# el resto se queda para el humano, y eso se dice al final en vez de disimularlo.)
39./scripts/licencias.sh --revisar 2>/dev/null | while IFS=$'\t' read -r n lic; do
40 [ -z "${n:-}" ] && continue
41 f=$(ls "recipes/$n.toml" 2>/dev/null | head -1)
42 [ -z "$f" ] && f=$(ls recipes/incoming-*/"$n.toml" 2>/dev/null | head -1)
43 [ -z "$f" ] && continue
44 u=$(grep -m1 -E '^[[:space:]]*(tarball|repo)[[:space:]]*=' "$f" 2>/dev/null)
45 case "$u" in *github.com*) ;; *) continue ;; esac
46 slug=$(printf '%s\n' "$u" | sed -E 's#.*github\.com[:/]+([^/"]+)/([^/"]+).*#\1/\2#; s/\.git$//')
47 case "$slug" in */*) printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" ;; esac
48done | sort -u > "$TMP/obj.tsv"
49echo "==> ambiguas con fuente en GitHub: $(wc -l < "$TMP/obj.tsv")"
50
51# En SERIE: la búsqueda de código de GitHub va a 10 consultas por minuto. Paralelizar sólo consigue
52# que devuelva 403 y que la mitad se pierda en silencio, que es peor que tardar.
53: > "$TMP/ok.tsv"; : > "$TMP/no.tsv"
54while IFS=$'\t' read -r n lic slug; do
55 paths=$(gh api -X GET search/code -f q="repo:$slug \"any later version\"" \
56 --jq '[.items[].path] | join(" ")' 2>/dev/null)
57 # Tres clases de fichero que PARECEN evidencia y no lo son. Las tres aparecieron en la primera
58 # corrida, y sólo se vieron porque guardamos la cita:
59 # · ficheros de licencia → la frase es plantilla del propio COPYING (ver cabecera);
60 # · FIXTURES DE TEST → `caligula` "probaba" ser GPL-3.0-or-later por una frase dentro de
61 # `checks/headless/expected.iso`, una imagen de prueba;
62 # · CÓDIGO VENDORIZADO → `libnl` citaba `include/linux-private/linux/seg6.h`, una cabecera del
63 # KERNEL incrustada. La licencia del kernel no es la de libnl.
64 util=""
65 for p in $paths; do
66 bn=$(basename "$p" | tr '[:upper:]' '[:lower:]')
67 case "$bn" in copying*|license*|licence*|copyright) continue ;; esac
68 case "$p" in
69 *test*|*Test*|*fixture*|*checks/*|*testdata/*|*.iso|*.bin|*.img) continue ;;
70 *vendor/*|*third_party/*|*3rdparty/*|*linux-private/*|*/deps/*|*external/*) continue ;;
71 esac
72 util="$p"; break
73 done
74 if [ -z "$util" ]; then printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" >> "$TMP/no.tsv"; sleep 6; continue; fi
75 cita=$(gh api "repos/$slug/contents/$util" --jq .content 2>/dev/null | base64 -d 2>/dev/null \
76 | grep -m1 -i "any later version" | tr -s ' \t' ' ' | sed 's/^ *//' | cut -c1-120)
77 # ── LA CITA TIENE QUE HABLAR DE LA MISMA VERSIÓN QUE LA LICENCIA DECLARADA ───────────────────
78 # `lm-sensors` estaba declarado GPL-2.0 y su cita decía «version 2.1 of the License», que es la
79 # LGPL: la frase era real pero NO sostenía lo que se le atribuía. Una evidencia que habla de otra
80 # licencia no es evidencia de ésta.
81 ver=$(printf '%s' "$lic" | sed -E 's/^A?L?GPL-([0-9.]+).*/\1/')
82 case "$cita" in
83 *"$ver"*) ;;
84 *) printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" >> "$TMP/no.tsv"; sleep 6; continue ;;
85 esac
86 nuevo=$(printf '%s' "$lic" | sed -E 's/^(A?L?GPL-[0-9.]+)$/\1-or-later/')
87 printf '%s\t%s\t%s :: %s\n' "$n" "$nuevo" "$util" "${cita:-(sin cita legible)}" >> "$TMP/ok.tsv"
88 sleep 6
89done < "$TMP/obj.tsv"
90
91N=$(wc -l < "$TMP/ok.tsv"); M=$(wc -l < "$TMP/no.tsv")
92{
93 echo "# licencias-desambiguadas.tsv — GENERADO por scripts/licencias-desambiguar.sh."
94 echo "# Resuelve -only vs -or-later buscando la concesión FUERA del COPYING (donde la frase es"
95 echo "# plantilla y daría siempre positivo). Tercera columna: fichero :: cita que lo decidió."
96 echo "# Resueltas $N · sin evidencia (quedan para el humano) $M"
97 sort "$TMP/ok.tsv"
98} > "$SALIDA"
99echo "==> resueltas $N · sin evidencia $M → $SALIDA"
100[ "$M" -gt 0 ] && { echo "==> sin evidencia:"; cut -f1 "$TMP/no.tsv" | tr '\n' ' '; echo; }