profile: [flatpak] provider — declarative flatpak apps #7

Merged
chad merged 1 commit from profile/flatpak-provider into master 2026-10-15 11:57:41 -04:00
Collaborator

The first built-in provider: declarative flatpak apps via a [flatpak] section in profile.toml.

[flatpak]
installation = "user"            # default; "system" also supported
remotes.flathub = "https://dl.flathub.org/repo/flathub.flatpakrepo"
apps = ["org.gnome.Calculator", "flathub:com.google.Chrome"]   # "remote:id" pins a remote
# prune = false                  # default: unmanaged extras are reported, not removed

Semantics (new cli/src/flatpak.rs):

  • Generations record the declared set (Generation.flatpak), which feeds the generation id. Editing the list makes a new generation, and generations switch re-converges to the old list. Profiles without [flatpak] keep their exact previous id: verified on think, the real profile still reports changes: false.
  • Ownership mirrors [[files]]. An installed app is uninstalled only when the previous generation declared it and the new one doesn't. Never-declared apps are extras: reported by plan and apply, removed only with prune = true. Ownership doesn't carry across installations, and removing [flatpak] entirely uninstalls only what was declared.
  • Convergent. Every apply, including the no-change path, reinstalls declared-but-missing apps and re-adds missing remotes, so a manual uninstall heals.
  • Order and failure handling. It runs last in apply, after packages, files and units: remotes, then installs one at a time (one bad id doesn't abort the rest), then uninstalls. Failures are collected and make apply exit non-zero, and re-running apply retries just the flatpaks.
  • Versions aren't pinned. flatpak owns updates, so rollback restores the declared set, not old builds.
  • plan uses only flatpak list / flatpak remotes, so it stays read-only. The JSON report gains flatpak: {installation, add_remotes, install, uninstall, extras}. A flatpak read error surfaces as an unresolved entry, so exit 1 still holds. The human output gets a flatpak (<installation> installation): section.
  • Validation before any work: reverse-DNS app ids, no duplicates, installation ∈ {user, system}, deny_unknown_fields.
  • Built-in only, no TOML-defined command providers (Decision #3). Rule of thumb: prefer [[files]] wherever a program supports layering or drop-ins (dconf is done that way in cn #72); use a provider only for state that lives in a database you can't layer.

Verified

  • cargo test --workspace: 135 pass (11 new: diff/ownership/prune/heal/cross-installation/id-stability/parse/validation).
  • End to end on think with an isolated FLATPAK_USER_DIR + HOME and the real flathub:
    • plan → apply installs Calculator (4s).
    • Re-apply is a no-op.
    • A manual uninstall shows in plan and is healed by apply.
    • A hand-installed Characters is reported as an extra and not removed.
    • Dropping Calculator from the profile uninstalls it and keeps Characters.
    • generations switch <gen1> reinstalls Calculator.
    • A bad id fails before any work.

Follow-ups: release v0.4.0, then the cn PR that adopts think's flatpaks.

The first built-in **provider**: declarative flatpak apps via a `[flatpak]` section in profile.toml. ```toml [flatpak] installation = "user" # default; "system" also supported remotes.flathub = "https://dl.flathub.org/repo/flathub.flatpakrepo" apps = ["org.gnome.Calculator", "flathub:com.google.Chrome"] # "remote:id" pins a remote # prune = false # default: unmanaged extras are reported, not removed ``` **Semantics** (new `cli/src/flatpak.rs`): - **Generations record the declared set** (`Generation.flatpak`), which feeds the generation id. Editing the list makes a new generation, and `generations switch` re-converges to the old list. Profiles *without* `[flatpak]` keep their exact previous id: verified on think, the real profile still reports `changes: false`. - **Ownership mirrors `[[files]]`.** An installed app is uninstalled only when the previous generation declared it and the new one doesn't. Never-declared apps are `extras`: reported by plan and apply, removed only with `prune = true`. Ownership doesn't carry across installations, and removing `[flatpak]` entirely uninstalls only what was declared. - **Convergent.** Every apply, including the no-change path, reinstalls declared-but-missing apps and re-adds missing remotes, so a manual uninstall heals. - **Order and failure handling.** It runs last in apply, after packages, files and units: remotes, then installs one at a time (one bad id doesn't abort the rest), then uninstalls. Failures are collected and make apply exit non-zero, and re-running apply retries just the flatpaks. - **Versions aren't pinned.** flatpak owns updates, so rollback restores the declared *set*, not old builds. - **`plan`** uses only `flatpak list` / `flatpak remotes`, so it stays read-only. The JSON report gains `flatpak: {installation, add_remotes, install, uninstall, extras}`. A flatpak read error surfaces as an `unresolved` entry, so exit 1 still holds. The human output gets a `flatpak (<installation> installation):` section. - **Validation before any work:** reverse-DNS app ids, no duplicates, `installation ∈ {user, system}`, `deny_unknown_fields`. - **Built-in only, no TOML-defined command providers** (Decision #3). Rule of thumb: prefer `[[files]]` wherever a program supports layering or drop-ins (dconf is done that way in cn #72); use a provider only for state that lives in a database you can't layer. **Verified** - `cargo test --workspace`: 135 pass (11 new: diff/ownership/prune/heal/cross-installation/id-stability/parse/validation). - End to end on think with an isolated `FLATPAK_USER_DIR` + HOME and the real flathub: - plan → apply installs Calculator (4s). - Re-apply is a no-op. - A manual uninstall shows in plan and is healed by apply. - A hand-installed Characters is reported as an extra and not removed. - Dropping Calculator from the profile uninstalls it and keeps Characters. - `generations switch <gen1>` reinstalls Calculator. - A bad id fails before any work. Follow-ups: release v0.4.0, then the cn PR that adopts think's flatpaks.
chad merged commit 47593d5a06 into master 2026-10-15 11:57:41 -04:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chad/bubblebox!7
No description provided.