GNOME Shell extension¶
The Meta Package Manager project maintains a GNOME Shell extension.
A top bar indicator lists the outdated packages reported by mpm outdated across every package manager on the system, and lets you upgrade them one by one or per manager. It is one of the two desktop menus the project maintains, and that page holds what the two have in common.
The extension looks for mpm on the session PATH, then in well-known locations (~/.local/bin, /usr/local/bin, Linuxbrew), and a custom launcher (like uv run mpm) can be configured in its settings. mpm 6.4.0 or newer is required.
Requirements¶
GNOME Shell
46to50.The
mpmCLI,6.4.0or newer, reachable from the GNOME session.For upgrades run in a terminal: any of
xdg-terminal-exec, Ptyxis, Console (kgx) or GNOME Terminal, or a custom terminal command set in the extension settings.
Installation¶
From extensions.gnome.org¶
Todo
The extension is not yet published on extensions.gnome.org. Once it lands there, it will be installable with one click from the site. Until then, use one of the methods below.
From a release zip¶
Every GitHub release carries the packed extension as a mpm-gnome-shell-extension.zip asset, next to the mpm binaries. Its provenance is attested, so you can verify it was built by this project’s release pipeline before installing:
$ gh attestation verify mpm-gnome-shell-extension.zip --repo kdeldycke/meta-package-manager --signer-repo kdeldycke/repomatic
Then install it:
$ gnome-extensions install --force mpm-gnome-shell-extension.zip
Important
A running GNOME Shell does not pick up a freshly installed extension, and gnome-extensions enable asks the shell rather than the disk. Run it too early and it answers Extension "mpm@kdeldycke.github.io" does not exist, however successful the install was. Restart the session first.
A Wayland session cannot restart the shell in place, so end the session and log back in:
$ gnome-session-quit --logout --no-prompt
An X11 session can restart the shell alone, from the Alt+F2 prompt: type r, then Enter.
Once the shell is back, enable the extension:
$ gnome-extensions enable [email protected]
State: ACTIVE confirms the shell loaded it:
$ gnome-extensions info [email protected]
[email protected]
Name: Meta Package Manager
(...)
Enabled: Yes
State: ACTIVE
Between releases, the bleeding-edge equivalent is produced on each extension change as a workflow artifact of tests-gnome-extension.yaml.
From a source checkout¶
$ git clone https://github.com/kdeldycke/meta-package-manager.git
$ cd ./meta-package-manager
$ glib-compile-schemas "gnome-shell/[email protected]/schemas/"
$ ln -snf "$(pwd)/gnome-shell/[email protected]" ~/.local/share/gnome-shell/extensions/
Restart the session as above, then enable it:
$ gnome-extensions enable [email protected]
Configuration¶
Settings live in the extension preferences window, also reachable from the indicator menu:
Setting |
Description |
Type |
Default |
|---|---|---|---|
|
Group each manager’s packages into a section of its own. |
Boolean |
|
|
Minutes between two package checks. |
Integer |
|
|
Seconds before the first check after login. |
Integer |
|
|
Seconds passed to |
Integer |
|
|
Custom |
String |
Empty |
|
Show the indicator even when everything is up to date. |
Boolean |
|
|
Show the outdated package count next to the icon. |
Boolean |
|
|
Desktop notification when new outdated packages appear. |
Boolean |
|
|
Run upgrades in a terminal window. |
Boolean |
|
|
Custom terminal emulator, empty to autodetect. |
String |
Empty |
|
Seconds before refreshing the list after an upgrade is started. |
Integer |
|
These settings only drive the menu layout and check cadence: everything else comes from mpm’s own configuration, as Desktop menus explains.
The About group at the foot of the window names two versions. The first is the extension’s own, compiled into its metadata.json. The second is the mpm release the extension resolved, with the command that answered it below. The two ship separately, so a bug report needs both.
Panel icons¶
The extension ships no state artwork. Each state names a stock symbolic icon, which the shell resolves against whichever icon theme is in force, recolors with the panel foreground, and lets a desktop theme restyle: an Ubuntu desktop draws Yaru’s rendering of these names, not Adwaita’s.
State |
Adwaita |
Yaru |
Icon name |
Menubar plugin |
Shown when |
|---|---|---|---|---|---|
Unknown |
|
Before the first check of the session. |
|||
Checking |
|
While a check is running. |
|||
Up to date |
|
📦✓ |
No selected manager reports an upgrade. |
||
Updates available |
|
🎁↑N |
Packages can be upgraded. Also marks each Upgrade all row. |
||
Error |
|
⚠️ ❗️ |
A check failed, or no runnable |
||
Manager failed |
|
⚠️ |
On the sub-menu header of a manager that reported errors. |
The two rendering columns are why the names matter more than any drawing: Yaru defines all six itself, so an Ubuntu desktop never draws the Adwaita ones. Both are shown from their upstream repositories, and neither is bundled here. Two of the Yaru previews point at a path other than the icon name: those names are symlinks in that theme, and raw.githubusercontent.com serves a symlink’s target as plain text rather than following it, so the cell renders nothing unless it names the file the link resolves to. To browse the rest, the full Adwaita and Yaru name-to-preview lists are worth a bookmark, as is the GNOME design team’s Icon Library. The preferences window carries the one piece of artwork the extension does ship, the project logo in its About row.
Screenshots¶
Both layouts, photographed from a real GNOME session driven by docs/gnome_screenshots_update.py and refreshed by docs-screenshots.yaml whenever the extension changes.
Each one is captured in both shell appearances, so the two are there to be compared rather than picked for you: the shell restyles its menu with the desktop’s light or dark preference, and the version diff has to keep its colors legible on both. The tabs are synchronized, so switching one switches the other.
The default flat layout lists every manager’s packages inline, under a header counting them.
The flatpak section also carries a manager error, which the menu renders as a monospace red line under the packages that did resolve:
With group-by-manager enabled, each manager gets a section that expands on a click, and the one that reported an error takes the dialog-warning-symbolic icon documented above:
Development workflow¶
The extension lives in the gnome-shell/ directory of the mpm repository and shares its version, release cycle and issue tracker.
Its logic is split in two: extension.js owns the widgetry while mpm.js is shell-free (it never imports resource:///org/gnome/shell/* modules), so the latter runs under a bare gjs interpreter:
$ gjs -m tests/gnome/run-tests.js
ok 1 - parseVersion nominal
(...)
Static invariants (metadata, GSettings schema, stylesheet and icon drift) are enforced by tests/test_gnome_extension.py in the regular Python test suite. The tests-gnome-extension.yaml workflow runs the gjs suite, checks the sources with shexli (the static analyzer extensions.gnome.org applies to every upload), packs the installable zip with gnome-extensions pack, and proves it installs with a gnome-extensions install round-trip.
Its eslint job holds the JavaScript to GNOME Shell’s own coding style, with the eslint-config-gnome rules declared by gnome-shell/eslint.config.mjs and pinned to the commit gnome-shell itself pins. No package.json or lockfile is committed: nobody would keep one refreshed, so the ESLint stack floats and a 7-day npm --min-release-age window gates the whole resolved tree, the same supply-chain guard mpm --cooldown applies to the packages mpm installs.
To exercise the extension in a real session, install it from your checkout (see above), then run a nested GNOME Shell so crashes and reloads stay contained:
$ dbus-run-session -- gnome-shell --nested --wayland
Logs are visible with:
$ journalctl --follow --output=cat /usr/bin/gnome-shell
Release process¶
The extension version is advertised through the version-name field of metadata.json, kept in lockstep with the mpm version by bump-my-version.
If the extension changed between releases, a fresh zip is uploaded to extensions.gnome.org for review. Reviews there are manual and can take a while.
Changelog¶
8.0.0.dev0(unreleased)Publish a
Desktop menuspage above the two frontends, carrying what they share: what a click runs, where their settings stop andmpm’s configuration starts, the version-diff colors and the bootstrap pair. Both keep their own URL.Breaking: rename the grouped-layout setting to
VAR_GROUP_BY_MANAGERin the plugin andgroup-by-managerin the extension. Only Xbar ever opened a real sub-menu; SwiftBar folds an accordion and the extension expands inline, so the old name described one host’s rendering rather than the setting. Any configuration setting the old name reverts to the default.Split a version diff at the same point in both frontends. The GNOME menu left the separator introducing the diverging token in the dimmed prefix, where the bar plugin colors it:
5.0.0~beta1-0ubuntu7highlighted0~beta1-0ubuntu7in one and.0~beta1-0ubuntu7in the other, and 17 of 21 sampled pairs disagreed.Stop coloring both versions in full when one is the other plus a whole new token.
14ubuntu6→14ubuntu6.1highlights.1alone, in place of every token including the ones that matched.Render each version of a package row as one string. The dimmed common prefix and its colored suffix are two labels, and the shell theme’s own row spacing held them apart:
5.0.0~beta1-0ubuntu7read as5.0. 0~beta1-0ubuntu7.Illustrate the extension’s page with screenshots of its menu, in both layouts and both shell appearances. Each one is captured from a real headless GNOME session by the new
docs-screenshots.yamlworkflow, which reshoots them whenever the extension changes.Publish the documentation at
https://mpm.run. Every absolute link inreadme.md, the benchmark tables, the packaging specs and both frontends’ metadata points at the new origin; the formerkdeldycke.github.ioURLs redirect to it.Render every panel state and menu marker with a stock symbolic icon (
software-update-available-symbolicand its neighbours), in place of four bundled SVGs and the 🆙 and ⚠️ emoji. The shell recolors a themed icon with the panel foreground and a desktop theme can restyle it, neither of which our own artwork allowed, and the emoji were a font glyph the shell could do nothing with.Mark a running check with a refresh icon in the panel and a greyed Checking… row, in place of the three-dot loading icon.
Report the resolved
mpmrelease and command in the preferences window, beside the extension’s own version.Track every signal through
connectObject()anddisconnectObject(), and drop the_destroyedflag the GNOME guidelines forbid. Teardown now hangs off the indicator actor itself, and a check still in flight learns it was disabled from its own cancellable.Require a cancellable in every
runCommand()call, and drop its watchdog the moment that cancellable fires rather than whenever the abandoned read settles. A run nobody could cancel left a main loop source alive pastdisable(), free to fire on a locked session.Run
shexli, the static analyzer extensions.gnome.org applies to every upload, as its own CI job. It exits0whatever it finds, so the gate reads the finding count out of its JSON report.Pack the extension from the commit being released. The zip attached to a release was built from the post-release version bump instead, so
v7.6.1shipped an extension advertising7.6.2.dev0as itsversion-name, the string extensions.gnome.org shows users.Bundle the license in the packed extension, which is distributed detached from the repository carrying it.
Offer
uv tool install --upgrade meta-package-managerwhen nompmis found, beside an entry opening the installation page for the systemsuvdoes not answer for.Restart the GNOME session before enabling the extension, in the documented installation steps.
gnome-extensions enableasks the running shell rather than the disk, so it answeredExtension "mpm@kdeldycke.github.io" does not existwhen run right after a successful install.Give each frontend page a release history of its own, built from the changelog entries scoped to it.
7.6.0(2026-08-10)Add a GNOME Shell extension (GNOME 46 to 50) mirroring the SwiftBar/Xbar plugin: a top bar indicator lists outdated packages per manager, and every menu action runs
mpmitself. Addresses #809.Attach the packed extension to every GitHub release as an attested
mpm-gnome-shell-extension.zipasset, built withgnome-extensions pack.Lint the extension and its gjs test runner with ESLint against GNOME Shell’s own
eslint-config-gnomeruleset, in a neweslintjob installing the stack behind a 7-daynpm --min-release-agecooldown.