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
+186 -61
View File
@@ -1,16 +1,16 @@
{
"schema": "hammer-build-state/1",
"totals": {
"recipes": 921,
"nodes": 923,
"sealed": 921,
"recipes": 924,
"nodes": 926,
"sealed": 924,
"ajeno": 2
},
"by_class": {
"ajeno": 2,
"c": 272,
"go": 362,
"gui": 48,
"gui": 51,
"kernel": 6,
"rust": 233
},
@@ -21,7 +21,7 @@
"ajeno": 2
},
"incoming-gnome": {
"sealed": 64
"sealed": 67
}
},
"by_profile": {
@@ -36,9 +36,9 @@
"sealed": 80
},
"escritorio-gnome": {
"raices": 25,
"clausura": 183,
"sealed": 183
"raices": 28,
"clausura": 190,
"sealed": 190
},
"escritorio-mirada": {
"raices": 17,
@@ -46,7 +46,7 @@
"sealed": 41
}
},
"sin_perfil": 661,
"sin_perfil": 660,
"orphan_deps": [],
"topo_ok": true,
"nodes": {
@@ -500,7 +500,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 6
"dependientes_total": 7
},
"argo-rollouts": {
"iname": "argo-rollouts",
@@ -1356,6 +1356,7 @@
"unblocks": 0,
"perfiles": [
"cli",
"escritorio-gnome",
"escritorio-sway"
],
"dependientes_total": 4
@@ -1530,7 +1531,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 9
"dependientes_total": 10
},
"caligula": {
"iname": "caligula",
@@ -3102,7 +3103,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 10
"dependientes_total": 11
},
"curlie": {
"iname": "curlie",
@@ -3391,7 +3392,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 0
"dependientes_total": 1
},
"devspace": {
"iname": "devspace",
@@ -4236,7 +4237,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 66
"dependientes_total": 67
},
"expat-shared": {
"iname": "expat-shared",
@@ -4809,7 +4810,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 22
"dependientes_total": 23
},
"foot": {
"iname": "foot",
@@ -4935,7 +4936,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 26
"dependientes_total": 27
},
"freeze": {
"iname": "freeze",
@@ -4979,7 +4980,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 31
"dependientes_total": 32
},
"fselect": {
"iname": "fselect",
@@ -5206,7 +5207,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 8
"dependientes_total": 9
},
"gdk-pixbuf-shared": {
"iname": "gdk-pixbuf-shared",
@@ -5360,7 +5361,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 104
"dependientes_total": 107
},
"gh": {
"iname": "gh",
@@ -5450,7 +5451,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 25
"dependientes_total": 26
},
"gi-foreign-typelibs": {
"iname": "gi-foreign-typelibs",
@@ -5992,7 +5993,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 43
"dependientes_total": 46
},
"glib-introspected": {
"iname": "glib-introspected",
@@ -6021,7 +6022,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 29
"dependientes_total": 30
},
"glib-shared": {
"iname": "glib-shared",
@@ -6245,6 +6246,35 @@
],
"dependientes_total": 0
},
"gnome-shell-extension-appindicator": {
"iname": "gnome-shell-extension-appindicator",
"version": "64",
"link": "dynamic",
"compiler": "zig-cc",
"deps": [
"gettext-tiny",
"glib",
"jq",
"libffi",
"meson",
"pcre2",
"pkgconf",
"python3",
"samurai",
"zlib"
],
"cls": "gui",
"queue": "incoming-gnome",
"foreign": false,
"hash": "b3:624995cae88d2a8997a3ea601569b83d3c255bf5dabfba34d8662bd1d8769683",
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"gnome-shell-extension-blur-my-shell": {
"iname": "gnome-shell-extension-blur-my-shell",
"version": "72",
@@ -6260,7 +6290,98 @@
"cls": "gui",
"queue": "incoming-gnome",
"foreign": false,
"hash": "b3:6b39a579c1d58e333c9c1d27875ba015705a340b883638afcde166fd6e6ac9d9",
"hash": "b3:c6fc6933317d86357e01d2cb056873f1713be64a349775d366ca6f5ac90d80ce",
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"gnome-shell-extension-dash-to-panel": {
"iname": "gnome-shell-extension-dash-to-panel",
"version": "73",
"link": "dynamic",
"compiler": "zig-cc",
"deps": [
"gettext-tiny",
"glib",
"libffi",
"make",
"pcre2",
"zlib"
],
"cls": "gui",
"queue": "incoming-gnome",
"foreign": false,
"hash": "b3:620ee7a11e13f0da06358aaa0eaf524145f91d0e8c8a9debd74cc923a41a6aff",
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"gnome-tweaks": {
"iname": "gnome-tweaks",
"version": "46.1",
"link": "dynamic",
"compiler": "zig-cc",
"deps": [
"appstream",
"cairo-shared",
"curl",
"desktop-file-utils",
"expat",
"fontconfig-shared",
"freetype-shared",
"fribidi",
"gdk-pixbuf",
"gettext-tiny",
"gi-foreign-girs",
"glib",
"glib-introspected",
"gobject-introspection",
"graphene",
"gsettings-desktop-schemas",
"gtk4",
"harfbuzz",
"iso-codes",
"libadwaita",
"libdrm",
"libepoxy",
"libffi",
"libgudev",
"libjpeg-turbo-shared",
"libpng-shared",
"libseccomp",
"libtiff-shared",
"libudev-zero",
"libxml2-shared",
"libxmlb",
"libyaml-shared",
"mesa",
"meson",
"openssl",
"pango",
"pcre2",
"pixman",
"pkgconf",
"py3-gobject",
"python3",
"samurai",
"wayland",
"wayland-protocols",
"xkeyboard-config",
"zlib-shared",
"zstd"
],
"cls": "gui",
"queue": "incoming-gnome",
"foreign": false,
"hash": "b3:ef9a900ea574f6d0129716b5ae3bc4b34b73450297adae3aeb2f21a3adb4e840",
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
@@ -6491,7 +6612,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 31
"dependientes_total": 32
},
"gobuster": {
"iname": "gobuster",
@@ -7154,7 +7275,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 6
"dependientes_total": 7
},
"grcov": {
"iname": "grcov",
@@ -7330,7 +7451,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 4
"dependientes_total": 5
},
"gtk3": {
"iname": "gtk3",
@@ -7429,7 +7550,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 5
"dependientes_total": 6
},
"gtk4-hello": {
"iname": "gtk4-hello",
@@ -7710,7 +7831,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 8
"dependientes_total": 9
},
"harfbuzz-shared": {
"iname": "harfbuzz-shared",
@@ -8444,7 +8565,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 5
"dependientes_total": 6
},
"istioctl": {
"iname": "istioctl",
@@ -8614,9 +8735,10 @@
"unblocks": 0,
"perfiles": [
"cli",
"escritorio-gnome",
"escritorio-sway"
],
"dependientes_total": 0
"dependientes_total": 1
},
"jql": {
"iname": "jql",
@@ -9788,7 +9910,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 1
"dependientes_total": 2
},
"libarchive": {
"iname": "libarchive",
@@ -9945,7 +10067,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 149
"dependientes_total": 150
},
"libei": {
"iname": "libei",
@@ -10022,7 +10144,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 21
"dependientes_total": 22
},
"libevdev": {
"iname": "libevdev",
@@ -10089,7 +10211,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 110
"dependientes_total": 113
},
"libffi-shared": {
"iname": "libffi-shared",
@@ -10289,7 +10411,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 5
"dependientes_total": 6
},
"libgusb": {
"iname": "libgusb",
@@ -10467,7 +10589,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 20
"dependientes_total": 21
},
"libksba": {
"iname": "libksba",
@@ -10644,7 +10766,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 33
"dependientes_total": 34
},
"libpsl": {
"iname": "libpsl",
@@ -10747,7 +10869,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 4
"dependientes_total": 5
},
"libsecret": {
"iname": "libsecret",
@@ -10930,7 +11052,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 7
"dependientes_total": 8
},
"libudev-zero": {
"iname": "libudev-zero",
@@ -10957,7 +11079,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 45
"dependientes_total": 46
},
"libunibreak": {
"iname": "libunibreak",
@@ -11190,7 +11312,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 10
"dependientes_total": 11
},
"libxmlb": {
"iname": "libxmlb",
@@ -11218,7 +11340,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 7
"dependientes_total": 8
},
"libyaml": {
"iname": "libyaml",
@@ -11260,7 +11382,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 2
"dependientes_total": 3
},
"linux": {
"iname": "linux",
@@ -11665,7 +11787,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 118
"dependientes_total": 119
},
"mako": {
"iname": "mako",
@@ -11987,7 +12109,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 146
"dependientes_total": 147
},
"mesa-llvmpipe": {
"iname": "mesa-llvmpipe",
@@ -12083,7 +12205,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 123
"dependientes_total": 125
},
"micro": {
"iname": "micro",
@@ -13312,6 +13434,7 @@
"unblocks": 0,
"perfiles": [
"cli",
"escritorio-gnome",
"escritorio-sway"
],
"dependientes_total": 1
@@ -13399,7 +13522,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 28
"dependientes_total": 29
},
"openssl-threads": {
"iname": "openssl-threads",
@@ -13666,7 +13789,7 @@
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 7
"dependientes_total": 8
},
"pango-shared": {
"iname": "pango-shared",
@@ -13848,7 +13971,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 82
"dependientes_total": 85
},
"pcre2-shared": {
"iname": "pcre2-shared",
@@ -14086,7 +14209,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 45
"dependientes_total": 46
},
"pkgconf": {
"iname": "pkgconf",
@@ -14110,7 +14233,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 395
"dependientes_total": 397
},
"plow": {
"iname": "plow",
@@ -14560,8 +14683,10 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"dependientes_total": 1
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 2
},
"py3-jinja2": {
"iname": "py3-jinja2",
@@ -14654,7 +14779,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 314
"dependientes_total": 316
},
"qrcp": {
"iname": "qrcp",
@@ -15269,7 +15394,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 266
"dependientes_total": 268
},
"sassc": {
"iname": "sassc",
@@ -18043,7 +18168,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 160
"dependientes_total": 161
},
"wayland-protocols": {
"iname": "wayland-protocols",
@@ -18071,7 +18196,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 57
"dependientes_total": 58
},
"websocketd": {
"iname": "websocketd",
@@ -18552,7 +18677,7 @@
"escritorio-kde",
"escritorio-sway"
],
"dependientes_total": 6
"dependientes_total": 7
},
"xorriso": {
"iname": "xorriso",
@@ -18991,7 +19116,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 113
"dependientes_total": 115
},
"zlib-ng": {
"iname": "zlib-ng",
@@ -19033,7 +19158,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 85
"dependientes_total": 86
},
"zola": {
"iname": "zola",
@@ -19092,7 +19217,7 @@
"escritorio-mirada",
"escritorio-sway"
],
"dependientes_total": 32
"dependientes_total": 33
}
}
}
+1 -1
View File
@@ -31,7 +31,7 @@
},
"escritorio-gnome": {
"grafo": "docs/state/build-state-gnome.json",
"nodos": 183,
"nodos": 190,
"en_deuda": 0,
"ondas": 0,
"profundidad": 0,
+15
View File
@@ -442,7 +442,22 @@ paquetes = [
# con `tar -x` y GNU tar no lee zip, y además el zip es un artefacto que construye EGO a partir
# de ese mismo repo. El layout instalado se verificó CONTRA ese zip: idéntico, sin sobras ni
# faltantes.
# ⚠ La LISTA de activas no vive en ninguna receta: `enabled-extensions` es un ARRAY y un override
# de GSettings lo escribe entero, así que dos recetas con el suyo no se suman — ganaría la última
# y las otras quedarían instaladas y MUERTAS, sin error. La arma `scripts/gnome/gnome-start-qemu.sh`
# enumerando los uuid presentes en /usr/share/gnome-shell/extensions antes de compilar los
# esquemas. Una receta no puede ser ese sitio: hammer exige `[source]` en toda receta.
"gnome-shell-extension-blur-my-shell",
# `dash-to-panel` es la que más cambia el uso diario: junta dash, ventanas y bandeja en una barra.
# `appindicator` devuelve los ICONOS DE BANDEJA que GNOME quitó — sin ella, las apps que usan
# StatusNotifierItem corren y no tienen dónde mostrarse.
"gnome-shell-extension-dash-to-panel", "gnome-shell-extension-appindicator",
# ── Y LA APP QUE GNOME NO INSTALA Y TODO EL MUNDO NECESITA ───────────────────────────────────
# `gnome-tweaks`: tema, iconos, cursor, tipografías, los botones de minimizar/maximizar que GNOME
# esconde, y el interruptor de las extensiones. Es una app de PYTHON, así que su receta declara
# `python3` y `py3-gobject` como deps de RUNTIME: sin ellas en la imagen se instala y no arranca,
# y ningún build puede delatarlo.
"gnome-tweaks",
]
[perfil.escritorio-cosmic]
@@ -0,0 +1,65 @@
# appindicator 64 — los ICONOS DE BANDEJA que GNOME quitó del shell. Es la extensión que hace que
# aparezcan en el panel las apps que usan `StatusNotifierItem` / `org.kde.StatusNotifierItem` (y las
# `libappindicator` viejas): clientes de chat, sincronizadores, gestores de portapapeles. Sin ella,
# esas apps corren y no tienen dónde mostrarse.
#
# Tercera extensión del catálogo. El patrón y el porqué de usar el REPO y no el zip de
# extensions.gnome.org están en la cabecera de `gnome-shell-extension-blur-my-shell`.
# El tag `v64` es el que EGO sirve para shell 48, preguntado a su API — ⚠ y acá la pregunta SÍ
# importó: el repo ya tiene `v65`, que es la que se habría tomado por reflejo.
#
# ══ ÉSTA SE CONSTRUYE CON MESON, NO CON UN MAKEFILE ════════════════════════════════════════════
# Las otras dos son un `pack` y un `make install`; ésta trae `meson.build` con los módulos `gnome`
# e `i18n`, y hace lo mismo que un paquete de distro cuando el prefix NO es local:
# · el árbol va a <prefix>/share/gnome-shell/extensions/<uuid>/
# · el gschema al directorio GLOBAL <prefix>/share/glib-2.0/schemas
# · los .mo a <prefix>/share/locale
# ⚠ `-Dlocal_install=disabled` explícito: la opción viene en `auto`, y en `auto` decide mirando si
# el prefix es /usr/local Y si el uid es 0. Nuestro sandbox corre como root con prefix /usr, así que
# hoy resolvería bien por accidente — pero «funciona por accidente se ve igual que funciona por
# diseño», y si mañana el lab dejara de ser root la instalación se iría a $HOME/.local y el artefacto
# sellaría vacío.
#
# ⚠ `jq` VA EN [deps] AUNQUE LA EXTENSIÓN SEA JAVASCRIPT PURO: `meson.build:22` saca el uuid del
# `metadata.json` con `run_command(jq, '-r', '.uuid', …)` — es una herramienta del sandbox, no una
# librería que se enlace. Sin ella meson corta en el configure.
name = "gnome-shell-extension-appindicator"
version = "64"
# licencia: el `meson.build` declara `license: 'GPL2'` y las cabeceras de los fuentes (extension.js,
# appIndicator.js…) dan la concesión completa: «either version 2 of the License, or (at your option)
# any later version». ⚠ No se dedujo del LICENSE: su apéndice «How to Apply» contiene esa frase
# siempre, así que buscarla ahí da positivo aunque el proyecto sea -only.
license = "GPL-2.0-or-later"
[source]
repo = "https://github.com/ubuntu/gnome-shell-extension-appindicator"
commit = "c934adc6c97363b8e6bb161ca8d1ac62d1da3d63" # tag v64 (ADR 0006: commit fijo, nunca HEAD)
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "dynamic"
[build.phases]
configure = "PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload -Dlocal_install=disabled"
compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
install = '''
set -e
UUID="appindicatorsupport@rgcjonas.gmail.com"
PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild
D="/out/usr/share/gnome-shell/extensions/$UUID"
# Guardián: se comprueba el RESULTADO, no la intención. Si `local_install` hubiera resuelto a
# «local», esto habría instalado en $HOME/.local y el artefacto sellaría igual, vacío (regla 3 de
# CLAUDE.md — un vacío llega hasta el final diciendo que todo fue bien).
test -s "$D/metadata.json"
test -s "$D/extension.js"
grep -q '"48"' "$D/metadata.json" # falla ruidoso si un bump deja de soportar shell 48
# El gschema tiene que haber ido al directorio GLOBAL, que es el que compila el lanzador al arrancar.
test -s /out/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.appindicator.gschema.xml
'''
[deps]
# `jq` es del configure (saca el uuid del metadata.json); `glib` trae glib-compile-schemas para el
# módulo `gnome` de meson y `gettext-tiny` el msgfmt para el módulo `i18n`.
build = ["meson", "samurai", "python3", "pkgconf", "jq", "glib", "pcre2", "libffi", "zlib", "gettext-tiny"]
@@ -81,23 +81,15 @@ for po in po/*.po; do
msgfmt -o "$D/locale/$lang/LC_MESSAGES/$UUID.mo" "$po"
done
# 4. INSTALADA ≠ ACTIVA. El shell sólo carga las extensiones que estén en la clave
# `org.gnome.shell enabled-extensions`, así que sin esto el artefacto sella, el fichero está en
# la imagen y NO PASA NADA al arrancar — el mismo cuadro de «sellado ≠ instalado» que este repo
# ya se comió con foot, mpv y libnotify, un escalón más abajo.
# Se hace con un override de GSettings, que cambia el DEFAULT sin tocar la config del usuario
# (quien no la quiera la apaga y su dconf gana). El `gnome-start-qemu.sh` corre
# `glib-compile-schemas` sobre este directorio en el primer arranque, así que el override entra.
#
# ⚠ TRAMPA PARA LA SEGUNDA EXTENSIÓN: `enabled-extensions` es un ARRAY y esto lo escribe ENTERO.
# Si mañana otra receta instala su propio override para la misma clave, no se suman: gana el
# fichero que compile último (orden alfabético) y la otra extensión queda instalada y muerta,
# sin error. Cuando llegue la segunda, la lista pasa a un solo sitio que las liste a todas.
mkdir -p /out/usr/share/glib-2.0/schemas
cat > /out/usr/share/glib-2.0/schemas/60_hammer-extensiones.gschema.override <<OVR
[org.gnome.shell]
enabled-extensions=['$UUID']
OVR
# 4. INSTALADA ≠ ACTIVA, y eso NO se resuelve acá. El shell sólo carga lo que esté en
# `org.gnome.shell enabled-extensions`, que es un ARRAY: un override de GSettings lo escribe
# ENTERO. Si cada receta trajera el suyo no se sumarían — ganaría el que compile último y las
# demás quedarían instaladas y MUERTAS, sin un solo error. Por eso la lista se arma en UN sitio,
# `scripts/gnome/gnome-start-qemu.sh`, que ya compila los esquemas al arrancar y ahora enumera
# los uuid presentes en /usr/share/gnome-shell/extensions. La política es «lo que la imagen
# instala, se activa»; quien no quiera una la apaga desde su dconf, que 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.)
# 5. Guardián barato: si el árbol quedara vacío o sin metadata, el artefacto sellaría igual y el
# fallo aparecería recién al arrancar el escritorio (regla 3 de CLAUDE.md — un vacío llega hasta
@@ -106,7 +98,6 @@ test -s "$D/metadata.json"
test -s "$D/extension.js"
test -s "$D/schemas/gschemas.compiled"
grep -q '"48"' "$D/metadata.json" # falla ruidoso si un bump de versión deja de soportar shell 48
grep -q "$UUID" /out/usr/share/glib-2.0/schemas/60_hammer-extensiones.gschema.override
'''
[deps]
@@ -0,0 +1,69 @@
# dash-to-panel 73 — la barra de tareas: mueve el dash al panel superior y junta lanzadores,
# ventanas abiertas y bandeja en una sola barra, al estilo Plasma/Windows 7+. Es, con diferencia, la
# extensión que más cambia el uso diario de GNOME.
#
# Segunda extensión del catálogo; el patrón lo fijó `gnome-shell-extension-blur-my-shell` y su
# cabecera explica por qué la fuente es el REPO y no el zip de extensions.gnome.org.
# El tag `v73` es el que EGO sirve para shell 48 (preguntado a su API, no supuesto) y el
# `metadata.json` del repo declara shell-version ["46","47","48","49","50"].
#
# ══ ACÁ SÍ SE USA EL `make install` DE UPSTREAM ════════════════════════════════════════════════
# A diferencia de blur-my-shell —cuyo `make build` llama a `gnome-extensions pack`, el CLI que
# nuestra receta del shell apaga— el Makefile de dash-to-panel tiene un `install` que HONRA
# `DESTDIR` y hace exactamente lo que hace un paquete de distro:
# · el árbol de la extensión va a $DESTDIR/usr/share/gnome-shell/extensions/<uuid>/
# · y en modo «system» SACA de ahí `schemas/` y `locale/` para ponerlos en los directorios
# GLOBALES: /usr/share/glib-2.0/schemas y /usr/share/locale.
# Se usa su install en vez de copiar a mano: es upstream quien decide qué entra y qué no.
#
# ⚠ Que el gschema termine en el directorio GLOBAL no es un detalle: lo compila el mismo
# `glib-compile-schemas` que corre el lanzador al arrancar, junto con los del shell. Por eso esta
# receta NO necesita compilarlo ella (y el Makefile igual lo compila para el `_build`).
#
# ⚠ `VERSION=73` SE PASA A MANO, y no es cosmético. Sin ella el Makefile hace
# `git describe --tags HEAD` para deducirla, y el árbol se materializa con `git archive`, SIN `.git`
# ⇒ la orden falla y `metadata.json` queda con el `"version": 9999` de marcador que trae el repo.
# Es la misma razón por la que la receta de `tzdata` pasa `VERSION=2026b`.
name = "gnome-shell-extension-dash-to-panel"
version = "73"
# licencia: el fichero COPYING del commit pineado es el texto del GPL v2, y el `metadata.json` de
# EGO no declara licencia. ⚠ La concesión está en la CABECERA de los fuentes (src/extension.js y
# compañía): «either version 2 of the License, or (at your option) any later version» ⇒ -or-later.
# No se dedujo del COPYING: su apéndice «How to Apply» contiene esa frase SIEMPRE, así que buscarla
# ahí da positivo aunque el proyecto sea -only (la trampa que ya está documentada en el repo).
license = "GPL-2.0-or-later"
[source]
repo = "https://github.com/home-sweet-gnome/dash-to-panel"
commit = "b31cbe1f3c9fa997b954b71199563fbedc2f3446" # tag v73 (ADR 0006: commit fijo, nunca HEAD)
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "dynamic"
[build.phases]
# Nada que compilar en el sentido de C: `make all` sólo compila el gschema y los catálogos .po.
configure = "true"
compile = 'make VERSION=73'
install = '''
set -e
UUID="dash-to-panel@jderose9.github.com"
make install DESTDIR=/out VERSION=73
D="/out/usr/share/gnome-shell/extensions/$UUID"
# Guardián: el `install` de upstream mueve cosas de sitio según DESTDIR, así que se comprueba el
# resultado y no la intención. Un artefacto a medias sella igual y el fallo sale recién al arrancar
# el escritorio (regla 3 de CLAUDE.md).
test -s "$D/metadata.json"
test -s "$D/extension.js"
grep -q '"version": 73' "$D/metadata.json" # falla si el sed de VERSION no corrió (quedaría 9999)
grep -q '"48"' "$D/metadata.json" # falla si un bump deja de soportar shell 48
# El gschema tiene que haber ido al directorio GLOBAL, que es lo que compila el lanzador.
test -s /out/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
'''
[deps]
# `make` para el Makefile, `glib` por glib-compile-schemas y `gettext-tiny` por msgfmt. Ninguna es
# una librería que la extensión enlace: no enlaza nada, es JavaScript.
build = ["make", "glib", "pcre2", "libffi", "zlib", "gettext-tiny"]
+97
View File
@@ -0,0 +1,97 @@
# gnome-tweaks 46.1 — el «tenés que instalarte esto» clásico de GNOME. Es la app que expone lo que
# el shell no deja tocar desde Configuración: tema, iconos, cursor, tipografías, botones de la barra
# de título (minimizar/maximizar, que GNOME esconde), comportamiento del touchpad, aplicaciones al
# inicio y el interruptor de las extensiones.
#
# ⚠ POR QUÉ 46.1 Y NO LA ÚLTIMA. gnome-tweaks NO tiene release 47 ni 48: la lista de
# download.gnome.org salta de 46.1 a 49.rc/49.0. Nuestro shell es el 48, así que la que corresponde
# es la ÚLTIMA anterior — la 49 apunta a la serie 49 de libadwaita/GTK y no hay motivo para
# arriesgar APIs nuevas contra un escritorio 48. Comprobado en el `cache.json` del propio servidor,
# no en una lista de memoria.
#
# ══ NO CUESTA NINGUNA RECETA NUEVA, y eso hubo que medirlo ═════════════════════════════════════
# Su `meson.build:22-28` declara las versiones mínimas de runtime en un bloque explícito «to make
# packaging more obvious», y las siete se cumplen con lo que ya está sellado:
# glib >= 2.78 ✓ 2.88 gobject-introspection >= 1.78 ✓ 1.84
# gtk4 >= 4.10 ✓ libadwaita-1 >= 1.4 ✓ 1.7.6
# pygobject-3.0 >= 3.46 ✓ 3.50 gudev-1.0 >= 238 ✓ 238
# gsettings-desktop-schemas >= 46 ✓ 48.0
#
# ⚠ El `pygobject-3.0` de esa lista lo provee `py3-gobject`, que YA ESTABA en el corpus con otro
# nombre —la escribió el frente GNOME para el build de libgweather— y publica `pygobject-3.0.pc`
# (verificado en su artefacto). Sin mirar eso, esta receta parecía arrastrar una cadena de Python
# entera. El nombre no es el hecho, otra vez.
#
# ⚠ Y NO necesita pycairo, que es la parte que `py3-gobject` construye con `-Dpycairo=disabled`:
# ningún fichero de `gtweak/` importa `cairo` (verificado sobre el tarball).
#
# ══ ES UNA APP DE PYTHON: SUS DEPS DE RUNTIME NO SON LAS DE BUILD ══════════════════════════════
# No se enlaza nada — `gnome-tweaks` es un script que importa `gi.repository`. Si `python3` y
# `py3-gobject` no están EN LA IMAGEN, la app se instala y no arranca, y el build no puede
# delatarlo. Por eso van en `[deps] runtime`, que es exactamente el caso de `xkbcomp` en xwayland.
name = "gnome-tweaks"
version = "46.1"
# licencia: no hace falta interpretar nada — los fuentes llevan etiqueta SPDX explícita.
# `gtweak/tweakmodel.py:2`: «SPDX-License-Identifier: GPL-3.0+», que es la forma vieja de
# `GPL-3.0-or-later`. El tarball no trae COPYING sino un directorio `LICENSES/`.
license = "GPL-3.0-or-later"
[source]
tarball = "https://download.gnome.org/sources/gnome-tweaks/46/gnome-tweaks-46.1.tar.xz"
sha256 = "2f192a7085fbd6843ecf825716d9da21ec9272029149ea35f3e159e0ac309b80"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "dynamic"
[build.phases]
# ⚠ SE SACA EL ÁRABE, y NO es una decisión sobre el idioma: es un BUG del `msgfmt` de
# `gettext-tiny`, el único que hay en el corpus. Revienta con SIGILL —«index 6 out of bounds for
# type 'size_t[6]'», exit 132— al compilar un mensaje con SEIS formas plurales, que es justo lo que
# tiene el árabe. Reproducido FUERA de la receta, con el binario sellado:
# msgfmt -o /tmp/x.mo po/ar.po → panic, exit 132, y deja un .mo de 0 bytes
# No es el idioma: `blur-my-shell` compila su `ar.po` sin problema porque no tiene NINGÚN mensaje
# plural (msgstr[5] = 0 apariciones); el de tweaks tiene exactamente uno (msgstr[5] = 1).
# ⚠ ARREGLARLO DE VERDAD ES OTRA UNIDAD DE TRABAJO: `gettext-tiny` la usan 108 recetas, así que
# tocarla re-hashea el corpus entero. Acá se pierde la traducción al árabe de UNA app y se deja el
# diagnóstico escrito; lo que NO se hace es callarlo.
configure = "sed -i '/^ar$/d' po/LINGUAS && PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload"
compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
install = '''
set -e
PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild
# Guardián: `gnome.post_install()` de meson se SALTA sus pasos cuando DESTDIR está seteado (lo dice
# en el log: «Skipping custom install script because DESTDIR is set»), así que lo que hay que
# comprobar es lo que SÍ tiene que quedar en el árbol. El gschema va al directorio GLOBAL, que es el
# que compila el lanzador al arrancar — igual que el de las extensiones.
test -s /out/usr/bin/gnome-tweaks
test -s /out/usr/share/glib-2.0/schemas/org.gnome.tweaks.gschema.xml
test -s /out/usr/share/applications/org.gnome.tweaks.desktop
# El módulo Python tiene que estar donde el intérprete lo busque, no en cualquier lado.
test -d /out/usr/lib/python3.12/site-packages/gtweak
'''
[deps]
# ⚠ RUNTIME, no build: es una app de Python que importa gi.repository. Sin estas dos en la imagen se
# instala y no arranca, y ningún build lo delata.
runtime = ["python3", "py3-gobject"]
# La clausura ENTERA, que las deps de hammer no son transitivas. Copiada de la que ya usa
# xdg-desktop-portal-gnome, que es el otro consumidor de gtk4+libadwaita de esta cola.
build = ["meson", "samurai", "python3", "pkgconf", "gettext-tiny", "gobject-introspection",
"py3-gobject", "gsettings-desktop-schemas", "gtk4", "libadwaita", "glib", "glib-introspected",
"gi-foreign-girs", "libgudev", "libudev-zero",
"cairo-shared", "pango", "graphene", "libepoxy", "wayland", "wayland-protocols", "libdrm",
"harfbuzz", "fribidi", "libpng-shared", "pixman", "freetype-shared", "expat", "libffi", "pcre2",
"zlib-shared", "gdk-pixbuf", "libjpeg-turbo-shared", "libtiff-shared", "mesa",
"fontconfig-shared", "xkeyboard-config", "iso-codes", "libseccomp",
# ⚠ `appstream` y su cola NO son un capricho: `libadwaita-1.pc` la lleva en `Requires`, así que
# pkg-config la resuelve TRANSITIVAMENTE y sin ella el configure corta con «Package 'appstream',
# required by 'libadwaita-1', not found» — culpando a libadwaita, que está perfectamente
# instalada. Es el patrón `.pc Requires` → `[deps].build` que el corpus ya tiene documentado.
"appstream", "libxmlb", "curl", "openssl", "libxml2-shared", "libyaml-shared", "zstd",
# `desktop-file-utils` por `update-desktop-database`: su `gnome.post_install()` lo pide como
# PROGRAMA y meson corta en el configure si no está (no es una librería que se enlace). La receta
# ya existe y encima está en el perfil desde el 2026-09-09.
"desktop-file-utils"]
+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.