Files
mesh-catalog/modules/flatpak/cmd/flatpak-tools/main.go
T
jochen 152ef9621d flatpak: the package, Flathub with it, and the installations as tools (hq to-be 42 phase 2.9)
The package ships Flathub in /usr/share/flatpak/remotes.d, so the module
declares no remote of its own and checks it instead. Eleven Go tools: list,
runtimes, remotes (naming the desktop's duplicate user Flathub), updates,
unused, disk usage, and install, remove, update and remove-unused, the
system installation's acts through sudo -n and the account's without.

uninstall --unused has no dry run, so flatpak_unused works it out from
flatpak's own answers: an application's runtime and SDK, the extension
points of what is used, and pins. Acts run as jobs inside the bundle,
because an install outlasts a call.
2026-10-04 13:02:23 +02:00

214 lines
7.2 KiB
Go

// The flatpak module's tools (novox/hq research 027/02, 026/05): the applications and runtimes in
// both installations, the remotes, pending updates, what nothing uses any more and the space it all
// takes; and installing, removing and updating. A Go bundle the node's runtime launches over stdio
// (ADR 0188, ADR 0193); it runs as the operator account. An act on the system installation goes
// through `sudo -n`; one on the account's own installation does not.
package main
import (
"fmt"
"os"
stdio "git.novox.be/novox/mesh-sdk/go"
)
var providedBy = map[string]string{
"flatpak": "the flatpak package, which this module installs",
"du": "the coreutils package",
}
func main() {
if err := stdio.Serve("", tools()); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
var installationArg = map[string]any{"type": "string", "enum": []string{"system", "user"},
"description": "the system installation (default), or the account's own"}
func installationOf(args map[string]any, def string) (string, error) {
i, err := optText(args, "installation", def)
if err != nil {
return "", err
}
if i == "" {
return "", nil
}
return i, oneOf("installation", i, "system", "user")
}
func refArg(args map[string]any, key string) (string, error) {
r, err := text(args, key)
if err != nil {
return "", err
}
return r, checkRef(r)
}
func tools() []stdio.Tool {
return []stdio.Tool{
{
Name: "flatpak_list",
Description: "The installed applications, in both installations or one: id, name, version, branch, origin, " +
"installation and size. (r)",
Input: map[string]any{"installation": installationArg},
Run: func(args map[string]any) (any, error) {
inst, err := installationOf(args, "")
if err != nil {
return nil, err
}
return List("app", inst)
},
},
{
Name: "flatpak_runtimes",
Description: "The installed runtimes and extensions, in both installations or one: id, branch, origin, installation and size. (r)",
Input: map[string]any{"installation": installationArg},
Run: func(args map[string]any) (any, error) {
inst, err := installationOf(args, "")
if err != nil {
return nil, err
}
return List("runtime", inst)
},
},
{
Name: "flatpak_remotes",
Description: "The remotes of both installations, with how many installed refs come from each, and findings: " +
"Flathub missing from the system installation, the same remote in both installations, a remote nothing " +
"is installed from. (r)",
Input: map[string]any{},
Run: func(map[string]any) (any, error) { return Remotes() },
},
{
Name: "flatpak_updates",
Description: "What an update would change: each ref with a newer commit on its remote. Asks the remotes, so it needs the network. (r)",
Input: map[string]any{"installation": installationArg},
Run: func(args map[string]any) (any, error) {
inst, err := installationOf(args, "")
if err != nil {
return nil, err
}
return Updates(inst)
},
},
{
Name: "flatpak_unused",
Description: "The runtimes and extensions no installed application needs any more, computed without changing " +
"anything: a runtime is used when an application names it as its runtime or SDK, when it fills an " +
"extension point of something used, or when it is pinned. With the space each takes. (r)",
Input: map[string]any{},
Run: func(map[string]any) (any, error) { return Unused() },
},
{
Name: "flatpak_disk_usage",
Description: "The space flatpak takes: each installation's directory on disk, and the applications and " +
"runtimes by size, largest first. (r)",
Input: map[string]any{},
Run: func(map[string]any) (any, error) { return DiskUsage() },
},
{
Name: "flatpak_install",
Description: "Install an application or runtime from a remote (default flathub), in the system installation " +
"(default) or the account's. Answers when finished, or after 18 s with a job to follow with flatpak_job. (a)",
Input: map[string]any{
"ref": map[string]any{"type": "string", "description": "an application id such as org.gimp.GIMP, or a full ref"},
"remote": map[string]any{"type": "string", "description": "the remote (default flathub)"},
"installation": installationArg,
},
Run: func(args map[string]any) (any, error) {
ref, err := refArg(args, "ref")
if err != nil {
return nil, err
}
remote, err := optText(args, "remote", "flathub")
if err != nil {
return nil, err
}
inst, err := installationOf(args, "system")
if err != nil {
return nil, err
}
return Install(ref, remote, inst)
},
},
{
Name: "flatpak_remove",
Description: "Uninstall an application or runtime, keeping its data unless delete_data is set. Answers when finished, or with a job to follow. (a)",
Input: map[string]any{
"ref": map[string]any{"type": "string", "description": "the application id or ref"},
"installation": installationArg,
"delete_data": map[string]any{"type": "boolean", "description": "remove the application's data in the home too"},
},
Run: func(args map[string]any) (any, error) {
ref, err := refArg(args, "ref")
if err != nil {
return nil, err
}
inst, err := installationOf(args, "system")
if err != nil {
return nil, err
}
del, err := optFlag(args, "delete_data", false)
if err != nil {
return nil, err
}
return Remove(ref, inst, del)
},
},
{
Name: "flatpak_update",
Description: "Update one ref, or everything in an installation when no ref is given. Answers when finished, or with a job to follow. (a)",
Input: map[string]any{
"ref": map[string]any{"type": "string", "description": "the application id or ref (default all)"},
"installation": installationArg,
},
Run: func(args map[string]any) (any, error) {
ref, err := optText(args, "ref", "")
if err != nil {
return nil, err
}
if ref != "" {
if err := checkRef(ref); err != nil {
return nil, err
}
}
inst, err := installationOf(args, "system")
if err != nil {
return nil, err
}
return Update(ref, inst)
},
},
{
Name: "flatpak_remove_unused",
Description: "Uninstall what flatpak itself finds unused in one installation (system by default). " +
"flatpak_unused shows what that is beforehand. Answers when finished, or with a job to follow. (a)",
Input: map[string]any{"installation": installationArg},
Run: func(args map[string]any) (any, error) {
inst, err := installationOf(args, "system")
if err != nil {
return nil, err
}
return RemoveUnused(inst)
},
},
{
Name: "flatpak_job",
Description: "A long act this module started: running or finished, its exit status and the end of its output. Without job, every act this process knows. (r)",
Input: map[string]any{"job": map[string]any{"type": "string", "description": "the job id an act answered"}},
Run: func(args map[string]any) (any, error) {
id, err := optText(args, "job", "")
if err != nil {
return nil, err
}
if id == "" {
return map[string]any{"jobs": listJobs()}, nil
}
return jobByID(id)
},
},
}
}