Skip to main content

Contributing

Thank you for your interest in contributing to Finupdate and the Bluefin utility app ecosystem!

Development Setup​

Finupdate is designed for Bluefin and other immutable Fedora-based desktops. The recommended workflow uses toolbox for fast Rust iteration and flatpak-builder (via Flatpak) for full integration testing. This matches how GNOME apps are developed upstream.

1. Create a toolbox (one-time)​

toolbox create finupdate
toolbox enter finupdate

Inside the toolbox, install build dependencies:

sudo dnf install -y \
cargo rust \
gtk4-devel libadwaita-devel \
meson ninja-build \
pkg-config

2. Install Flatpak build tools (on host, one-time)​

flatpak install flathub org.flatpak.Builder
flatpak install flathub org.gnome.Sdk//50 org.gnome.Platform//50

Quick Build & Test Cycle​

# Fast cargo iteration β€” run inside toolbox:
toolbox run --container finupdate cargo build
toolbox run --container finupdate cargo check

# Full Flatpak build + install β€” run on host:
flatpak run org.flatpak.Builder --user --install --force-clean _flatpak \
build-aux/org.tunaos.finupdate.Devel.json

# Run the Flatpak:
flatpak run org.tunaos.finupdate.Devel

Or use the just recipes (see justfile):

just build # cargo check inside toolbox
just flatpak # full Flatpak build + install
just run # run the installed Flatpak

Notes​

  • Keep cargo build and flatpak-builder output directories separate: toolbox builds go to ./target/, Flatpak builds go to ./_flatpak/. They don't conflict.
  • flatpak-builder always builds inside the SDK sandbox β€” the toolbox is only for fast iteration.
  • If you switch between the two, no cleanup is needed.

Architecture Overview​

Read PATTERNS.md for the full architecture. Key principles:

  1. One component per file β€” each relm4 component lives in its own .rs file
  2. Messages over callbacks β€” all state changes happen through the relm4 message system
  3. Separate worker thread β€” async I/O (subprocess, network) runs on tokio in a background thread
  4. State machine at app level β€” the AppState enum drives all UI transitions

Code Style​

Rust​

  • Follow standard rustfmt formatting (default config)
  • Use tracing macros (tracing::info!, tracing::error!) not println!
  • All public items need doc comments (///)
  • Module-level doc comments (//!) explain the pattern, not just what the code does
  • Prefer explicit error messages over .unwrap() in production paths

GTK/Adwaita​

  • No hardcoded pixel values for spacing β€” use CSS classes (margin-12, etc.) or Adwaita defaults
  • No custom colors β€” rely on Adwaita style classes (suggested-action, destructive-action, dim-label)
  • Symbolic icons only β€” always use name-symbolic suffix
  • AdwStatusPage for states β€” idle, error, success, and empty states
  • AdwToast for transient feedback β€” "copied to clipboard", "update complete"

Accessibility​

  • Every icon-only button must have set_tooltip_text AND set_accessible_label
  • Keyboard navigation must work for all interactive elements
  • Test in both light and dark mode

Making Changes​

Adding a New Feature​

  1. Identify which component owns the feature (app.rs, status_view.rs, or new component)
  2. Add message variants to the appropriate Input/Output enum
  3. Implement the handler in update()
  4. Update the view! macro or init() if new widgets are needed
  5. Update PATTERNS.md if the feature introduces a new pattern

Adding a New Component​

# Create the file:
touch src/ui/my_component.rs

# Add to src/ui/mod.rs:
pub mod my_component;

Then implement using the #[relm4::component(pub)] macro. See log_view.rs for the simplest example or status_view.rs for a complex one.

Modifying the Flatpak Manifest​

The manifest is at build-aux/org.tunaos.finupdate.Devel.json. Key things:

  • New D-Bus permissions: add to finish-args β†’ --talk-name=...
  • New system access: add to finish-args β†’ --filesystem=...
  • SDK version changes: update both runtime-version and sdk-extensions version

Data Files​

All data files use Meson templates (.in suffix) with placeholder substitution:

  • @APP_ID@ β†’ the full application ID (includes .Devel suffix in dev builds)
  • @ICON@ β†’ the icon name (matches APP_ID)

If you add a new data file, register it in data/meson.build.

Commit Messages​

Follow conventional commits:

feat: add feature description
fix: what was broken and how it's fixed
build: build system changes
docs: documentation only
refactor: code changes that don't add features or fix bugs

Include the Co-authored-by trailer for AI-assisted commits.

Before Submitting a PR​

Run through the HIG compliance checklist in PATTERNS.md.

Quick sanity checks:

  • toolbox run --container finupdate cargo build compiles cleanly with no warnings
  • toolbox run --container finupdate cargo clippy passes
  • App launches and the new feature works visually
  • Dark mode looks correct
  • Keyboard navigation works
  • No hardcoded colors or pixel values

File Map​

FilePurpose
justfilejust recipes for common dev tasks
Cargo.tomlRust dependencies and metadata
meson.buildTop-level Meson build (deps, subdirs)
meson_options.txtBuild profile option (development/release)
src/main.rsEntry point (logging, app launch)
src/config.rsBuild-time constants
src/config.rs.inMeson template for config.rs
src/app.rsMain window component + state machine
src/update_worker.rsAsync subprocess worker (tokio)
src/ui/mod.rsUI module declarations
src/ui/status_view.rsState-driven content area (Stack)
src/ui/log_view.rsScrollable log output
src/ui/update_list.rsPer-module update cards with Nerd Mode
src/ui/preferences.rsPreferences dialog
src/settings.rsSettings persistence (XDG JSON)
src/meson.buildCargo build integration
data/meson.buildInstall desktop/metainfo/icons
data/*.desktop.inDesktop entry template
data/*.metainfo.xml.inAppStream metadata template
data/icons/*.svgApp icons (regular + symbolic)
build-aux/*.jsonFlatpak manifests (Devel + release)
build-aux/dist-vendor.shVendor cargo deps for meson dist
PATTERNS.mdReusable patterns for Bluefin apps
CONTRIBUTING.mdThis file

Getting Help​