System update tool. Supports apt, distrobox, dnf, flatpak, firmware, pacman, podman, python, snap, urpmi, zypper.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-11 18:49:00 -05:00
src 1.0.6: add generate-config subcommand to write default config with optional --force 2026-10-11 16:24:58 -05:00
supply-chain Add a cargo-vet supply-chain policy 2026-10-09 20:25:10 -05:00
.gitignore Harden child environments and cap output buffering, bump rev to 1.0.1 2026-10-09 17:59:26 -05:00
build.sh 1.0.6: GUI dropdown escalator picker; reordered default escalator search; password caching improvements; broadened prompt detection; batched privileged ops; version bump 2026-10-11 15:07:56 -05:00
Cargo.lock 1.0.6: GUI dropdown escalator picker; reordered default escalator search; password caching improvements; broadened prompt detection; batched privileged ops; version bump 2026-10-11 15:07:56 -05:00
Cargo.toml 1.0.6: GUI dropdown escalator picker; reordered default escalator search; password caching improvements; broadened prompt detection; batched privileged ops; version bump 2026-10-11 15:07:56 -05:00
CHANGELOG.md 1.0.6: add generate-config subcommand to write default config with optional --force 2026-10-11 16:24:58 -05:00
config.template 1.0.6: GUI dropdown escalator picker; reordered default escalator search; password caching improvements; broadened prompt detection; batched privileged ops; version bump 2026-10-11 15:07:56 -05:00
GPLv3.md Added GPL v3 licesnse file 2024-11-23 12:32:36 -06:00
LICENSE.md Fixed typo 2024-10-07 11:30:17 -05:00
README.md removed old verbage to future features. them features be here alredy. 2026-10-11 18:49:00 -05:00

osupdater

ABOUT

A rust application, both terminal and graphical, for figuring out what package manager(s) are present on your system and using them to update your software packages. Run it with no arguments (or name updaters on the command line) for the CLI; run osupdater gui for the window.

This is for my educational purposes of learning rust to do tasks that I have done successfully in either bash or C/C++. Feel free to snatch this up or not.

Take note that the parameters each update tool is invoked with are still hard coded. The config file described below chooses which tools run by default, not the flags they are given. To change those parameters, you will need to edit the source.

CONFIGURATION

Running osupdater with no arguments reads a config file to decide what to update. If no config file exists, every updater runs, which is the same as passing all.

The per-user location needs no root, so it is the easy one to set up:

mkdir -p ~/.config/osupdater
install -m 644 config.template ~/.config/osupdater/osupdater.conf

For a system-wide default covering every user, install it as root instead:

sudo install -m 644 config.template /etc/osupdater.conf

The two are searched in that order and the first one found wins; they are not merged. So if /etc/osupdater.conf exists, your personal file is ignored.

Then set the tools you do not want to false. Values are TOML booleans, so write true / false, not y / n. Anything you leave out stays enabled, so you only list what you are turning off:

[PackageManagers]
apt = true
dnf = true
pacman = false
urpmi = true
zypper = true

The available keys are apt, brew, distrobox, dnf, flatpak, firmware, pacman, podman, python, snap, urpmi and zypper. The [PackageManagers], [AlternateFormats], [Sandboxes], [Containers] and [Firmware] sections are organisational only, so a key may be listed under any of them. A misspelled key or section is reported as an error rather than silently ignored, since a typo would otherwise quietly re-enable something you meant to disable.

The separate [Privilege] section chooses how the steps that need root are run:

[Privilege]
escalator = "run0"   # sudo, run0, doas, pkexec, or su (su -c)

If escalator is left out, the first one found is used, in the order run0, sudo, doas, pkexec, su (where su is invoked as su -c). Unlike the tool keys, this applies to every run, including ones that name tools on the command line, since it is a per-system choice rather than a per-run one. If a configured escalator is not installed it is not silently swapped for another; osupdater tells you and continues with the tools that do not need root (brew, pip-review, distrobox, and the user-level flatpak/snap/podman steps). The same happens when no escalator is found at all.

The [Logging] section sets whether a per-run log file is written:

[Logging]
write_log = false

If write_log is left out (or true) a log is written for every run, matching the command line's default. Set it to false to turn logging off by default; --no-log still turns it off for a single run. The GUI's "Write a log file" checkbox reads this value when the window opens and writes it back with "Save as defaults".

The [Update] section controls the startup version check:

[Update]
check = false

If check is left out (or true), osupdater looks — at most once a day — for a newer release tag on the project's git remote and, when there is one, tells you and offers to run the install for you. Set it to false to turn the check off; --no-update-check skips it for a single run. The update is installed with cargo install --git https://git.schotty.com/andrew/osupdater.git --tag vX.Y.Z --locked --force, and the running copy keeps working until you restart it. The result is cached under the state directory (~/.local/state/osupdater/), so a normal run does not reach the network every time.

