Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

updates

Pending package upgrades (pacman, optionally AUR), install progress and whether a reboot is due.

mantle.updates:configure({ interval = 3600 })

button {
    on_click = function() mantle.updates:check() end,
    children = {
        text {
            content = mantle.updates:map(function(updates)
                if updates == nil or updates.checking then
                    return "…"
                end
                return updates.count > 0 and (updates.count .. " updates") or "up to date"
            end),
        },
    },
}

State

mantle.updates:get() returns UpdatesState, nil before the first push. A field marked ? may be absent.

mantle.updates’s payload.

FieldTypeDescription
aur_error?stringWhy AUR packages are missing: the last check’s AUR query failed, or aur is on with no aur_helper; nil otherwise. packages still holds the repos’ answer.
aur_helper?stringAUR helper found at start, "paru" or "yay", or nil; used only once configure sets aur.
check_error?stringWhy the last check failed, or nil after a success. A check never modifies the system.
checkingbooleanA check is running.
consecutive_check_failuresintegerCheck failures in a row; a success resets it to 0.
countintegerAlways #packages.
install_current_packagestringPackage being installed; empty before the first step line.
install_current_stepinteger1-based number of the package being installed, e.g. pacman’s (2/5); 0 before the first.
install_error?stringWhy the package manager could not be run or waited on, or nil. Its own failures are install_exit_code.
install_exit_code?integerPackage manager’s exit code for the last install (0 success); nil while running, before one, or when a signal killed it.
install_finished_at?integerUnix seconds when the last install’s process ended, whatever its status; nil while running, before one, or when it failed to spawn.
install_logstring[]The last 200 lines of install output, stdout and stderr interleaved, newest last; cleared when an install starts.
install_total_stepsintegerPackages in the transaction; 0 until the first step line, so draw progress as indeterminate.
installingbooleanAn install is running; the install_* fields describe the latest run.
last_successful_check?integerUnix seconds of the last successful check (or the checked_at seed), else nil.
package_manager?stringPackage manager, e.g. "pacman", from the first push, which comes at start; nil when none is supported, and then every action is ignored.
packagesUpdateCandidate[]Pending upgrades. A failed check keeps the last good list.
reboot_requiredboolean/run/mantle-reboot-required exists, watched live. Mantle never writes it; anything you set up may, a pacman hook for example, and /run empties on reboot.

UpdateCandidate

One installed package with a newer version.

FieldTypeDescription
download_sizeintegerBytes to fetch; 0 when already cached.
installed_sizeintegerBytes the new version occupies installed; not a delta.
namestringPackage name.
new_versionstringVersion on offer.
old_versionstringInstalled version.
repository?stringSource repository, e.g. "extra" or "aur"; empty in a seeded list that lacks it.

Actions

Call each as mantle.updates:<action>(arguments...); ? marks an argument you may omit.

ActionArgumentsDescription
checkChecks for upgrades now, even when dormant; ignored while checking.
configureconfig: UpdatesConfigureSets the check schedule and AUR use, and seeds a remembered check.
installRuns a full upgrade through pkexec: pacman -Syu --noconfirm, or aur_helper when aur is on; dnf upgrade -y --refresh; apt-get update then apt-get upgrade --with-new-pkgs. Ignored while installing. Does not recheck afterwards.

UpdatesConfigure

configure’s table. One wrong-typed key drops the whole call.

FieldTypeDescription
aur?booleanAlso check the AUR and install through aur_helper. Sends every foreign package name to aur.archlinux.org and builds without PKGBUILD review.
checked_at?integerPersisted Unix seconds of the last successful check. Seeds last_successful_check only while that is nil, so a restart need not recheck at once.
intervalintegerSeconds between scheduled checks, the first at once unless last_successful_check is younger; 0 checks only on check.
packages?UpdateCandidate[]Persisted packages from that check, seeded on the same terms; ignored without checked_at.

Backend

The first manager that owns / wins, each on PATH at start: pacman with packages in /var/lib/pacman/local, else apt-get with packages in /var/lib/dpkg/status, else dnf. With none, package_manager is nil and every action is ignored. The dnf and apt backends are verified in containers only, untested on a Fedora or Ubuntu install for now.

