Skip to main content

index

A modern GTK4/libadwaita system update frontend for Bluefin and Universal Blue

Finupdate provides a graphical interface for running system updates on Bluefin and Universal Blue systems. It orchestrates bootc, flatpak, brew, and distrobox directly β€” no uupd required. It's the first app in the Bluefin utility suite and serves as a reference implementation for future apps.

GNOME 47+ Rust License: MIT

Features​

  • One-click system updates β€” orchestrates bootc, flatpak, brew, and distrobox via a single pkexec elevation
  • Live log streaming β€” real-time stdout/stderr from each update module
  • Elapsed timer β€” shows how long the update has been running
  • Copy log β€” clipboard integration for sharing output
  • Cancel support β€” gracefully cancel a running update
  • Desktop notifications β€” GNotification when update completes/fails
  • Reboot prompt β€” confirmation dialog to restart after updates
  • Window close guard β€” prevents accidental close during active updates
  • Last update time β€” shows when uupd last ran successfully
  • Keyboard shortcuts β€” Ctrl+Q (quit), Ctrl+? (shortcuts window)
  • About dialog β€” accessible via hamburger menu
  • Flatpak sandbox aware β€” uses flatpak-spawn --host when sandboxed
  • Dark mode β€” automatic via libadwaita (follows system preference)
  • GNOME HIG compliant β€” symbolic icons, proper spacing, accessibility

Screenshots​

The app has four states:

IdleUpdatingCompleteError
Status page with "Check for Updates" buttonProgress bar + live log + timerSuccess page with reboot optionError page with retry

Requirements​

Runtime​

  • GTK 4.16+ (GNOME 47+)
  • libadwaita 1.7+
  • bootc or rpm-ostree on the host system
  • flatpak, brew, distrobox β€” optional; each module is skipped if the tool is absent
  • uupd β€” optional; if present, enables the "Automatic background updates" toggle in Preferences

Build​

  • Rust 1.85+ (edition 2024)
  • Meson 0.59+
  • GTK4 and libadwaita development headers

Installing​

Released builds are published to the TunaOS Flatpak remote for x86_64 and aarch64:

flatpak remote-add --if-not-exists tuna-os https://tunaos.org/flatpak/tuna-os.flatpakrepo
flatpak install tuna-os org.tunaos.finupdate

The remote is an OCI index backed by ghcr.io/tuna-os/finupdate; see tuna-os/flatpak-index. Builds are pushed by .github/workflows/publish-flatpak.yml on every push to main.

Building​

# One-time: install the GNOME SDK and Rust extension
flatpak install flathub org.gnome.Sdk//50 org.gnome.Platform//50
flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//25.08
flatpak install flathub org.flatpak.Builder

# Build and install locally
flatpak run org.flatpak.Builder --user --install --force-clean _flatpak \
build-aux/org.tunaos.finupdate.Devel.json

# Run
flatpak run org.tunaos.finupdate.Devel

Option B: Native Meson build​

Requires GTK4 and libadwaita dev packages installed:

# Fedora/Bluefin:
sudo dnf install gtk4-devel libadwaita-devel meson cargo

# Build
meson setup _build
meson compile -C _build

# Run
./_build/src/finupdate

Option C: Cargo only (dev iteration)​

If you have GTK4/libadwaita headers available (e.g., in a devcontainer):

cargo build # Debug
cargo build --release # Release
./target/release/finupdate

Development​

Architecture​

src/
β”œβ”€β”€ main.rs # Entry: logging + relm4 app launch
β”œβ”€β”€ config.rs # Build-time constants (APP_ID, VERSION)
β”œβ”€β”€ config.rs.in # Meson template β†’ config.rs
β”œβ”€β”€ app.rs # Top-level component (window, state machine, actions)
β”œβ”€β”€ update_worker.rs # Async subprocess (tokio + mpsc streaming)
└── ui/
β”œβ”€β”€ mod.rs # Module declarations
β”œβ”€β”€ status_view.rs # State-driven content switcher (gtk::Stack)
└── log_view.rs # Scrollable monospace text output

State Machine​

Idle ──[StartUpdate]──→ Updating ──[Complete]──→ Complete ──[Dismiss]──→ Idle
β”‚ ↑
└──────[Error]──→ Error ──────[Retry/Dismiss]β”€β”€β”˜
β”‚
└──────[Cancel]──→ Idle

Component Tree​

App (AdwApplicationWindow)
β”œβ”€β”€ AdwToolbarView
β”‚ β”œβ”€β”€ AdwHeaderBar (menu button + cancel button)
β”‚ └── AdwToastOverlay (transient notifications)
β”‚ └── StatusView (gtk::Stack)
β”‚ β”œβ”€β”€ "idle" β†’ AdwStatusPage
β”‚ β”œβ”€β”€ "updating" β†’ AdwToastOverlay
β”‚ β”‚ └── ProgressBar + LogView + BottomBar
β”‚ β”œβ”€β”€ "complete" β†’ AdwStatusPage
β”‚ └── "error" β†’ AdwStatusPage
└── LogView (gtk::TextView in ScrolledWindow)

Key Design Decisions​

  1. relm4 over raw gtk4-rs β€” Component model with message passing prevents callback spaghetti
  2. Tokio in a separate thread β€” GTK owns the main thread; async I/O needs its own runtime
  3. mpsc channels (not callbacks) β€” Decouples worker from UI; enables isolated unit testing
  4. gtk::Stack (not show/hide) β€” Built-in crossfade transitions, no manual visibility management
  5. Imperative widget construction β€” Some complex widgets built in init() when the view! macro can't express them

Flatpak Sandbox Notes​

When running in Flatpak, the app uses flatpak-spawn --host to execute commands on the host:

  • Requires --talk-name=org.freedesktop.Flatpak in Flatpak manifest
  • Detection: checks for /.flatpak-info file
  • All host commands (uupd, systemctl) are automatically wrapped

Environment Variables​

VariableEffect
RUST_LOG=finupdate=debugEnable debug logging
RUST_LOG=traceFull trace output
GTK_DEBUG=interactiveGTK Inspector

Contributing​

See CONTRIBUTING.md for development workflow and guidelines.

Reusable Patterns​

See PATTERNS.md for documented architectural patterns that should be used by all future Bluefin utility apps.

License​

MIT β€” see Cargo.toml

  • Project Bluefin β€” the desktop OS this is built for
  • GNOME HIG β€” the design guidelines we follow
  • uupd β€” optional host daemon; if installed, its timer can be toggled from Preferences