gnome: dash-to-panel, appindicator y gnome-tweaks — y la lista de activas sale de las recetas

GNOME deja de ser la cáscara: tres extensiones y la app que el proyecto no instala y todo el mundo
termina instalando. El perfil queda 190/190 con 28 raíces, deuda 0.

  dash-to-panel v73   junta dash, ventanas y bandeja en una sola barra (lo que más cambia el uso)
  appindicator v64    devuelve los ICONOS DE BANDEJA que GNOME quitó; sin ella las apps que usan
                      StatusNotifierItem corren y no tienen dónde mostrarse
  blur-my-shell v72   (ya estaba; se le saca el override, ver abajo)
  gnome-tweaks 46.1   tema, iconos, cursor, tipografías, los botones de minimizar/maximizar que
                      GNOME esconde, y el interruptor de las extensiones

⚠ LA LISTA DE ACTIVAS SE MUEVE A UN SOLO SITIO, y esto era una bomba de relojería. `enabled-extensions`
es un ARRAY: un override de GSettings lo escribe ENTERO. Con la primera extensión el override vivía
en su receta y funcionaba; con la segunda NO se habrían sumado — ganaría la que compile última
(orden alfabético) y las otras quedarían instaladas y MUERTAS, sin un solo error. Ahora la arma
`scripts/gnome/gnome-start-qemu.sh`, que ya compilaba los esquemas al arrancar, enumerando los uuid
presentes en /usr/share/gnome-shell/extensions. La política es «lo que la imagen instala, se
activa»; el dconf del usuario gana sobre el default.
Una receta NO puede ser ese sitio: hammer exige `[source]` en toda receta («missing field `source`»),
así que no hay forma de escribir una receta de pura política. Se probó el bloque contra un árbol de
juguete: lista las dos extensiones, salta un directorio sin metadata.json, el override compila con
stderr vacío y `gsettings get` devuelve las dos. Y si la lista cambia borra `gschemas.compiled` para
forzar recompilación — sin eso, una imagen ya arrancada ignoraría la extensión nueva en silencio.

⚠ EL HALLAZGO CARO: `msgfmt` de gettext-tiny REVIENTA con las formas plurales del árabe. SIGILL,
exit 132, «index 6 out of bounds for type 'size_t[6]'», y deja un .mo de 0 bytes. Reproducido FUERA
de la receta con el binario sellado. No es el idioma sino el mensaje: blur-my-shell compila su
ar.po sin problema porque no tiene NINGÚN plural (msgstr[5] = 0); el de tweaks tiene exactamente
uno. Acá se saca `ar` del LINGUAS y se pierde la traducción de UNA app, con el diagnóstico escrito
en la receta. ARREGLARLO ES OTRA UNIDAD DE TRABAJO: gettext-tiny la usan 108 recetas.

Cada receta usa el mecanismo de SU upstream, que no es el mismo en las tres:
  blur-my-shell   `make build` llama a `gnome-extensions pack` —el CLI que nuestro shell apaga— así
                  que se copia el árbol a mano, con el layout LEÍDO del zip de EGO y comparado
                  contra él (idéntico, sin sobras ni faltantes)
  dash-to-panel   su Makefile tiene un `install` que honra DESTDIR y hace lo de un paquete de
                  distro: gschema y locale a los directorios GLOBALES. ⚠ `VERSION=73` se pasa a
                  mano porque sin ella el Makefile hace `git describe` y el árbol viene por
                  `git archive`, sin `.git` ⇒ metadata quedaría con el marcador "version": 9999
  appindicator    meson. ⚠ `-Dlocal_install=disabled` explícito: en `auto` decide mirando si el uid
                  es 0, o sea que hoy acierta POR ACCIDENTE — y si el lab dejara de correr como
                  root instalaría en $HOME/.local y el artefacto sellaría vacío. Y `jq` va en deps
                  aunque sea JavaScript puro: su meson saca el uuid del metadata.json con jq

