═══ LA CORRECCIÓN DE FONDO: no es `pack`, son las IMÁGENES ═══
El SDD 19 y el 20 decían «inyectar el texto de la licencia en `hammer pack`, que es aguas
abajo del ArtifactHash y sale gratis». La conclusión sobre el coste era correcta pero el
SITIO estaba mal, y por una razón que sólo se ve leyendo el código: `pack` produce un
`.swm`, que es una RECETA DE TRANSFORMACIÓN sobre fuente pública y por diseño explícito
«NUNCA transporta binarios cocidos»; e `install` REPRODUCE construyendo, con cache-hit del
corpus, en vez de bajar binarios. O sea que **el canal de paquetes de hammer no distribuye
binarios** y la obligación de acompañar-el-binario ahí casi no aplica.
Donde sí aplica, con toda su fuerza, es en la IMAGEN INSTALABLE: su rootfs se puebla con
`hammer install` sobre un prefix y de ahí sale un USB/ISO lleno de binarios que se le da a
alguien. Ése es el punto de entrega. (La obligación del espejo de FUENTES es otra y sigue
entera: las recetas apuntan a URLs de terceros que se caen.)
═══ LO QUE ENTRA ═══
· `scripts/licencias-textos.sh` + `licenses/`: los 44 textos CANÓNICOS de SPDX
(spdx/license-list-data), uno por identificador realmente en uso — la lista se saca de las
recetas, no de un fichero escrito a mano que se desincroniza. 764K.
· `scripts/licencias-rootfs.sh`: escribe /usr/share/licenses/<pkg>/ con el texto de CADA
licencia de la expresión (`Unlicense OR MIT` ⇒ los DOS textos: en un OR el destinatario
elige, y entregar uno solo le quita una opción que el autor le concedió), más un
MANIFEST.tsv. Medido: 1,0 MB para el perfil `cli` de 44 paquetes.
═══ Y SOBRE TODO: ES UN VETO ═══
Sale ≠0 si algún paquete del rootfs no declara licencia. Un informe que no puede bloquear no
cambia lo que ocurre; esto corta el paso. Y se ganó el sueldo al primer intento: reveló que
los perfiles `base` y `cli` —los dos más simples, los que primero se publicarían— llevaban
10 y 12 paquetes con binarios y licencia desconocida. Curados los 10 que se podían afirmar
(doas, iputils, less, mandoc, procps-ng, rsync, strace, sudo, tree, usbutils); ambos perfiles
bajan a 2.
Esos 2 NO se rellenan a propósito, y quedan vetando:
· lsof — licencia propia del proyecto, sin identificador SPDX. Toca `LicenseRef-lsof` con
su texto, que ya viaja en el tarball (la receta instala su COPYING).
· tzdata — dominio público por declaración de sus autores, y SPDX no tiene identificador
para eso (es una AUSENCIA de licencia, no una licencia). Toca
`LicenseRef-PublicDomain` documentado, no forzar uno de la lista.
De paso: normalizado `GPL-3.0+` (sufijo antiguo) a `GPL-3.0-or-later`, misma equivalencia
documentada que la barra de Cargo. Y el caso especial que puse para `Linux-syscall-note`
(«las excepciones viven en exceptions/») era una suposición razonable y FALSA: SPDX las
publica en el mismo `text/`; ese directorio no existe.
1063 de 1141 (93%). Hashes verificados: 0 movidos.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
83 lines
4.8 KiB
Bash
Executable File
83 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# licencias-rootfs.sh — mete los textos de licencia en un rootfs y VETA el que no cumpla.
|
|
#
|
|
# ── EL PUNTO DE ENTREGA ES LA IMAGEN, NO EL PAQUETE ────────────────────────────────────────────
|
|
# El canal de paquetes de hammer no distribuye binarios: `pack` produce un `.swm` (receta de
|
|
# transformación sobre fuente pública, «NUNCA transporta binarios cocidos») e `install` REPRODUCE
|
|
# construyendo con cache-hit del corpus. Donde sí se le entrega un binario a alguien es en la
|
|
# **imagen instalable**, cuyo rootfs se puebla con `hammer install` sobre un prefix. Por eso la
|
|
# obligación de acompañar-el-binario-con-su-licencia se cumple ACÁ. (El SDD 19/20 decía «inyectar en
|
|
# `hammer pack`»; era una lectura equivocada de por dónde salen los binarios.)
|
|
#
|
|
# ── LO QUE HACE ────────────────────────────────────────────────────────────────────────────────
|
|
# Para cada paquete del rootfs escribe `/usr/share/licenses/<pkg>/`: el texto canónico de cada
|
|
# licencia de su expresión SPDX (`MIT OR Apache-2.0` ⇒ los DOS textos, porque el destinatario elige)
|
|
# y un `SPDX` con la expresión literal. Más un `MANIFEST.tsv` global: paquete → expresión.
|
|
#
|
|
# ── Y SOBRE TODO: ES UN VETO, NO UN ADORNO ─────────────────────────────────────────────────────
|
|
# Sale ≠0 si algún paquete del rootfs NO declara licencia. Publicar una imagen con binarios cuya
|
|
# licencia no conocemos es exactamente el riesgo que obliga a bajar una descarga ya publicada, y es
|
|
# el tipo de cosa que nadie mira el día del anuncio si no falla sola. Un informe que no puede
|
|
# bloquear no cambia lo que ocurre; por eso esto corta el paso.
|
|
# `--informe` lo degrada a sólo mirar, para poder medir sin frenar un build en curso.
|
|
#
|
|
# Uso: scripts/licencias-rootfs.sh <prefix> [pkg...] (sin pkgs: los lee de stdin)
|
|
# scripts/licencias-rootfs.sh --informe <prefix> ...
|
|
set -uo pipefail
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
|
|
TEXTOS="${TEXTOS:-licenses}"
|
|
|
|
SOLO_INFORME=0
|
|
[ "${1:-}" = "--informe" ] && { SOLO_INFORME=1; shift; }
|
|
PREFIX="${1:-}"; shift || true
|
|
[ -n "$PREFIX" ] || { echo "uso: $0 [--informe] <prefix> [pkg...]"; exit 2; }
|
|
[ -d "$TEXTOS" ] || { echo "!! faltan los textos: $TEXTOS (correr scripts/licencias-textos.sh)"; exit 1; }
|
|
|
|
# Los paquetes: por argumento, o por stdin, o —si no hay ninguno— los que el rootfs tenga bajo
|
|
# /usr/bin, que es una aproximación pobre pero honesta y avisada.
|
|
if [ "$#" -gt 0 ]; then printf '%s\n' "$@" > /tmp/.lic-pkgs
|
|
elif [ ! -t 0 ]; then cat > /tmp/.lic-pkgs
|
|
else echo "!! sin lista de paquetes (pasala por argumento o stdin)"; exit 2; fi
|
|
sort -u /tmp/.lic-pkgs | grep -v '^$' > /tmp/.lic-pkgs2 && mv /tmp/.lic-pkgs2 /tmp/.lic-pkgs
|
|
|
|
licencia_de_receta() {
|
|
f=$(ls "recipes/$1.toml" 2>/dev/null | head -1)
|
|
[ -z "$f" ] && f=$(ls recipes/incoming-*/"$1.toml" 2>/dev/null | head -1)
|
|
[ -z "$f" ] && return 1
|
|
awk -F'"' '/^\[/{exit} /^[[:space:]]*license[[:space:]]*=/{print $2; exit}' "$f"
|
|
}
|
|
|
|
DESTDIR="$PREFIX/usr/share/licenses"
|
|
[ "$SOLO_INFORME" = 1 ] || mkdir -p "$DESTDIR"
|
|
MAN="$DESTDIR/MANIFEST.tsv"
|
|
[ "$SOLO_INFORME" = 1 ] || {
|
|
{ echo "# paquete<TAB>expresión SPDX — generado por scripts/licencias-rootfs.sh"
|
|
echo "# El texto de cada licencia está en /usr/share/licenses/<paquete>/"; } > "$MAN"
|
|
}
|
|
|
|
con=0; sin=0; sinlista=""; sintexto=""
|
|
while IFS= read -r pkg; do
|
|
lic=$(licencia_de_receta "$pkg" || true)
|
|
if [ -z "${lic:-}" ]; then sin=$((sin+1)); sinlista="$sinlista $pkg"; continue; fi
|
|
con=$((con+1))
|
|
[ "$SOLO_INFORME" = 1 ] && continue
|
|
d="$DESTDIR/$pkg"; mkdir -p "$d"; printf '%s\n' "$lic" > "$d/SPDX"
|
|
# Una expresión puede nombrar VARIAS licencias; se copian TODAS. En un `OR` el destinatario elige,
|
|
# así que entregar sólo una le quita una opción que el autor le concedió.
|
|
for id in $(printf '%s\n' "$lic" | tr ' ' '\n' | grep -E '^[A-Za-z0-9]' | grep -vE '^(OR|AND|WITH)$'); do
|
|
if [ -s "$TEXTOS/$id.txt" ]; then cp "$TEXTOS/$id.txt" "$d/$id.txt"
|
|
else case "$sintexto" in *" $id "*) ;; *) sintexto="$sintexto $id ";; esac; fi
|
|
done
|
|
printf '%s\t%s\n' "$pkg" "$lic" >> "$MAN"
|
|
done < /tmp/.lic-pkgs
|
|
|
|
tot=$((con+sin))
|
|
echo "==> paquetes: $tot · con licencia $con · SIN licencia $sin"
|
|
[ -n "$sintexto" ] && echo "!! identificadores sin texto en $TEXTOS/:$sintexto"
|
|
if [ "$sin" -gt 0 ]; then
|
|
echo "!! ESTA IMAGEN NO SE PUEDE PUBLICAR: $sin paquetes con binarios y licencia desconocida."
|
|
echo "!! $(printf '%s' "$sinlista" | cut -c1-300)"
|
|
[ "$SOLO_INFORME" = 1 ] || exit 1
|
|
fi
|
|
[ "$SOLO_INFORME" = 1 ] || echo "==> escrito $DESTDIR (manifiesto: MANIFEST.tsv)"
|