1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-06 04:22:11 -04:00

refactor(sysupdate): Streamline DMS Updater command handling

This commit is contained in:
purian23
2026-05-05 15:16:36 -04:00
parent b209827f38
commit d49c49cd99
13 changed files with 405 additions and 99 deletions

View File

@@ -100,29 +100,13 @@ func runSystemUpdateCheck() {
}
stopSpin := startSpinner("Checking for updates… ")
type backendResult struct {
ID string `json:"id"`
Display string `json:"displayName"`
Packages []sysupdate.Package `json:"packages"`
}
var results []backendResult
var allPkgs []sysupdate.Package
var firstErr error
for _, b := range backends {
pkgs, err := b.CheckUpdates(ctx)
if err != nil && firstErr == nil {
firstErr = fmt.Errorf("%s: %w", b.ID(), err)
}
results = append(results, backendResult{ID: b.ID(), Display: b.DisplayName(), Packages: pkgs})
allPkgs = append(allPkgs, pkgs...)
}
allPkgs, firstErr := collectUpdates(ctx, backends)
stopSpin()
allPkgs = filterUpdateTargets(allPkgs)
if sysUpdateJSON {
out, _ := json.MarshalIndent(map[string]any{
"backends": results,
"backends": backendResults(backends, allPkgs),
"packages": allPkgs,
"error": errOrEmpty(firstErr),
"count": len(allPkgs),
@@ -145,6 +129,26 @@ func runSystemUpdateCheck() {
}
}
type backendResult struct {
ID string `json:"id"`
Display string `json:"displayName"`
Packages []sysupdate.Package `json:"packages"`
}
func backendResults(backends []sysupdate.Backend, pkgs []sysupdate.Package) []backendResult {
results := make([]backendResult, 0, len(backends))
for _, b := range backends {
var backendPkgs []sysupdate.Package
for _, p := range pkgs {
if sysupdate.BackendHasTargets(b, []sysupdate.Package{p}, true, true) {
backendPkgs = append(backendPkgs, p)
}
}
results = append(results, backendResult{ID: b.ID(), Display: b.DisplayName(), Packages: backendPkgs})
}
return results
}
func runSystemUpdateApply() {
checkCtx, checkCancel := context.WithTimeout(context.Background(), sysUpdateListPmTime)
defer checkCancel()
@@ -157,6 +161,7 @@ func runSystemUpdateApply() {
stopSpin := startSpinner("Checking for updates…")
pkgs, firstErr := collectUpdates(checkCtx, backends)
stopSpin()
pkgs = filterUpdateTargets(pkgs)
if firstErr != nil {
fmt.Printf("Warning: %v\n\n", firstErr)
}
@@ -190,14 +195,24 @@ func runSystemUpdateApply() {
DryRun: sysUpdateDry,
UseSudo: true,
}
opts.AttachStdio = sysupdate.UpgradeNeedsPrivilege(backends, pkgs, opts)
onLine := func(line string) { fmt.Println(line) }
ran := false
for _, b := range backends {
if !sysupdate.BackendHasTargets(b, pkgs, opts.IncludeAUR, opts.IncludeFlatpak) {
continue
}
ran = true
fmt.Printf("\n== %s ==\n", b.DisplayName())
if err := b.Upgrade(ctx, opts, onLine); err != nil {
log.Fatalf("%s upgrade failed: %v", b.ID(), err)
}
}
if !ran {
fmt.Println("Nothing to upgrade.")
return
}
if sysUpdateDry {
fmt.Println("\nDry run complete (no changes applied).")
return
@@ -218,6 +233,20 @@ func collectUpdates(ctx context.Context, backends []sysupdate.Backend) ([]sysupd
return all, firstErr
}
func filterUpdateTargets(pkgs []sysupdate.Package) []sysupdate.Package {
if !sysUpdateNoAUR {
return pkgs
}
out := pkgs[:0]
for _, p := range pkgs {
if p.Repo == sysupdate.RepoAUR {
continue
}
out = append(out, p)
}
return out
}
func runSystemUpdateSetInterval(seconds int) {
resp, err := sendServerRequest(models.Request{
ID: 1,

View File

@@ -45,13 +45,14 @@ func (aptBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(
OnLine: onLine,
})
}
names := pickTargetNames(opts.Targets, "apt", true)
if len(names) == 0 {
if !BackendHasTargets(aptBackend{}, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
privesc := privescBin(opts.UseSudo)
argv := append([]string{privesc, "env", "DEBIAN_FRONTEND=noninteractive", "LC_ALL=C", bin, "install", "-y", "--only-upgrade"}, names...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
return Run(ctx, aptUpgradeArgv(bin, opts), RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
func aptUpgradeArgv(bin string, opts UpgradeOptions) []string {
return privilegedArgv(opts, "env", "DEBIAN_FRONTEND=noninteractive", "LC_ALL=C", bin, "upgrade", "-y")
}
func parseAptUpgradable(text string) []Package {

View File

@@ -45,27 +45,37 @@ func (b dnfBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine fun
if opts.DryRun {
return Run(ctx, []string{b.bin, "upgrade", "--assumeno"}, RunOptions{OnLine: onLine})
}
names := pickTargetNames(opts.Targets, b.bin, true)
if len(names) == 0 {
if !BackendHasTargets(b, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
privesc := privescBin(opts.UseSudo)
argv := append([]string{privesc, b.bin, "upgrade", "-y"}, names...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
return Run(ctx, dnfUpgradeArgv(b.bin, opts), RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
func dnfUpgradeArgv(bin string, opts UpgradeOptions) []string {
return privilegedArgv(opts, bin, "upgrade", "--refresh", "-y")
}
func dnfListUpgrades(ctx context.Context, bin string) (string, error) {
cmd := exec.CommandContext(ctx, bin, "list", "--upgrades", "--refresh", "--quiet")
out, err := cmd.Output()
argv := dnfCheckUpdatesArgv(bin)
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
out, err := cmd.CombinedOutput()
if err == nil {
return string(out), nil
}
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok && exitErr.ExitCode() == 1 {
return "", nil
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok && exitErr.ExitCode() == 100 {
return string(out), nil
}
return "", err
}
func dnfCheckUpdatesArgv(bin string) []string {
subcommand := "check-update"
if bin == "dnf5" {
subcommand = "check-upgrade"
}
return []string{bin, subcommand, "--refresh", "--quiet"}
}
func rpmInstalledVersions(ctx context.Context) map[string]string {
out, err := exec.CommandContext(ctx, "rpm", "-qa", "--qf", `%{NAME}\t%{VERSION}-%{RELEASE}\n`).Output()
if err != nil {

View File

@@ -67,6 +67,21 @@ bash.x86_64 5.2.40-1.fc41 updates`,
{Name: "bash.x86_64", Repo: RepoSystem, Backend: "dnf", FromVersion: "", ToVersion: "5.2.40-1.fc41"},
},
},
{
name: "skips dnf warning lines while keeping package rows",
input: `Failed to expire repository cache in path "/home/user/.cache/libdnf5/updates": cannot open file
example-driver.x86_64 2:9.8.7-1.fc99 updates
example-tool.noarch 1.2.3^45.gitabcdef-1.fc99 copr`,
backendID: "dnf5",
installed: map[string]string{
"example-driver": "2:9.8.6-1.fc99",
"example-tool": "1.2.2^44.gitabcdef-1.fc99",
},
want: []Package{
{Name: "example-driver.x86_64", Repo: RepoSystem, Backend: "dnf5", FromVersion: "2:9.8.6-1.fc99", ToVersion: "2:9.8.7-1.fc99"},
{Name: "example-tool.noarch", Repo: RepoSystem, Backend: "dnf5", FromVersion: "1.2.2^44.gitabcdef-1.fc99", ToVersion: "1.2.3^45.gitabcdef-1.fc99"},
},
},
}
for _, tt := range tests {
@@ -78,3 +93,22 @@ bash.x86_64 5.2.40-1.fc41 updates`,
})
}
}
func TestDnfCheckUpdatesArgv(t *testing.T) {
tests := []struct {
bin string
want []string
}{
{bin: "dnf5", want: []string{"dnf5", "check-upgrade", "--refresh", "--quiet"}},
{bin: "dnf", want: []string{"dnf", "check-update", "--refresh", "--quiet"}},
}
for _, tt := range tests {
t.Run(tt.bin, func(t *testing.T) {
got := dnfCheckUpdatesArgv(tt.bin)
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("dnfCheckUpdatesArgv(%q) = %#v, want %#v", tt.bin, got, tt.want)
}
})
}
}

View File

@@ -73,27 +73,14 @@ func (flatpakBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine f
if opts.DryRun {
return Run(ctx, []string{"flatpak", "update", "--no-deploy", "-y"}, RunOptions{OnLine: onLine})
}
refs := flatpakTargetRefs(opts.Targets)
if len(refs) == 0 {
if !BackendHasTargets(flatpakBackend{}, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
argv := append([]string{"flatpak", "update", "-y", "--noninteractive"}, refs...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
return Run(ctx, flatpakUpgradeArgv(), RunOptions{OnLine: onLine})
}
func flatpakTargetRefs(targets []Package) []string {
out := make([]string, 0, len(targets))
for _, p := range targets {
if p.Backend != "flatpak" {
continue
}
ref := p.Ref
if ref == "" {
ref = p.Name
}
out = append(out, ref)
}
return out
func flatpakUpgradeArgv() []string {
return []string{"flatpak", "update", "-y", "--noninteractive"}
}
func parseFlatpakUpdates(text string, installed map[string]flatpakInstalledEntry) []Package {

View File

@@ -43,13 +43,14 @@ func (b pacmanBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine
if opts.DryRun {
return Run(ctx, []string{"pacman", "-Sup"}, RunOptions{OnLine: onLine})
}
names := pickTargetNames(opts.Targets, b.ID(), opts.IncludeAUR)
if len(names) == 0 {
if !BackendHasTargets(b, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
privesc := privescBin(opts.UseSudo)
argv := append([]string{privesc, "pacman", "-Sy", "--noconfirm", "--needed"}, names...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
return Run(ctx, pacmanUpgradeArgv(opts), RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
func pacmanUpgradeArgv(opts UpgradeOptions) []string {
return privilegedArgv(opts, "pacman", "-Syu", "--noconfirm", "--needed")
}
type archHelperBackend struct {
@@ -94,35 +95,28 @@ func (b archHelperBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onL
if opts.DryRun {
return Run(ctx, []string{b.id, "-Sup"}, RunOptions{OnLine: onLine})
}
names := pickTargetNames(opts.Targets, b.id, opts.IncludeAUR)
if len(names) == 0 {
if !BackendHasTargets(b, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
if os.Getenv("DMS_FORCE_PKEXEC") == "1" {
argv := append([]string{"pkexec", b.id, "-Sy", "--noconfirm", "--needed"}, names...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
argv := append([]string{"pkexec"}, archHelperUpgradeArgv(b.id, opts.IncludeAUR)...)
return Run(ctx, argv, RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
term := findTerminal(opts.Terminal)
if term == "" {
return fmt.Errorf("no terminal found (pick one in DMS settings, set $TERMINAL, or install kitty/ghostty/foot/alacritty)")
}
cmd := fmt.Sprintf("%s -Sy --noconfirm --needed %s", b.id, strings.Join(names, " "))
cmd := strings.Join(archHelperUpgradeArgv(b.id, opts.IncludeAUR), " ")
title := fmt.Sprintf("DMS — System Update (%s)", b.id)
return Run(ctx, wrapInTerminal(term, title, cmd), RunOptions{OnLine: onLine})
}
func pickTargetNames(targets []Package, backendID string, includeAUR bool) []string {
out := make([]string, 0, len(targets))
for _, p := range targets {
if p.Backend != backendID {
continue
}
if !includeAUR && p.Repo == RepoAUR {
continue
}
out = append(out, p.Name)
func archHelperUpgradeArgv(id string, includeAUR bool) []string {
argv := []string{id, "-Syu", "--noconfirm", "--needed"}
if !includeAUR {
argv = append(argv, "--repo")
}
return out
return argv
}
func pacmanRepoUpdates(ctx context.Context) (string, error) {

View File

@@ -117,9 +117,16 @@ func bootedDeployment(deps []ostreeDeployment) *ostreeDeployment {
}
func (rpmOstreeBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
if !BackendHasTargets(rpmOstreeBackend{}, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
return Run(ctx, rpmOstreeUpgradeArgv(opts), RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
func rpmOstreeUpgradeArgv(opts UpgradeOptions) []string {
argv := []string{"rpm-ostree", "upgrade"}
if opts.DryRun {
argv = append(argv, "--check")
}
return Run(ctx, argv, RunOptions{OnLine: onLine})
return argv
}

View File

@@ -74,11 +74,12 @@ func (zypperBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine fu
if opts.DryRun {
return Run(ctx, []string{"zypper", "--non-interactive", "--dry-run", "update"}, RunOptions{OnLine: onLine})
}
names := pickTargetNames(opts.Targets, "zypper", true)
if len(names) == 0 {
if !BackendHasTargets(zypperBackend{}, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return nil
}
privesc := privescBin(opts.UseSudo)
argv := append([]string{privesc, "zypper", "--non-interactive", "update"}, names...)
return Run(ctx, argv, RunOptions{OnLine: onLine})
return Run(ctx, zypperUpgradeArgv(opts), RunOptions{OnLine: onLine, AttachStdio: opts.AttachStdio})
}
func zypperUpgradeArgv(opts UpgradeOptions) []string {
return privilegedArgv(opts, "zypper", "--non-interactive", "update")
}

View File

@@ -14,8 +14,9 @@ import (
)
type RunOptions struct {
Env []string
OnLine func(string)
Env []string
OnLine func(string)
AttachStdio bool
}
func Run(ctx context.Context, argv []string, opts RunOptions) error {
@@ -27,6 +28,19 @@ func Run(ctx context.Context, argv []string, opts RunOptions) error {
if len(opts.Env) > 0 {
cmd.Env = append(cmd.Environ(), opts.Env...)
}
if opts.AttachStdio {
cmd.Cancel = func() error {
if cmd.Process == nil {
return nil
}
return cmd.Process.Kill()
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
if cmd.Process == nil {

View File

@@ -16,11 +16,12 @@ import (
)
const (
defaultIntervalSeconds = 30 * 60
minIntervalSeconds = 5 * 60
recentLogCapacity = 200
checkTimeout = 5 * time.Minute
upgradeTimeout = 30 * time.Minute
defaultIntervalSeconds = 30 * 60
minIntervalSeconds = 5 * 60
recentLogCapacity = 200
checkTimeout = 5 * time.Minute
upgradeTimeout = 30 * time.Minute
postUpgradeCompleteDelay = 3 * time.Second
)
type Manager struct {
@@ -310,18 +311,18 @@ func (m *Manager) runUpgrade(ctx context.Context, opts UpgradeOptions) {
return
}
backends := upgradeBackends(m.selection, opts)
if len(backends) == 0 {
m.setError(ErrCodeNoBackend, "no backend selected for upgrade")
return
}
if len(opts.Targets) == 0 {
m.mu.RLock()
opts.Targets = append([]Package(nil), m.state.Packages...)
m.mu.RUnlock()
}
backends := upgradeBackends(m.selection, opts)
if len(backends) == 0 {
m.setError(ErrCodeNoBackend, "no backend selected for upgrade")
return
}
opID := fmt.Sprintf("op-%d", time.Now().UnixNano())
m.mu.Lock()
m.state.Phase = PhaseUpgrading
@@ -351,13 +352,7 @@ func (m *Manager) runUpgrade(ctx context.Context, opts UpgradeOptions) {
}
}
m.mu.Lock()
m.state.Phase = PhaseIdle
m.state.OperationID = ""
m.state.OperationStarted = 0
m.mu.Unlock()
m.markDirty()
go m.runRefresh(context.Background())
m.finishSuccessfulUpgrade(true)
}
func (m *Manager) runCustomUpgrade(ctx context.Context, command, terminalOverride string) {
@@ -395,10 +390,29 @@ func (m *Manager) runCustomUpgrade(ctx context.Context, command, terminalOverrid
return
}
m.finishSuccessfulUpgrade(false)
}
func (m *Manager) finishSuccessfulUpgrade(clearPackages bool) {
m.appendLog("Upgrade complete.")
timer := time.NewTimer(postUpgradeCompleteDelay)
defer timer.Stop()
select {
case <-m.stopChan:
return
case <-timer.C:
}
m.mu.Lock()
m.state.Phase = PhaseIdle
m.state.OperationID = ""
m.state.OperationStarted = 0
if clearPackages {
m.state.Packages = m.state.Packages[:0]
m.state.Count = 0
}
m.mu.Unlock()
m.markDirty()
go m.runRefresh(context.Background())
@@ -407,18 +421,25 @@ func (m *Manager) runCustomUpgrade(ctx context.Context, command, terminalOverrid
func upgradeBackends(sel Selection, opts UpgradeOptions) []Backend {
var out []Backend
if sel.System != nil {
out = append(out, sel.System)
out = appendUpgradeBackend(out, sel.System, opts)
}
for _, b := range sel.Overlay {
switch {
case b.Repo() == RepoFlatpak && !opts.IncludeFlatpak:
continue
}
out = append(out, b)
out = appendUpgradeBackend(out, b, opts)
}
return out
}
func appendUpgradeBackend(out []Backend, b Backend, opts UpgradeOptions) []Backend {
if !BackendHasTargets(b, opts.Targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return out
}
return append(out, b)
}
func (m *Manager) appendLog(line string) {
m.mu.Lock()
if cap(m.state.RecentLog) == 0 {

View File

@@ -0,0 +1,60 @@
package sysupdate
func BackendHasTargets(b Backend, targets []Package, includeAUR, includeFlatpak bool) bool {
if b == nil || len(targets) == 0 {
return false
}
id := b.ID()
repo := b.Repo()
for _, p := range targets {
switch p.Repo {
case RepoFlatpak:
if !includeFlatpak {
continue
}
case RepoAUR:
if !includeAUR {
continue
}
}
switch repo {
case RepoFlatpak:
if p.Repo == RepoFlatpak || p.Backend == id {
return true
}
case RepoOSTree:
if p.Repo == RepoOSTree || p.Backend == id {
return true
}
default:
if p.Backend == id {
return true
}
}
}
return false
}
func UpgradeNeedsPrivilege(backends []Backend, targets []Package, opts UpgradeOptions) bool {
if opts.DryRun {
return false
}
for _, b := range backends {
if b == nil {
continue
}
if b.NeedsAuth() && BackendHasTargets(b, targets, opts.IncludeAUR, opts.IncludeFlatpak) {
return true
}
}
return false
}
func privilegedArgv(opts UpgradeOptions, argv ...string) []string {
privesc := privescBin(opts.UseSudo)
out := make([]string, 0, len(argv)+1)
out = append(out, privesc)
out = append(out, argv...)
return out
}

View File

@@ -77,6 +77,7 @@ type UpgradeOptions struct {
IncludeAUR bool
DryRun bool
UseSudo bool
AttachStdio bool
CustomCommand string
Terminal string
Targets []Package

View File

@@ -0,0 +1,147 @@
package sysupdate
import (
"reflect"
"testing"
)
func TestUpgradeCommandBuilders(t *testing.T) {
pkexecOpts := UpgradeOptions{UseSudo: false}
tests := []struct {
name string
got []string
want []string
}{
{
name: "dnf full upgrade",
got: dnfUpgradeArgv("dnf5", pkexecOpts),
want: []string{"pkexec", "dnf5", "upgrade", "--refresh", "-y"},
},
{
name: "apt full upgrade",
got: aptUpgradeArgv("apt-get", pkexecOpts),
want: []string{"pkexec", "env", "DEBIAN_FRONTEND=noninteractive", "LC_ALL=C", "apt-get", "upgrade", "-y"},
},
{
name: "zypper full update",
got: zypperUpgradeArgv(pkexecOpts),
want: []string{"pkexec", "zypper", "--non-interactive", "update"},
},
{
name: "pacman full sync upgrade",
got: pacmanUpgradeArgv(pkexecOpts),
want: []string{"pkexec", "pacman", "-Syu", "--noconfirm", "--needed"},
},
{
name: "aur helper full update with aur",
got: archHelperUpgradeArgv("paru", true),
want: []string{"paru", "-Syu", "--noconfirm", "--needed"},
},
{
name: "aur helper repo-only full update",
got: archHelperUpgradeArgv("yay", false),
want: []string{"yay", "-Syu", "--noconfirm", "--needed", "--repo"},
},
{
name: "flatpak full update",
got: flatpakUpgradeArgv(),
want: []string{"flatpak", "update", "-y", "--noninteractive"},
},
{
name: "rpm-ostree upgrade",
got: rpmOstreeUpgradeArgv(UpgradeOptions{}),
want: []string{"rpm-ostree", "upgrade"},
},
{
name: "rpm-ostree check",
got: rpmOstreeUpgradeArgv(UpgradeOptions{DryRun: true}),
want: []string{"rpm-ostree", "upgrade", "--check"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !reflect.DeepEqual(tt.got, tt.want) {
t.Fatalf("argv = %#v, want %#v", tt.got, tt.want)
}
})
}
}
func TestBackendHasTargetsRespectsBackendAndOptions(t *testing.T) {
targets := []Package{
{Name: "bash.x86_64", Repo: RepoSystem, Backend: "dnf5"},
{Name: "google-chrome", Repo: RepoAUR, Backend: "paru"},
{Name: "Discord", Repo: RepoFlatpak, Backend: "flatpak"},
{Name: "silverblue", Repo: RepoOSTree, Backend: "rpm-ostree"},
}
if !BackendHasTargets(dnfBackend{bin: "dnf5"}, targets, true, true) {
t.Fatal("dnf5 target was not detected")
}
if BackendHasTargets(dnfBackend{bin: "dnf"}, targets, true, true) {
t.Fatal("dnf target should not match dnf5 package targets")
}
if !BackendHasTargets(archHelperBackend{id: "paru"}, targets, true, true) {
t.Fatal("AUR helper target was not detected")
}
if BackendHasTargets(archHelperBackend{id: "paru"}, targets, false, true) {
t.Fatal("AUR helper should not match AUR-only target when AUR is disabled")
}
if !BackendHasTargets(flatpakBackend{}, targets, true, true) {
t.Fatal("Flatpak target was not detected")
}
if BackendHasTargets(flatpakBackend{}, targets, true, false) {
t.Fatal("Flatpak target should not match when Flatpak is disabled")
}
if !BackendHasTargets(rpmOstreeBackend{}, targets, true, true) {
t.Fatal("rpm-ostree target was not detected")
}
}
func TestUpgradeNeedsPrivilegeSkipsFlatpakOnly(t *testing.T) {
backends := []Backend{dnfBackend{bin: "dnf5"}, flatpakBackend{}}
opts := UpgradeOptions{IncludeAUR: true, IncludeFlatpak: true}
flatpakOnly := []Package{{Name: "Discord", Repo: RepoFlatpak, Backend: "flatpak"}}
if UpgradeNeedsPrivilege(backends, flatpakOnly, opts) {
t.Fatal("Flatpak-only updates should not need privileged auth")
}
mixed := []Package{
{Name: "bash.x86_64", Repo: RepoSystem, Backend: "dnf5"},
{Name: "Discord", Repo: RepoFlatpak, Backend: "flatpak"},
}
if !UpgradeNeedsPrivilege(backends, mixed, opts) {
t.Fatal("mixed system updates should need privileged auth")
}
opts.DryRun = true
if UpgradeNeedsPrivilege(backends, mixed, opts) {
t.Fatal("dry-run updates should not need privileged auth")
}
}
func TestUpgradeBackendsFiltersFlatpakOnly(t *testing.T) {
sel := Selection{
System: dnfBackend{bin: "dnf5"},
Overlay: []Backend{flatpakBackend{}},
}
opts := UpgradeOptions{
IncludeAUR: true,
IncludeFlatpak: true,
Targets: []Package{{Name: "Discord", Repo: RepoFlatpak, Backend: "flatpak"}},
}
got := upgradeBackends(sel, opts)
if len(got) != 1 || got[0].ID() != "flatpak" {
t.Fatalf("upgradeBackends(flatpak-only) = %#v, want only flatpak", got)
}
opts.Targets = append(opts.Targets, Package{Name: "bash.x86_64", Repo: RepoSystem, Backend: "dnf5"})
got = upgradeBackends(sel, opts)
if len(got) != 2 || got[0].ID() != "dnf5" || got[1].ID() != "flatpak" {
t.Fatalf("upgradeBackends(mixed) = %#v, want dnf5 then flatpak", got)
}
}