osupdater never rewrites a config file on its own, except to recover one it cannot parse. A file that predates the current format — for example the pre-0.10 y / n booleans — is migrated in place to true / false; if it still cannot be parsed it is replaced with the generated defaults. Either way the previous contents are kept beside it as <file>.bak, and osupdater says what it did and asks you to recheck your settings. The system-wide /etc/osupdater.conf is never rewritten, since a regular user cannot own it; a bad one there is reported and left for you to fix.

Naming tools on the command line always overrides the config file:

Command Effect
osupdater run whatever the config file enables
osupdater apt dnf run only apt and dnf, ignoring the config
osupdater all run every updater, ignoring the config

osupdater must be run as a regular user, not via sudo; it calls sudo (or the configured escalator) itself for the steps that need root.

GRAPHICAL INTERFACE

osupdater gui opens a window with a checkbox per updater and an output pane:

osupdater gui

The checkboxes start out matching what the config enables, exactly like a bare osupdater run. "Run defaults" runs whatever the config enables; "Run selected" runs only the ticked tools. "Save as defaults" writes the current ticked selection, the escalator menu choice, and the "Write a log file" checkbox into the per-user config at ~/.config/osupdater/osupdater.conf (and refuses to touch a system-wide file), so a future run -- in the GUI or on the command line -- starts from that exact set. If /etc/osupdater.conf exists it still takes precedence, and the window points that out after saving. A run is executed by the same code paths as the CLI -- the same config validation, escalator resolution, per-tool security checks, hardened child environments, and per-run logging -- with the output streamed into the window instead of the terminal. Runs keep writing the per-run logs described below; untick "Write a log file" or pass --no-log to skip them.

The privilege-escalator picker lists every escalator osupdater supports. The one that is not installed on this machine is shown dimmed with a "(not installed)" note instead of a button, so the window says both which escalators are supported and which are usable right now; install one and reopen the window to select it.

When a run reaches a step that needs root, the escalator (sudo, run0, doas, pkexec, or su) does not prompt on the terminal osupdater was launched from. Instead the GUI opens a password dialog. The escalator runs on a pseudo-terminal that the window controls: the moment it prints its "assword" prompt, the dialogs appears, and the password is typed straight into that pty when you confirm it. Nothing about the password ever lands in an argument, an environment variable, or a file. A wrong password is detected (the escalator prompts again) and the dialog reopens for a retry; cancelling the dialog skips the remaining privilege steps for that run. With network/sudo caching, an escalator may not prompt at all, in which case no dialog is shown.

The window is part of the default build. It is the gui cargo feature; to build a lean command-line-only binary, disable it:

cargo build --release --no-default-features

LOGGING

Every run writes its own log to ~/.local/state/osupdater/osupdater-YYYYMMDD-NN.log, or under $XDG_STATE_HOME/osupdater/ when that is set. NN is the run number within that day, so five runs on 30 September 2026 give osupdater-20260930-01.log through osupdater-20260930-05.log. The log records osupdater's own messages and the full output of every updater it runs, so it shows what actually changed rather than only whether it succeeded.

One file per run means a run's record stays intact and is never interleaved with the next one:

