diff --git a/docs/licencias-desambiguadas.tsv b/docs/licencias-desambiguadas.tsv new file mode 100644 index 00000000..615ed753 --- /dev/null +++ b/docs/licencias-desambiguadas.tsv @@ -0,0 +1,13 @@ +# licencias-desambiguadas.tsv — GENERADO por scripts/licencias-desambiguar.sh. +# Resuelve -only vs -or-later buscando la concesión FUERA del COPYING (donde la frase es +# plantilla y daría siempre positivo). Tercera columna: fichero :: cita que lo decidió. +# Resueltas 9 (auditadas a mano; 3 descartadas por evidencia inválida) · sin evidencia 40 +dosfstools GPL-3.0-or-later src/lfn.h :: (at your option) any later version. +htop GPL-2.0-or-later README.md :: GNU General Public License, version 2 (GPL-2.0) or, at your option, any later version. +libndp LGPL-2.1-or-later libndp/list.h :: * version 2.1 of the License, or (at your option) any later version. +libqalculate GPL-2.0-or-later libqalculate/qalculate.h :: (at your option) any later version. +libsndfile LGPL-2.1-or-later src/id3.h :: ** (at your option) any later version. +libusb LGPL-2.1-or-later android/config.h :: * version 2.1 of the License, or (at your option) any later version. +qrencode LGPL-2.1-or-later mask.h :: * version 2.1 of the License, or any later version. +rink GPL-3.0-or-later core/definitions.units :: # (at your option) any later version. +tig GPL-2.0-or-later src/graph.c :: * the License, or (at your option) any later version. diff --git a/recipes/dosfstools.toml b/recipes/dosfstools.toml index 33cde9a4..e0021441 100644 --- a/recipes/dosfstools.toml +++ b/recipes/dosfstools.toml @@ -3,7 +3,7 @@ # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. name = "dosfstools" version = "4.2" -license = "GPL-3.0" +license = "GPL-3.0-or-later" [source] tarball = "https://github.com/dosfstools/dosfstools/releases/download/v4.2/dosfstools-4.2.tar.gz" diff --git a/recipes/htop.toml b/recipes/htop.toml index 7dc330e0..6a21388b 100644 --- a/recipes/htop.toml +++ b/recipes/htop.toml @@ -13,7 +13,7 @@ name = "htop" version = "3.5.1" -license = "GPL-2.0" +license = "GPL-2.0-or-later" [source] tarball = "https://github.com/htop-dev/htop/releases/download/3.5.1/htop-3.5.1.tar.xz" diff --git a/recipes/incoming-cosmic/libqalculate.toml b/recipes/incoming-cosmic/libqalculate.toml index d680e47b..ce9d6e1d 100644 --- a/recipes/incoming-cosmic/libqalculate.toml +++ b/recipes/incoming-cosmic/libqalculate.toml @@ -32,7 +32,7 @@ # en argv» era una suposición, no una medición. name = "libqalculate" version = "5.12.0" -license = "GPL-2.0" +license = "GPL-2.0-or-later" [source] tarball = "https://github.com/Qalculate/libqalculate/releases/download/v5.12.0/libqalculate-5.12.0.tar.gz" diff --git a/recipes/incoming-cosmic/libsndfile.toml b/recipes/incoming-cosmic/libsndfile.toml index 9452a538..878f7e41 100644 --- a/recipes/incoming-cosmic/libsndfile.toml +++ b/recipes/incoming-cosmic/libsndfile.toml @@ -17,7 +17,7 @@ # ISLA DINÁMICA: shared, porque libpulse y todo lo que cuelga de gnome-shell son .so. name = "libsndfile" version = "1.2.2" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/libsndfile/libsndfile/releases/download/1.2.2/libsndfile-1.2.2.tar.xz" diff --git a/recipes/incoming-gnome/libsndfile.toml b/recipes/incoming-gnome/libsndfile.toml index 9452a538..878f7e41 100644 --- a/recipes/incoming-gnome/libsndfile.toml +++ b/recipes/incoming-gnome/libsndfile.toml @@ -17,7 +17,7 @@ # ISLA DINÁMICA: shared, porque libpulse y todo lo que cuelga de gnome-shell son .so. name = "libsndfile" version = "1.2.2" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/libsndfile/libsndfile/releases/download/1.2.2/libsndfile-1.2.2.tar.xz" diff --git a/recipes/incoming-gnome/libusb.toml b/recipes/incoming-gnome/libusb.toml index 68a150f4..9a3d84dc 100644 --- a/recipes/incoming-gnome/libusb.toml +++ b/recipes/incoming-gnome/libusb.toml @@ -15,7 +15,7 @@ # en el cierre, y sin el riesgo del choque eudev/libudev-zero por el mismo .pc. name = "libusb" version = "1.0.30" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2" diff --git a/recipes/incoming-kde/libndp.toml b/recipes/incoming-kde/libndp.toml index dff0e1a9..15eeea45 100644 --- a/recipes/incoming-kde/libndp.toml +++ b/recipes/incoming-kde/libndp.toml @@ -7,7 +7,7 @@ # "de gueto" que gcc/cmake: herramienta del rootfs, no del catálogo). name = "libndp" version = "1.9" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/jpirko/libndp/archive/refs/tags/v1.9.tar.gz" diff --git a/recipes/incoming-kde/libqalculate.toml b/recipes/incoming-kde/libqalculate.toml index 1b6937fa..2cb40b26 100644 --- a/recipes/incoming-kde/libqalculate.toml +++ b/recipes/incoming-kde/libqalculate.toml @@ -9,7 +9,7 @@ # --disable-textport (evita readline/ncurses; sólo lib, sin binario qalc). libxml-2.0 REQUIRED → libxml2 sellado ✓. name = "libqalculate" version = "5.6.0" -license = "GPL-2.0" +license = "GPL-2.0-or-later" [source] tarball = "https://github.com/Qalculate/libqalculate/releases/download/v5.6.0/libqalculate-5.6.0.tar.gz" diff --git a/recipes/incoming-kde/libsndfile.toml b/recipes/incoming-kde/libsndfile.toml index 0575bf93..1bfe8680 100644 --- a/recipes/incoming-kde/libsndfile.toml +++ b/recipes/incoming-kde/libsndfile.toml @@ -5,7 +5,7 @@ # nativos, que es lo que PulseAudio necesita para los sonidos del sistema. Evita la cadena flac+opus. name = "libsndfile" version = "1.2.2" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/libsndfile/libsndfile/releases/download/1.2.2/libsndfile-1.2.2.tar.xz" diff --git a/recipes/incoming-kde/qrencode.toml b/recipes/incoming-kde/qrencode.toml index 80fd0b7f..ce60b344 100644 --- a/recipes/incoming-kde/qrencode.toml +++ b/recipes/incoming-kde/qrencode.toml @@ -7,7 +7,7 @@ # NO → estático). find_package(PNG)/find_package(Iconv) NO son REQUIRED; iconv lo aporta musl. name = "qrencode" version = "4.1.1" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/fukuchi/libqrencode/archive/refs/tags/v4.1.1.tar.gz" diff --git a/recipes/libusb.toml b/recipes/libusb.toml index da613506..44a19b97 100644 --- a/recipes/libusb.toml +++ b/recipes/libusb.toml @@ -3,7 +3,7 @@ # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. name = "libusb" version = "1.0.30" -license = "LGPL-2.1" +license = "LGPL-2.1-or-later" [source] tarball = "https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2" diff --git a/recipes/rink.toml b/recipes/rink.toml index 7b525e33..224ea930 100644 --- a/recipes/rink.toml +++ b/recipes/rink.toml @@ -4,7 +4,7 @@ # - las deps van con su nombre NIX; remapealas a las recetas del corpus si difieren. name = "rink" version = "0.9.0" -license = "GPL-3.0" +license = "GPL-3.0-or-later" [source] repo = "https://github.com/tiffany352/rink-rs" diff --git a/recipes/tig.toml b/recipes/tig.toml index 67cbd5f0..8fcb7bad 100644 --- a/recipes/tig.toml +++ b/recipes/tig.toml @@ -1,7 +1,7 @@ # tig 2.6.1 — interfaz ncurses para git (C). De-Alpinizada: gcc, configure-split, ncurses+pcre2. name = "tig" version = "2.6.1" -license = "GPL-2.0" +license = "GPL-2.0-or-later" [source] tarball = "https://github.com/jonas/tig/releases/download/tig-2.6.1/tig-2.6.1.tar.gz" sha256 = "5adeabdcd93aa0423d618da8b878b53482bef6e0e9e1fe224acc0f18031fe91e" diff --git a/scripts/licencias-desambiguar.sh b/scripts/licencias-desambiguar.sh new file mode 100755 index 00000000..494db362 --- /dev/null +++ b/scripts/licencias-desambiguar.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# licencias-desambiguar.sh — resuelve `-only` vs `-or-later` en los SPDX obsoletos, CON LA CITA. +# +# ── EL PROBLEMA QUE CIERRA ───────────────────────────────────────────────────────────────────── +# La API de licencias de GitHub devuelve identificadores obsoletos (`GPL-2.0`, `GPL-3.0`, `LGPL-2.1`, +# `AGPL-3.0`). SPDX los declaró obsoletos **porque son ambiguos**: no dicen si el proyecto concede +# «sólo esta versión» o «ésta o cualquier posterior». La diferencia no es cosmética — decide con qué +# se puede combinar el paquete y bajo qué términos puede redistribuirlo quien lo reciba. +# +# ── POR QUÉ NO SE PUEDE MIRAR EL COPYING (la trampa) ─────────────────────────────────────────── +# El fichero COPYING de la GPL es **idéntico** en los dos casos: es el texto de la licencia, no la +# concesión. Y encima su apéndice «cómo aplicar la licencia a tus programas» contiene literalmente la +# frase «or (at your option) any later version» ⇒ buscarla en el COPYING da SIEMPRE positivo y +# parecería evidencia siendo texto de plantilla. Es la misma trampa que la regla del `.a` no-PIC, +# donde `grep R_X86_64_32` contaba reubicaciones de `.debug_*` y mentía. +# +# La concesión vive en las **cabeceras de los fuentes** y en el README. Por eso acá se busca la frase +# en TODO el repo y se **descartan explícitamente** COPYING/LICENSE/LICENCE: si sólo aparece ahí, no +# hay evidencia y el paquete se deja para el humano. +# +# ── SE GUARDA LA CITA, NO SÓLO EL VEREDICTO ──────────────────────────────────────────────────── +# Cada línea de salida lleva el fichero y la frase exacta que decidió. Una licencia es una afirmación +# legal: quien la revise tiene que poder ver POR QUÉ dice lo que dice sin repetir el trabajo. Un +# veredicto sin su prueba obliga a confiar; con la prueba, se audita en diez segundos. +# +# Ejemplo real (htop): yo lo habría puesto `-only` de memoria. `htop.c` dice «Released under the GNU +# GPLv2+» y el README «version 2 (GPL-2.0) or, at your option, any later version» ⇒ es `-or-later`. +# La memoria se equivoca; la cita no. +# +# Uso: scripts/licencias-desambiguar.sh (escribe docs/licencias-desambiguadas.tsv) +set -uo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT" +SALIDA="${SALIDA:-docs/licencias-desambiguadas.tsv}" +command -v gh >/dev/null && gh auth status >/dev/null 2>&1 || { echo "!! 'gh' ausente o sin auth"; exit 1; } +TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT + +# Las ambiguas con fuente en GitHub. (La búsqueda de código de GitHub sólo funciona sobre GitHub; +# el resto se queda para el humano, y eso se dice al final en vez de disimularlo.) +./scripts/licencias.sh --revisar 2>/dev/null | while IFS=$'\t' read -r n lic; do + [ -z "${n:-}" ] && continue + 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 + slug=$(printf '%s\n' "$u" | sed -E 's#.*github\.com[:/]+([^/"]+)/([^/"]+).*#\1/\2#; s/\.git$//') + case "$slug" in */*) printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" ;; esac +done | sort -u > "$TMP/obj.tsv" +echo "==> ambiguas con fuente en GitHub: $(wc -l < "$TMP/obj.tsv")" + +# En SERIE: la búsqueda de código de GitHub va a 10 consultas por minuto. Paralelizar sólo consigue +# que devuelva 403 y que la mitad se pierda en silencio, que es peor que tardar. +: > "$TMP/ok.tsv"; : > "$TMP/no.tsv" +while IFS=$'\t' read -r n lic slug; do + paths=$(gh api -X GET search/code -f q="repo:$slug \"any later version\"" \ + --jq '[.items[].path] | join(" ")' 2>/dev/null) + # Tres clases de fichero que PARECEN evidencia y no lo son. Las tres aparecieron en la primera + # corrida, y sólo se vieron porque guardamos la cita: + # · ficheros de licencia → la frase es plantilla del propio COPYING (ver cabecera); + # · FIXTURES DE TEST → `caligula` "probaba" ser GPL-3.0-or-later por una frase dentro de + # `checks/headless/expected.iso`, una imagen de prueba; + # · CÓDIGO VENDORIZADO → `libnl` citaba `include/linux-private/linux/seg6.h`, una cabecera del + # KERNEL incrustada. La licencia del kernel no es la de libnl. + util="" + for p in $paths; do + bn=$(basename "$p" | tr '[:upper:]' '[:lower:]') + case "$bn" in copying*|license*|licence*|copyright) continue ;; esac + case "$p" in + *test*|*Test*|*fixture*|*checks/*|*testdata/*|*.iso|*.bin|*.img) continue ;; + *vendor/*|*third_party/*|*3rdparty/*|*linux-private/*|*/deps/*|*external/*) continue ;; + esac + util="$p"; break + done + if [ -z "$util" ]; then printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" >> "$TMP/no.tsv"; sleep 6; continue; fi + cita=$(gh api "repos/$slug/contents/$util" --jq .content 2>/dev/null | base64 -d 2>/dev/null \ + | grep -m1 -i "any later version" | tr -s ' \t' ' ' | sed 's/^ *//' | cut -c1-120) + # ── LA CITA TIENE QUE HABLAR DE LA MISMA VERSIÓN QUE LA LICENCIA DECLARADA ─────────────────── + # `lm-sensors` estaba declarado GPL-2.0 y su cita decía «version 2.1 of the License», que es la + # LGPL: la frase era real pero NO sostenía lo que se le atribuía. Una evidencia que habla de otra + # licencia no es evidencia de ésta. + ver=$(printf '%s' "$lic" | sed -E 's/^A?L?GPL-([0-9.]+).*/\1/') + case "$cita" in + *"$ver"*) ;; + *) printf '%s\t%s\t%s\n' "$n" "$lic" "$slug" >> "$TMP/no.tsv"; sleep 6; continue ;; + esac + nuevo=$(printf '%s' "$lic" | sed -E 's/^(A?L?GPL-[0-9.]+)$/\1-or-later/') + printf '%s\t%s\t%s :: %s\n' "$n" "$nuevo" "$util" "${cita:-(sin cita legible)}" >> "$TMP/ok.tsv" + sleep 6 +done < "$TMP/obj.tsv" + +N=$(wc -l < "$TMP/ok.tsv"); M=$(wc -l < "$TMP/no.tsv") +{ + echo "# licencias-desambiguadas.tsv — GENERADO por scripts/licencias-desambiguar.sh." + echo "# Resuelve -only vs -or-later buscando la concesión FUERA del COPYING (donde la frase es" + echo "# plantilla y daría siempre positivo). Tercera columna: fichero :: cita que lo decidió." + echo "# Resueltas $N · sin evidencia (quedan para el humano) $M" + sort "$TMP/ok.tsv" +} > "$SALIDA" +echo "==> resueltas $N · sin evidencia $M → $SALIDA" +[ "$M" -gt 0 ] && { echo "==> sin evidencia:"; cut -f1 "$TMP/no.tsv" | tr '\n' ' '; echo; }