Files
takana/scripts/licencias-rootfs.sh
Sergio 476168bb07 takana etapa 5c: comentarios de scripts, MOTD, y un BUG que introdujo la etapa 4
250 líneas de comentario en 151 scripts. Control verificado: el diff no toca
NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa.

El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces:
las tres eran el MOTD que el script escribe DENTRO de la imagen construida —
texto del producto, no comentario del script. Se cambiaron aparte y a
propósito, que es rebranding, no limpieza.

Y el hallazgo caro:  casaba contra ,
que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate.
La etapa 4 lo movió a  y el script quedó casando NADA. No fallaba:
imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado
con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y
tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja.

Además 14 rutas de módulo  en docs, que el barrido anterior no tocó
porque  no es frontera de palabra.
2026-09-09 19:28:48 +00:00

112 lines
6.7 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 takana 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 `takana 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
# `takana 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
# ⚠ SE RESUELVE POR EL CAMPO `name`, NO POR EL NOMBRE DE FICHERO (2026-09-09).
# Antes esto hacía `ls recipes/$1.toml` y nada más, y el paquete se llama por su campo `name`, que
# en 34 recetas del corpus NO coincide con el fichero (recipes/nu.toml → `nushell`, dust.toml →
# `du-dust`, incoming-kde/qtbase.toml → `qt6-qtbase`…). Medido: **14 paquetes que SÍ declaran
# licencia salían como «licencia desconocida»** y el guardián vetaba una imagen perfectamente
# publicable. Un falso veto se nota; el hermano silencioso no: si el fichero <pkg>.toml existe pero
# pertenece a OTRO paquete, la versión vieja reportaba la licencia EQUIVOCADA sin decir nada. Hoy no
# pasa (medido: 0 casos), pero el índice por `name` lo hace imposible en vez de improbable.
# El índice se arma UNA vez: 1128 recetas, no una por paquete.
IDX=$(mktemp)
for f in recipes/*.toml recipes/incoming-*/*.toml; do
[ -f "$f" ] || continue
awk -F'"' -v ARCH="$f" -v BASE="$(basename "$f" .toml)" '
/^\[/{exit}
/^[[:space:]]*name[[:space:]]*=/ {if(!n) n=$2}
/^[[:space:]]*license[[:space:]]*=/{if(!l) l=$2}
END{if(n) printf "%s\t%s\t%s\t%s\n", n, l, ARCH, BASE}' "$f"
done > "$IDX"
trap 'rm -f "$IDX"' EXIT
# Precedencia: la receta canónica (recipes/*.toml) antes que una cola incoming-*, igual que antes.
# Comprobado que no cambia nada hoy: de los 34 nombres duplicados, CERO declaran licencias distintas.
# Se busca por `name` y, si no aparece, por nombre de FICHERO: los dos llamadores existen y usan
# claves distintas — el grafo de estado nombra sus nodos por el fichero (`dust`) y el artefacto del
# store por el campo `name` (`du-dust`). Resolver sólo por uno rompe al otro; medido en los dos
# sentidos sobre el perfil base+cli y sobre el corpus entero.
licencia_de_receta() {
lic=$(awk -F'\t' -v p="$1" '
$1==p && $2!="" {if ($3 !~ /^recipes\/incoming-/) {print $2; exit} else if (!c) c=$2}
END{if (c) print c}' "$IDX")
[ -n "$lic" ] || lic=$(awk -F'\t' -v p="$1" '
$4==p && $2!="" {if ($3 !~ /^recipes\/incoming-/) {print $2; exit} else if (!c) c=$2}
END{if (c) print c}' "$IDX")
[ -n "$lic" ] || return 1
printf '%s\n' "$lic"
}
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)"