ls -t ~/.local/state/osupdater/*.log | head -1

Each line is stamped with a UTC timestamp, which is what makes the log searchable after the fact:

2026-09-30T23:53:45Z === osupdater 0.9.6 started at 2026-09-30T23:53:45Z (apt dnf) ===
2026-09-30T23:53:46Z === updater: apt ===
2026-09-30T23:53:46Z === osupdater: running /usr/bin/apt
2026-09-30T23:53:51Z Setting up libcurl4 8.5.0-2 ...
2026-09-30T23:53:52Z === updater: dnf ===
2026-09-30T23:59:03Z === osupdater 0.9.6 finished success after 324s ===

To find when a given package was last touched, across every run:

grep -i curl ~/.local/state/osupdater/*.log

To see the updaters and outcomes of just the most recent run, read that run's file. Its started line names the version, the updaters, and the time:

tail -n 1000 "$(ls -t ~/.local/state/osupdater/*.log | head -1)" | grep -E '=== updater:|finished'

Notes on the format: each run is bracketed by started and finished lines carrying the version, the updaters selected, and the outcome with the elapsed time. === updater: <name> === marks which updater owns the lines that follow it. Output the child wrote to stderr is tagged [<program> stderr], so a warning can be told apart from normal output. The log holds no colour codes, so it stays greppable.

Logs are created mode 0600, since update output can name local paths and hosts. The 100 most recent are kept and older ones are deleted on startup, so the directory cannot grow without bound; files that are not named like a log are left alone. Pass --no-log to skip writing one for a single run:

osupdater --no-log

FILES

File Function
./src rust source
./config.template sample config enabling every updater
./build.sh bash script to build the source

TESTED ENVIRONMENTS

Currently when I start work on distributions that I do not have a live environment to use or a VM available, I will use distrobox. Then, as time permits, I use VMs to test as a next best thing to a live environment.

Consider distrobox tests as the lowest grade pass, then VMs better and native tests best.

Trusted contributors will count as a passed/failed contributor test.

DISTRIBUTION PERSONALLY TESTED CONTRIBUTOR TESTED
Arch yes no
Debian distrobox yes - tswamp
OpenMandriva / Mageia no no
RHEL / Fedora yes yes
SuSE/ OpenSuSE yes no
Ubuntu yes no
CONTAINER TYPE PERSONALLY TESTED CONTRIBUTOR TESTED
Distrobox yes yes
Flatpak yes yes
Podman yes yes
Snap yes no
OTHER TOOLS PERSONALLY TESTED CONTRIBUTOR TESTED
Brew yes no
Python via pip-review yes yes
Firmware via fwupdmgr yes yes

COMPILATION

In the repo there is a build script that will build things for you. To do so follow the instructions below to setup your build environment. You can also use cargo to build and install as well, but again, the build environment will need to be setup.

BUILD ENVIRONMENT

To compile the application you will need to have rust and cargo installed.

ARCH & DERIVATIVES

sudo pacman -Sy rust
rustup default stable

DEBIAN & DERIVATIVES

The official documentation from Debian states one can either install the apt packages or use the official rustup installer.

Using apt packages for Debian 12+ (my used and tested method):

sudo apt update -y
sudo apt upgrade -y
sudo apt install rustc cargo rustfmt -y

Using rustup (required for Debian 11.x and older):

sudo apt update -y
sudo apt upgrade -y
sudo apt install curl build-essential gcc make -y
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

The final command above will pull in and run the installer. Follow the prompts on screen.

OPENMANDRIVA/MAGEIA (UNTESTED)

sudo urpmi update
sudo urpmi install gcc curl make
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

RHEL/FEDORA

sudo dnf install rust cargo -y

SUSE/OPENSUSE

The official documentation from OpenSuSE.org states to use rustup, so here it is:

sudo zypper install rustup -y && rustup toolchain install stable

OFFICIAL INSTALLATION METHOD -- VIA CARGO

As of right now I have not published anything to crates.io or anything similar. So as of right now you can do a:

cargo install --git https://git.schotty.com/andrew/osupdater.git

This will download the repo, compile, and install the binary in ~/.cargo/bin/, so ensure if you use this method you have that path in your $PATH environment variable.

ALTERNATE INSTALLATION METHOD --GETTING SOURCE AND COMPILATION

To downloaded and then compile the program is done as follows:

git clone https://git.schotty.com/andrew/osupdater
cd osupdater
./build.sh

This should just build the application for you, providing you setup rust correctly. If you have issues compiling, double check that the toolchain is installed correctly for your platform. If the above instructions are insufficient, file a bug report and I can update them to include any missing instructions and/or caveats.

SCOPE & USAGE

CURRENT SCOPE AND LIMITATIONS

The current scope and intended use of the tool is:

  • Not to be run as root -- be a regular user with sudo rights to run the package manager tools.
  • The testing has been done in a limited fashion. I have several Workstations, Laptops, VMs, VPS's, and contributors that are able to validate things. The testing matrix above has the details, play in a VM if your platform is untested.
  • My test pecking order is to test on real hardware, else in distrobox or a VM , else a trusted contributor report..
  • If you have any doubts as to what the hell is going on under the hood, look at the source. If the parameters are too conservative/aggressive -- edit the source or wait until I get custom parameters implemented.

RUNNING OSUPDATE

Running the application is as simple as executing the ouput binary located at osupdater/target/release/osupdater

Depending on tastes, you can copy said binary to ~/.local/bin or any other location that is in your PATH.

The application will output whatever it does, and ignore any package managers that are not present on your system.

  • To run all the tools:
osupdater

or

osupdater all
  • To select one tool:
osupdater dnf
  • To run several tools:
osupdater dnf flatpak distrobox
  • To load the graphical tool:
osupdater gui

AVAILABLE UPDATER TOOLS SUPPORTED

  • Detect and run everything found

    • all
  • Package managers

    • apt
    • brew
    • dnf
    • pacman
    • urpmi
    • zypper
  • Application sandboxes

    • flatpak
    • python
    • snap
  • Containers

    • distrobox
    • podman
  • Firmware

    • firmware (via fwupdmgr)

NOTES

During testing via distrobox, pip-review will fail if the host python version is not the same as the container. The tool will find the pip-review package, but be unable to execute due to a version mismatch.

DISTROBOX CONTAINERS USED FOR TESTING

USING DISTROBOX

Starting container:

distrobox-create --name CONTAINER_NAME --image CONTAINER_URI
distrobox-enter CONTAINER_NAME

Entering container:

distrobox enter CONTAINER_NAME

Stopping container:

podman stop CONTAINER_NAME
podman rm CONTAINERF_NAME

CONTAINER URI'S

Arch:

  • quay.io/toolbx/arch-toolbox:latest

Debian:

  • quay.io/toolbx-images/debian-toolbox:latest
  • quay.io/toolbx/ubuntu-toolbox:latest

RHEL/Fedora:

  • quay.io/toolbx-images/almalinux-toolbox:latest
  • registry.fedoraproject.org/fedora-toolbox:40
  • quay.io/toolbx-images/rhel-toolbox:latest

SuSE/OpenSuSE:

  • registry.opensuse.org/opensuse/distrobox:latest