Partpacman (Arch)dnf (Fedora)apt (Debian, Ubuntu)
Checkcurl fetches each repo database pacman-conf lists into $XDG_RUNTIME_DIR/mantle/pacman, skipping one the mirror reports unchanged. pacman -Qu, -Sp and -Si read it against /var/lib/pacman/localdnf repoquery --refresh --upgrades, then --installed for old_version; metadata goes to your cache (~/.cache/libdnf5)apt-get update into $XDG_RUNTIME_DIR/mantle/apt with its hooks cleared, then apt-get -s upgrade --with-new-pkgs and apt-cache show for sizes
AURWith aur = true, pacman -Qm names the foreign packages, one curl POST to the AUR RPC (30 s timeout) covers them, and vercmp orders the versions. aur_helper is paru, else yayNone: aur_helper is nil, aur only sets aur_errorSame as dnf
Installenv LC_ALL=C pkexec pacman -Syu --noconfirm, or env LC_ALL=C <aur_helper> -Syu --noconfirm --sudo pkexec with aur onpkexec env LC_ALL=C dnf upgrade -y --refreshpkexec env LC_ALL=C DEBIAN_FRONTEND=noninteractive sh -c "apt-get update && apt-get -y ... upgrade --with-new-pkgs"; a changed config file keeps your copy
Progresspacman’s (2/5) upgrading name lines; nothing while downloading, since pacman draws no progress without a tty[ 4/12] Upgrading name-... lines. The total also counts removals of the old versions and, on dnf5, two setup steps; dnf5 cuts a long name to fit its columnEach Setting up name line against the 4 upgraded, 1 newly installed summary; 0 while downloading

Every check runs as you and leaves the system’s package databases untouched. pkexec asks the session’s polkit agent; the polkit rule makes that one approval per run for wheel users of pacman, and dnf and apt install in one pkexec call, so one prompt. reboot_required mirrors /run/mantle-reboot-required through an inotify watch on /run, whatever the manager.

What packages holds differs by manager:

Fieldpacmandnfapt
repositoryThe repo, or "aur"The repo idThe suites carrying the new version, comma-joined: "noble-updates,noble-security"
download_size0 once cachedThe package sizeThe .deb’s size even when cached
installed_sizeFrom -Si, rounded to ~5 KiBExactExact to the KiB
Left outIgnorePkg entriesexcludepkgs entriesNew dependencies the upgrade pulls in; a package whose upgrade would remove another, and Ubuntu’s phased updates not yet offered, are held back, listed and installed by neither. sudo apt full-upgrade takes those
DuplicatesNoneOne entry per architecture when two of one package upgradeNone

How do I…

Remember the last check across restarts

Save each successful check in a persistent_table, and send configure only once the file has loaded, so its seed lands before the first scheduled check.

local dir = (os.getenv("HOME") or "") .. "/.local/state/myshell"
local cache = persistent_table { path = dir, name = "updates.json" }
local file = dir .. "/updates.json"

mantle.storage:on_change(function(storage, previous)
    local saved = storage.files[file]
    if saved and not (previous and previous.files[file]) then
        mantle.updates:configure({
            interval = 3600,
            checked_at = saved.checked_at,
            packages = saved.packages,
        })
    end
end)

mantle.updates:on_change(function(updates, previous)
    local at = updates.last_successful_check
    if at and at ~= (previous and previous.last_successful_check) then
        cache:set("checked_at", at)
        cache:set("packages", updates.packages)
    end
end)

return text {
    content = mantle.updates:map(function(updates)
        return updates and tostring(updates.count) or ""
    end),
}

Gotchas

TrapFix
packages still lists everything after a successful installinstall does not recheck. Invoke check from on_change when installing falls with install_exit_code == 0
install ends at once with install_exit_code 127No polkit agent answered pkexec. Read mantle.polkit and draw its prompt (polkit), or run another agent. 126 means the prompt was dismissed

Source: supervisor/src/capabilities/updates/