sysupdate(flatpak): list updates when one needs an uninstalled runtime

Fixes #3950
Port 2.0
This commit is contained in:
purian23 committed 2026-10-26 22:56:23 -04:00
1 parent 28d5d56028
commit a7bd6a8e06
2 files changed
+57 -8

No files matched your search

@@ -3,6 +3,7 @@ package sysupdate
import (
"context"
"errors"
"fmt"
"os/exec"
"strings"
)
@@ -21,18 +22,21 @@ func (flatpakBackend) RunsInTerminal() bool { return false }
func (flatpakBackend) IsAvailable(_ context.Context) bool { return commandExists("flatpak") }
func (flatpakBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
// Run `flatpak update`
cmd := exec.CommandContext(ctx, "flatpak", "update")
// Without --no-deps an update needing an uninstalled runtime prompts first, and declining aborts before the table prints.
cmd := exec.CommandContext(ctx, "flatpak", "update", "--no-deps")
cmd.Stdin = strings.NewReader("n\nn\n") // decline up to 2 installation prompts
out, err := cmd.Output()
if err != nil {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok && exitErr.ExitCode() == 1 && len(out) > 0 {
} else if len(out) == 0 {
return nil, err
pkgs := parseFlatpakUpdateOutput(string(out), flatpakInstalled(ctx))
// Declining the final prompt also exits non-zero, so only a failure without a table is an error.
if err != nil && len(pkgs) == 0 {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
if msg := lastNonEmptyLine(string(exitErr.Stderr)); msg != "" {
return nil, fmt.Errorf("flatpak update: %s", msg)
}
}
return nil, err
}
installed := flatpakInstalled(ctx)
return parseFlatpakUpdateOutput(string(out), installed), nil
return pkgs, nil
}
type flatpakInstalledEntry struct {
@@ -2,6 +2,7 @@ package sysupdate
import (
"reflect"
"strings"
"testing"
)
@@ -100,3 +101,47 @@ func TestParseFlatpakUpdateOutput(t *testing.T) {
})
}
}
func TestFlatpakCheckUpdates(t *testing.T) {
const runtimePrompt = `[ "$1" = list ] && exit 0
for a in "$@"; do
if [ "$a" = --no-deps ]; then
printf ' 1.\tVesktop\tdev.vencord.Vesktop\tstable\tu\tflathub\t< 130 MB\n\nProceed with these changes? [Y/n]: '
exit 1
fi
done
printf 'Required runtime for dev.vencord.Vesktop/x86_64/stable found in remote flathub\nDo you want to install it? [Y/n]: '
echo 'error: The application dev.vencord.Vesktop/x86_64/stable requires the runtime org.freedesktop.Platform/x86_64/26.08 which is not installed' >&2
exit 1`
for _, tt := range []struct {
name string
script string
wantRef string
wantErr string
}{
{name: "update needing a new runtime", script: runtimePrompt, wantRef: "dev.vencord.Vesktop//stable"},
{
name: "failure without a table",
script: "[ \"$1\" = list ] && exit 0\necho 'Looking for updates…'\necho 'error: Unable to load summary from remote flathub' >&2\nexit 1",
wantErr: "Unable to load summary",
},
} {
t.Run(tt.name, func(t *testing.T) {
dir := t.TempDir()
t.Setenv("PATH", dir)
writeUpdateExecutable(t, dir, "flatpak", tt.script)
pkgs, err := flatpakBackend{}.CheckUpdates(t.Context())
if tt.wantErr != "" {
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("err = %v, want %q (a failed check must not read as up to date)", err, tt.wantErr)
}
return
}
if err != nil || len(pkgs) != 1 || pkgs[0].Ref != tt.wantRef {
t.Errorf("pkgs = %#v, err = %v, want %s", pkgs, err, tt.wantRef)
}
})
}
}