⚠ gnome-tweaks NO costó ninguna receta nueva, y eso hubo que medirlo: su meson declara siete deps
mínimas de runtime y las siete ya estaban selladas. La que parecía traer una cadena de Python
entera —`pygobject-3.0`— la provee `py3-gobject`, que YA ESTABA en el corpus con otro nombre y
publica `pygobject-3.0.pc`. El nombre no es el hecho, otra vez.
Sí costó dos deps que el configure reclamó y que son el patrón `.pc Requires` → `[deps].build`:
`appstream` (la pide `libadwaita-1.pc` y el error culpa a libadwaita, que está bien instalada) y
`desktop-file-utils` (meson la busca como PROGRAMA para `update-desktop-database`).
Sus deps de RUNTIME —python3 y py3-gobject— van declaradas: es una app de Python, si no están en la
imagen se instala y no arranca, y ningún build lo delata.

Las cuatro licencias, verificadas en la fuente pineada y NINGUNA leída del COPYING —cuyo apéndice
«How to Apply» trae siempre la frase «or any later version»—: blur-my-shell del README del commit,
dash-to-panel y appindicator de la cabecera de sus fuentes, gnome-tweaks de su etiqueta SPDX.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV
This commit is contained in:
Sergio
2026-09-09 19:41:52 +00:00
co-authored by Claude Opus 5
parent a37b47a004
commit 4e4a2c8d2a
8 changed files with 481 additions and 80 deletions
+39
View File
@@ -104,6 +104,45 @@ chmod 700 "$XDG_RUNTIME_DIR"
[ -s /etc/machine-id ] || cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d - > /etc/machine-id
cp -f /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true
# ── ACTIVAR LAS EXTENSIONES QUE LA IMAGEN TRAE ────────────────────────────────────────────────
# Una extensión INSTALADA no hace nada: el shell sólo carga las que estén en
# `org.gnome.shell enabled-extensions`. Sin esto, empaquetarlas sella artefactos que la imagen
# lleva y nadie ejecuta — «sellado ≠ instalado», un escalón más abajo.
#
# ⚠ POR QUÉ LA LISTA SE ARMA ACÁ Y NO EN CADA RECETA. `enabled-extensions` es un ARRAY: un override
# lo escribe ENTERO. Si cada receta trajera el suyo, no se sumarían — ganaría el fichero que
# compile último (orden alfabético) y las demás quedarían instaladas y MUERTAS, sin un solo error.
# La lista tiene que existir en un único sitio, y una receta no puede ser ese sitio: hammer exige
# `[source]` en toda receta (comprobado: «missing field `source`»), así que no hay forma de escribir
# una receta de pura política sin inventarle una fuente. Acá, donde ya se compilan los esquemas, sí.
#
# La política es «lo que la imagen instala, se activa»: el catálogo sólo empaqueta extensiones que
# queremos encendidas. Quien no quiera una la apaga desde su dconf, que gana sobre el default.
EXT_DIR=/usr/share/gnome-shell/extensions
OVR="$GSETTINGS_SCHEMA_DIR/60_hammer-extensiones.gschema.override"
if [ -d "$EXT_DIR" ]; then
uuids=""
for d in "$EXT_DIR"/*/; do
[ -s "$d/metadata.json" ] || continue
u="$(basename "$d")"
uuids="$uuids${uuids:+, }'$u'"
done
if [ -n "$uuids" ]; then
nuevo_ovr="[org.gnome.shell]
enabled-extensions=[$uuids]"
# Si la lista cambió respecto de la compilada, hay que RECOMPILAR: el bloque de abajo sólo
# compila cuando falta `gschemas.compiled`, así que un override nuevo sobre una imagen ya
# arrancada quedaría ignorado en silencio.
if [ ! -f "$OVR" ] || [ "$(cat "$OVR")" != "$nuevo_ovr" ]; then
printf '%s\n' "$nuevo_ovr" > "$OVR"
rm -f "$GSETTINGS_SCHEMA_DIR/gschemas.compiled"
fi
say "extensiones a activar: $uuids"
else
say "no hay extensiones instaladas en $EXT_DIR"
fi
fi
# gschemas.compiled: meson NO lo genera cuando DESTDIR está seteado (lo dice en el log del build:
# "Skipping custom install script because DESTDIR is set"). Sin él, GSettings aborta al primer
# g_settings_new() y el shell no llega ni a abrir el DRM. Se compila acá, en el primer arranque.