mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-30 01:22:06 -04:00
Revert "system updater: make all distros use terminal"
This reverts commit 1467f5dba9.
This commit is contained in:
@@ -7,8 +7,6 @@ This file is more of a quick reference so I know what to account for before next
|
|||||||
- Terminal mux
|
- Terminal mux
|
||||||
- Locale overrides
|
- Locale overrides
|
||||||
- new neovim theming
|
- new neovim theming
|
||||||
- system upder overhaul
|
|
||||||
- New Log system, remember to update all plugins after release as it breaks compat
|
|
||||||
|
|
||||||
# 1.4.0
|
# 1.4.0
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -184,20 +183,10 @@ func runSystemUpdateApply() {
|
|||||||
DryRun: sysUpdateDry,
|
DryRun: sysUpdateDry,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLine := func(line string) { fmt.Println(line) }
|
||||||
for _, b := range backends {
|
for _, b := range backends {
|
||||||
cmd, err := b.UpgradeCommand(opts)
|
fmt.Printf("\n== %s ==\n", b.DisplayName())
|
||||||
if err != nil {
|
if err := b.Upgrade(ctx, opts, onLine); err != nil {
|
||||||
log.Fatalf("%s: %v", b.ID(), err)
|
|
||||||
}
|
|
||||||
if cmd == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf("\n== %s ==\n$ %s\n\n", b.DisplayName(), cmd)
|
|
||||||
shell := exec.CommandContext(ctx, "sh", "-c", cmd)
|
|
||||||
shell.Stdin = os.Stdin
|
|
||||||
shell.Stdout = os.Stdout
|
|
||||||
shell.Stderr = os.Stderr
|
|
||||||
if err := shell.Run(); err != nil {
|
|
||||||
log.Fatalf("%s upgrade failed: %v", b.ID(), err)
|
log.Fatalf("%s upgrade failed: %v", b.ID(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ type Backend interface {
|
|||||||
Repo() RepoKind
|
Repo() RepoKind
|
||||||
IsAvailable(ctx context.Context) bool
|
IsAvailable(ctx context.Context) bool
|
||||||
NeedsAuth() bool
|
NeedsAuth() bool
|
||||||
|
RunsInTerminal() bool
|
||||||
CheckUpdates(ctx context.Context) ([]Package, error)
|
CheckUpdates(ctx context.Context) ([]Package, error)
|
||||||
UpgradeCommand(opts UpgradeOptions) (string, error)
|
Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Selection struct {
|
type Selection struct {
|
||||||
@@ -36,10 +37,11 @@ func (s Selection) Info() []BackendInfo {
|
|||||||
out := make([]BackendInfo, 0, len(all))
|
out := make([]BackendInfo, 0, len(all))
|
||||||
for _, b := range all {
|
for _, b := range all {
|
||||||
out = append(out, BackendInfo{
|
out = append(out, BackendInfo{
|
||||||
ID: b.ID(),
|
ID: b.ID(),
|
||||||
DisplayName: b.DisplayName(),
|
DisplayName: b.DisplayName(),
|
||||||
Repo: b.Repo(),
|
Repo: b.Repo(),
|
||||||
NeedsAuth: b.NeedsAuth(),
|
NeedsAuth: b.NeedsAuth(),
|
||||||
|
RunsInTerminal: b.RunsInTerminal(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ var aptUpgradableLine = regexp.MustCompile(`^([^/]+)/\S+\s+(\S+)\s+\S+\s+\[upgra
|
|||||||
|
|
||||||
type aptBackend struct{}
|
type aptBackend struct{}
|
||||||
|
|
||||||
func (aptBackend) ID() string { return "apt" }
|
func (aptBackend) ID() string { return "apt" }
|
||||||
func (aptBackend) DisplayName() string { return "APT" }
|
func (aptBackend) DisplayName() string { return "APT" }
|
||||||
func (aptBackend) Repo() RepoKind { return RepoSystem }
|
func (aptBackend) Repo() RepoKind { return RepoSystem }
|
||||||
func (aptBackend) NeedsAuth() bool { return true }
|
func (aptBackend) NeedsAuth() bool { return true }
|
||||||
|
func (aptBackend) RunsInTerminal() bool { return false }
|
||||||
func (aptBackend) IsAvailable(_ context.Context) bool {
|
func (aptBackend) IsAvailable(_ context.Context) bool {
|
||||||
return commandExists("apt") || commandExists("apt-get")
|
return commandExists("apt") || commandExists("apt-get")
|
||||||
}
|
}
|
||||||
@@ -33,16 +34,19 @@ func (aptBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
|||||||
return parseAptUpgradable(string(out)), nil
|
return parseAptUpgradable(string(out)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aptBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (aptBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
bin := "apt-get"
|
bin := "apt-get"
|
||||||
if !commandExists(bin) {
|
if !commandExists(bin) {
|
||||||
bin = "apt"
|
bin = "apt"
|
||||||
}
|
}
|
||||||
env := "DEBIAN_FRONTEND=noninteractive LC_ALL=C "
|
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return env + bin + " upgrade --dry-run", nil
|
return Run(ctx, []string{bin, "upgrade", "--dry-run"}, RunOptions{
|
||||||
|
Env: []string{"DEBIAN_FRONTEND=noninteractive", "LC_ALL=C"},
|
||||||
|
OnLine: onLine,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return "sudo " + env + bin + " upgrade -y", nil
|
argv := []string{"pkexec", "env", "DEBIAN_FRONTEND=noninteractive", "LC_ALL=C", bin, "upgrade", "-y"}
|
||||||
|
return Run(ctx, argv, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAptUpgradable(text string) []Package {
|
func parseAptUpgradable(text string) []Package {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package sysupdate
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -17,10 +16,11 @@ type dnfBackend struct {
|
|||||||
bin string
|
bin string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b dnfBackend) ID() string { return b.bin }
|
func (b dnfBackend) ID() string { return b.bin }
|
||||||
func (b dnfBackend) DisplayName() string { return strings.ToUpper(b.bin) }
|
func (b dnfBackend) DisplayName() string { return strings.ToUpper(b.bin) }
|
||||||
func (b dnfBackend) Repo() RepoKind { return RepoSystem }
|
func (b dnfBackend) Repo() RepoKind { return RepoSystem }
|
||||||
func (b dnfBackend) NeedsAuth() bool { return true }
|
func (b dnfBackend) NeedsAuth() bool { return true }
|
||||||
|
func (b dnfBackend) RunsInTerminal() bool { return false }
|
||||||
|
|
||||||
func (b dnfBackend) IsAvailable(ctx context.Context) bool {
|
func (b dnfBackend) IsAvailable(ctx context.Context) bool {
|
||||||
if !commandExists(b.bin) {
|
if !commandExists(b.bin) {
|
||||||
@@ -41,11 +41,11 @@ func (b dnfBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
|||||||
return parseDnfList(out, b.bin, installed), nil
|
return parseDnfList(out, b.bin, installed), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b dnfBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (b dnfBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return fmt.Sprintf("%s upgrade --assumeno", b.bin), nil
|
return Run(ctx, []string{b.bin, "upgrade", "--assumeno"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("sudo %s upgrade -y", b.bin), nil
|
return Run(ctx, []string{"pkexec", b.bin, "upgrade", "-y"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|
||||||
func dnfListUpgrades(ctx context.Context, bin string) (string, error) {
|
func dnfListUpgrades(ctx context.Context, bin string) (string, error) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func (flatpakBackend) ID() string { return "flatpak" }
|
|||||||
func (flatpakBackend) DisplayName() string { return "Flatpak" }
|
func (flatpakBackend) DisplayName() string { return "Flatpak" }
|
||||||
func (flatpakBackend) Repo() RepoKind { return RepoFlatpak }
|
func (flatpakBackend) Repo() RepoKind { return RepoFlatpak }
|
||||||
func (flatpakBackend) NeedsAuth() bool { return false }
|
func (flatpakBackend) NeedsAuth() bool { return false }
|
||||||
|
func (flatpakBackend) RunsInTerminal() bool { return false }
|
||||||
func (flatpakBackend) IsAvailable(_ context.Context) bool { return commandExists("flatpak") }
|
func (flatpakBackend) IsAvailable(_ context.Context) bool { return commandExists("flatpak") }
|
||||||
|
|
||||||
func (flatpakBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
func (flatpakBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
||||||
@@ -68,11 +69,12 @@ type flatpakInstalledEntry struct {
|
|||||||
commit string
|
commit string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (flatpakBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (flatpakBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
|
argv := []string{"flatpak", "update", "-y", "--noninteractive"}
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return "flatpak update --no-deploy -y", nil
|
argv = []string{"flatpak", "update", "--no-deploy", "-y"}
|
||||||
}
|
}
|
||||||
return "flatpak update -y --noninteractive", nil
|
return Run(ctx, argv, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFlatpakUpdates(text string, installed map[string]flatpakInstalledEntry) []Package {
|
func parseFlatpakUpdates(text string, installed map[string]flatpakInstalledEntry) []Package {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func (pacmanBackend) ID() string { return "pacman" }
|
|||||||
func (pacmanBackend) DisplayName() string { return "Pacman" }
|
func (pacmanBackend) DisplayName() string { return "Pacman" }
|
||||||
func (pacmanBackend) Repo() RepoKind { return RepoSystem }
|
func (pacmanBackend) Repo() RepoKind { return RepoSystem }
|
||||||
func (pacmanBackend) NeedsAuth() bool { return true }
|
func (pacmanBackend) NeedsAuth() bool { return true }
|
||||||
|
func (pacmanBackend) RunsInTerminal() bool { return false }
|
||||||
func (pacmanBackend) IsAvailable(_ context.Context) bool { return commandExists("pacman") }
|
func (pacmanBackend) IsAvailable(_ context.Context) bool { return commandExists("pacman") }
|
||||||
|
|
||||||
func (b pacmanBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
func (b pacmanBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
||||||
@@ -38,11 +39,11 @@ func (b pacmanBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
|
|||||||
return parseArchUpdates(out, b.ID(), RepoSystem), nil
|
return parseArchUpdates(out, b.ID(), RepoSystem), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pacmanBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (b pacmanBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return "pacman -Sup", nil
|
return Run(ctx, []string{"pacman", "-Sup"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
return "sudo pacman -Syu --noconfirm", nil
|
return Run(ctx, []string{"pkexec", "pacman", "-Syu", "--noconfirm"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|
||||||
type archHelperBackend struct {
|
type archHelperBackend struct {
|
||||||
@@ -52,6 +53,7 @@ type archHelperBackend struct {
|
|||||||
func (b archHelperBackend) ID() string { return b.id }
|
func (b archHelperBackend) ID() string { return b.id }
|
||||||
func (b archHelperBackend) Repo() RepoKind { return RepoSystem }
|
func (b archHelperBackend) Repo() RepoKind { return RepoSystem }
|
||||||
func (b archHelperBackend) NeedsAuth() bool { return true }
|
func (b archHelperBackend) NeedsAuth() bool { return true }
|
||||||
|
func (b archHelperBackend) RunsInTerminal() bool { return true }
|
||||||
func (b archHelperBackend) IsAvailable(_ context.Context) bool { return commandExists(b.id) }
|
func (b archHelperBackend) IsAvailable(_ context.Context) bool { return commandExists(b.id) }
|
||||||
|
|
||||||
func (b archHelperBackend) DisplayName() string {
|
func (b archHelperBackend) DisplayName() string {
|
||||||
@@ -80,15 +82,20 @@ func (b archHelperBackend) CheckUpdates(ctx context.Context) ([]Package, error)
|
|||||||
return pkgs, nil
|
return pkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b archHelperBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (b archHelperBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return fmt.Sprintf("%s -Sup", b.id), nil
|
return Run(ctx, []string{b.id, "-Sup"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
cmd := fmt.Sprintf("%s -Syu --noconfirm", b.id)
|
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 -Syu", b.id)
|
||||||
if !opts.IncludeAUR {
|
if !opts.IncludeAUR {
|
||||||
cmd += " --repo"
|
cmd += " --repo"
|
||||||
}
|
}
|
||||||
return cmd, nil
|
title := fmt.Sprintf("DMS — System Update (%s)", b.id)
|
||||||
|
return Run(ctx, wrapInTerminal(term, title, cmd), RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|
||||||
func pacmanRepoUpdates(ctx context.Context) (string, error) {
|
func pacmanRepoUpdates(ctx context.Context) (string, error) {
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ func init() {
|
|||||||
|
|
||||||
type rpmOstreeBackend struct{}
|
type rpmOstreeBackend struct{}
|
||||||
|
|
||||||
func (rpmOstreeBackend) ID() string { return "rpm-ostree" }
|
func (rpmOstreeBackend) ID() string { return "rpm-ostree" }
|
||||||
func (rpmOstreeBackend) DisplayName() string { return "rpm-ostree" }
|
func (rpmOstreeBackend) DisplayName() string { return "rpm-ostree" }
|
||||||
func (rpmOstreeBackend) Repo() RepoKind { return RepoOSTree }
|
func (rpmOstreeBackend) Repo() RepoKind { return RepoOSTree }
|
||||||
func (rpmOstreeBackend) NeedsAuth() bool { return true }
|
func (rpmOstreeBackend) NeedsAuth() bool { return true }
|
||||||
|
func (rpmOstreeBackend) RunsInTerminal() bool { return false }
|
||||||
|
|
||||||
func (b rpmOstreeBackend) IsAvailable(ctx context.Context) bool {
|
func (b rpmOstreeBackend) IsAvailable(ctx context.Context) bool {
|
||||||
if !commandExists("rpm-ostree") {
|
if !commandExists("rpm-ostree") {
|
||||||
@@ -115,9 +116,10 @@ func bootedDeployment(deps []ostreeDeployment) *ostreeDeployment {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rpmOstreeBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (rpmOstreeBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
|
argv := []string{"rpm-ostree", "upgrade"}
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return "rpm-ostree upgrade --check", nil
|
argv = append(argv, "--check")
|
||||||
}
|
}
|
||||||
return "rpm-ostree upgrade", nil
|
return Run(ctx, argv, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func (zypperBackend) ID() string { return "zypper" }
|
|||||||
func (zypperBackend) DisplayName() string { return "Zypper" }
|
func (zypperBackend) DisplayName() string { return "Zypper" }
|
||||||
func (zypperBackend) Repo() RepoKind { return RepoSystem }
|
func (zypperBackend) Repo() RepoKind { return RepoSystem }
|
||||||
func (zypperBackend) NeedsAuth() bool { return true }
|
func (zypperBackend) NeedsAuth() bool { return true }
|
||||||
|
func (zypperBackend) RunsInTerminal() bool { return false }
|
||||||
func (zypperBackend) IsAvailable(_ context.Context) bool { return commandExists("zypper") }
|
func (zypperBackend) IsAvailable(_ context.Context) bool { return commandExists("zypper") }
|
||||||
|
|
||||||
type zypperUpdateList struct {
|
type zypperUpdateList struct {
|
||||||
@@ -69,9 +70,9 @@ func parseZypperXML(out []byte) ([]Package, error) {
|
|||||||
return pkgs, nil
|
return pkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (zypperBackend) UpgradeCommand(opts UpgradeOptions) (string, error) {
|
func (zypperBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
|
||||||
if opts.DryRun {
|
if opts.DryRun {
|
||||||
return "zypper --non-interactive --dry-run update", nil
|
return Run(ctx, []string{"zypper", "--non-interactive", "--dry-run", "update"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
return "sudo zypper --non-interactive update", nil
|
return Run(ctx, []string{"pkexec", "zypper", "--non-interactive", "update"}, RunOptions{OnLine: onLine})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,63 @@
|
|||||||
package sysupdate
|
package sysupdate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(ctx context.Context, argv []string) error {
|
type RunOptions struct {
|
||||||
|
Env []string
|
||||||
|
OnLine func(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ctx context.Context, argv []string, opts RunOptions) error {
|
||||||
if len(argv) == 0 {
|
if len(argv) == 0 {
|
||||||
return fmt.Errorf("sysupdate.Run: empty argv")
|
return fmt.Errorf("sysupdate.Run: empty argv")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
|
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
|
||||||
return cmd.Run()
|
if len(opts.Env) > 0 {
|
||||||
|
cmd.Env = append(cmd.Environ(), opts.Env...)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go pump(stdout, opts.OnLine, &wg)
|
||||||
|
go pump(stderr, opts.OnLine, &wg)
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return cmd.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pump(r io.Reader, onLine func(string), wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
if onLine == nil {
|
||||||
|
_, _ = io.Copy(io.Discard, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
scanner.Buffer(make([]byte, 64*1024), 1024*1024)
|
||||||
|
for scanner.Scan() {
|
||||||
|
onLine(scanner.Text())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Capture(ctx context.Context, argv []string) (string, error) {
|
func Capture(ctx context.Context, argv []string) (string, error) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
defaultIntervalSeconds = 30 * 60
|
defaultIntervalSeconds = 30 * 60
|
||||||
minIntervalSeconds = 5 * 60
|
minIntervalSeconds = 5 * 60
|
||||||
|
recentLogCapacity = 200
|
||||||
checkTimeout = 5 * time.Minute
|
checkTimeout = 5 * time.Minute
|
||||||
upgradeTimeout = 30 * time.Minute
|
upgradeTimeout = 30 * time.Minute
|
||||||
)
|
)
|
||||||
@@ -239,6 +240,7 @@ func (m *Manager) runRefresh(parent context.Context) {
|
|||||||
}
|
}
|
||||||
m.state.Phase = PhaseRefreshing
|
m.state.Phase = PhaseRefreshing
|
||||||
m.state.Error = nil
|
m.state.Error = nil
|
||||||
|
m.state.RecentLog = nil
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
m.markDirty()
|
m.markDirty()
|
||||||
|
|
||||||
@@ -296,13 +298,57 @@ func (m *Manager) runUpgrade(ctx context.Context, opts UpgradeOptions) {
|
|||||||
m.opMu.Unlock()
|
m.opMu.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
combined, err := buildBundledCommand(m.selection, opts)
|
if opts.CustomCommand != "" {
|
||||||
if err != nil {
|
m.runCustomUpgrade(ctx, opts.CustomCommand, opts.Terminal)
|
||||||
m.setError(ErrCodeNoBackend, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
term := findTerminal(opts.Terminal)
|
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
|
||||||
|
m.state.OperationID = opID
|
||||||
|
m.state.OperationStarted = time.Now().Unix()
|
||||||
|
m.state.RecentLog = m.state.RecentLog[:0]
|
||||||
|
m.state.Error = nil
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.markDirty()
|
||||||
|
|
||||||
|
onLine := func(line string) { m.appendLog(line) }
|
||||||
|
for _, b := range backends {
|
||||||
|
m.appendLog(fmt.Sprintf("== %s ==", b.DisplayName()))
|
||||||
|
if err := b.Upgrade(ctx, opts, onLine); err != nil {
|
||||||
|
code := ErrCodeBackendFailed
|
||||||
|
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||||
|
code = ErrCodeTimeout
|
||||||
|
} else if errors.Is(ctx.Err(), context.Canceled) {
|
||||||
|
code = ErrCodeCancelled
|
||||||
|
}
|
||||||
|
m.mu.Lock()
|
||||||
|
m.state.Phase = PhaseError
|
||||||
|
m.state.Error = &ErrorInfo{Code: code, Message: fmt.Sprintf("%s: %v", b.ID(), err)}
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.markDirty()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.state.Phase = PhaseIdle
|
||||||
|
m.state.OperationID = ""
|
||||||
|
m.state.OperationStarted = 0
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.markDirty()
|
||||||
|
go m.runRefresh(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) runCustomUpgrade(ctx context.Context, command, terminalOverride string) {
|
||||||
|
term := findTerminal(terminalOverride)
|
||||||
if term == "" {
|
if term == "" {
|
||||||
m.setError(ErrCodeBackendFailed, "no terminal found (pick one in DMS settings, set $TERMINAL, or install kitty/ghostty/foot/alacritty)")
|
m.setError(ErrCodeBackendFailed, "no terminal found (pick one in DMS settings, set $TERMINAL, or install kitty/ghostty/foot/alacritty)")
|
||||||
return
|
return
|
||||||
@@ -313,12 +359,14 @@ func (m *Manager) runUpgrade(ctx context.Context, opts UpgradeOptions) {
|
|||||||
m.state.Phase = PhaseUpgrading
|
m.state.Phase = PhaseUpgrading
|
||||||
m.state.OperationID = opID
|
m.state.OperationID = opID
|
||||||
m.state.OperationStarted = time.Now().Unix()
|
m.state.OperationStarted = time.Now().Unix()
|
||||||
|
m.state.RecentLog = m.state.RecentLog[:0]
|
||||||
m.state.Error = nil
|
m.state.Error = nil
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
m.markDirty()
|
m.markDirty()
|
||||||
|
|
||||||
argv := wrapInTerminal(term, "DMS — System Update", combined)
|
onLine := func(line string) { m.appendLog(line) }
|
||||||
if err := Run(ctx, argv); err != nil {
|
argv := wrapInTerminal(term, "DMS — System Update (custom)", command)
|
||||||
|
if err := Run(ctx, argv, RunOptions{OnLine: onLine}); err != nil {
|
||||||
code := ErrCodeBackendFailed
|
code := ErrCodeBackendFailed
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(ctx.Err(), context.DeadlineExceeded):
|
case errors.Is(ctx.Err(), context.DeadlineExceeded):
|
||||||
@@ -343,31 +391,6 @@ func (m *Manager) runUpgrade(ctx context.Context, opts UpgradeOptions) {
|
|||||||
go m.runRefresh(context.Background())
|
go m.runRefresh(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBundledCommand(sel Selection, opts UpgradeOptions) (string, error) {
|
|
||||||
if opts.CustomCommand != "" {
|
|
||||||
return opts.CustomCommand, nil
|
|
||||||
}
|
|
||||||
backends := upgradeBackends(sel, opts)
|
|
||||||
if len(backends) == 0 {
|
|
||||||
return "", errors.New("no backend selected for upgrade")
|
|
||||||
}
|
|
||||||
parts := make([]string, 0, len(backends))
|
|
||||||
for _, b := range backends {
|
|
||||||
cmd, err := b.UpgradeCommand(opts)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("%s: %w", b.ID(), err)
|
|
||||||
}
|
|
||||||
if cmd == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts = append(parts, cmd)
|
|
||||||
}
|
|
||||||
if len(parts) == 0 {
|
|
||||||
return "", errors.New("no backend produced an upgrade command")
|
|
||||||
}
|
|
||||||
return strings.Join(parts, " && "), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func upgradeBackends(sel Selection, opts UpgradeOptions) []Backend {
|
func upgradeBackends(sel Selection, opts UpgradeOptions) []Backend {
|
||||||
var out []Backend
|
var out []Backend
|
||||||
if sel.System != nil {
|
if sel.System != nil {
|
||||||
@@ -383,6 +406,20 @@ func upgradeBackends(sel Selection, opts UpgradeOptions) []Backend {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) appendLog(line string) {
|
||||||
|
m.mu.Lock()
|
||||||
|
if cap(m.state.RecentLog) == 0 {
|
||||||
|
m.state.RecentLog = make([]string, 0, recentLogCapacity)
|
||||||
|
}
|
||||||
|
if len(m.state.RecentLog) >= recentLogCapacity {
|
||||||
|
copy(m.state.RecentLog, m.state.RecentLog[1:])
|
||||||
|
m.state.RecentLog = m.state.RecentLog[:recentLogCapacity-1]
|
||||||
|
}
|
||||||
|
m.state.RecentLog = append(m.state.RecentLog, line)
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.markDirty()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) setError(code ErrorCode, msg string) {
|
func (m *Manager) setError(code ErrorCode, msg string) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
m.state.Phase = PhaseError
|
m.state.Phase = PhaseError
|
||||||
@@ -421,6 +458,7 @@ func cloneState(s State) State {
|
|||||||
out := s
|
out := s
|
||||||
out.Backends = append([]BackendInfo(nil), s.Backends...)
|
out.Backends = append([]BackendInfo(nil), s.Backends...)
|
||||||
out.Packages = append([]Package(nil), s.Packages...)
|
out.Packages = append([]Package(nil), s.Packages...)
|
||||||
|
out.RecentLog = append([]string(nil), s.RecentLog...)
|
||||||
if s.Error != nil {
|
if s.Error != nil {
|
||||||
errCopy := *s.Error
|
errCopy := *s.Error
|
||||||
out.Error = &errCopy
|
out.Error = &errCopy
|
||||||
|
|||||||
@@ -41,10 +41,11 @@ type Package struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BackendInfo struct {
|
type BackendInfo struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
DisplayName string `json:"displayName"`
|
DisplayName string `json:"displayName"`
|
||||||
Repo RepoKind `json:"repo"`
|
Repo RepoKind `json:"repo"`
|
||||||
NeedsAuth bool `json:"needsAuth"`
|
NeedsAuth bool `json:"needsAuth"`
|
||||||
|
RunsInTerminal bool `json:"runsInTerminal"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorInfo struct {
|
type ErrorInfo struct {
|
||||||
@@ -66,6 +67,7 @@ type State struct {
|
|||||||
NextCheckUnix int64 `json:"nextCheckUnix,omitempty"`
|
NextCheckUnix int64 `json:"nextCheckUnix,omitempty"`
|
||||||
OperationID string `json:"operationId,omitempty"`
|
OperationID string `json:"operationId,omitempty"`
|
||||||
OperationStarted int64 `json:"operationStartedUnix,omitempty"`
|
OperationStarted int64 `json:"operationStartedUnix,omitempty"`
|
||||||
|
RecentLog []string `json:"recentLog,omitempty"`
|
||||||
Error *ErrorInfo `json:"error,omitempty"`
|
Error *ErrorInfo `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ DankPopout {
|
|||||||
color: "transparent"
|
color: "transparent"
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
|
readonly property bool hasTerminalBackend: (SystemUpdateService.backends || []).some(b => b.runsInTerminal === true)
|
||||||
|
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
systemUpdatePopout.close();
|
systemUpdatePopout.close();
|
||||||
@@ -206,9 +208,13 @@ DankPopout {
|
|||||||
includeAUR: SettingsData.updaterAllowAUR,
|
includeAUR: SettingsData.updaterAllowAUR,
|
||||||
terminal: SessionData.terminalOverride
|
terminal: SessionData.terminalOverride
|
||||||
};
|
};
|
||||||
systemUpdatePopout._reopenAfterUpgrade = true;
|
if (updaterPanel.hasTerminalBackend) {
|
||||||
|
systemUpdatePopout._reopenAfterUpgrade = true;
|
||||||
|
SystemUpdateService.runUpdates(opts);
|
||||||
|
systemUpdatePopout.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
SystemUpdateService.runUpdates(opts);
|
SystemUpdateService.runUpdates(opts);
|
||||||
systemUpdatePopout.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +383,7 @@ DankPopout {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
visible: SystemUpdateService.isUpgrading
|
visible: SystemUpdateService.isUpgrading && updaterPanel.hasTerminalBackend
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -397,13 +403,38 @@ DankPopout {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: I18n.tr("See the terminal window for prompts. This popout will return when the upgrade exits.")
|
text: I18n.tr("AUR helpers are interactive — see the terminal window for prompts. This popout will return to idle when the upgrade exits.")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
visible: SystemUpdateService.isUpgrading && !updaterPanel.hasTerminalBackend
|
||||||
|
contentWidth: width
|
||||||
|
contentHeight: logText.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
onContentHeightChanged: {
|
||||||
|
if (contentHeight > height) {
|
||||||
|
contentY = contentHeight - height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: logText
|
||||||
|
width: parent.width
|
||||||
|
text: (SystemUpdateService.recentLog || []).join("\n")
|
||||||
|
font.family: Theme.monoFontFamily || "monospace"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Rectangle {
|
|||||||
property string iconName: ""
|
property string iconName: ""
|
||||||
property string text: ""
|
property string text: ""
|
||||||
property bool isDestructive: false
|
property bool isDestructive: false
|
||||||
|
property bool enabled: true
|
||||||
|
|
||||||
signal triggered
|
signal triggered
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ Singleton {
|
|||||||
property string distributionPretty: ""
|
property string distributionPretty: ""
|
||||||
property string pkgManager: ""
|
property string pkgManager: ""
|
||||||
property bool distributionSupported: false
|
property bool distributionSupported: false
|
||||||
|
property var recentLog: []
|
||||||
property int intervalSeconds: 1800
|
property int intervalSeconds: 1800
|
||||||
property int lastCheckUnix: 0
|
property int lastCheckUnix: 0
|
||||||
property int nextCheckUnix: 0
|
property int nextCheckUnix: 0
|
||||||
@@ -88,6 +89,7 @@ Singleton {
|
|||||||
distribution = data.distro || "";
|
distribution = data.distro || "";
|
||||||
distributionPretty = data.distroPretty || "";
|
distributionPretty = data.distroPretty || "";
|
||||||
distributionSupported = (backends.length > 0);
|
distributionSupported = (backends.length > 0);
|
||||||
|
recentLog = data.recentLog || [];
|
||||||
intervalSeconds = data.intervalSeconds || 1800;
|
intervalSeconds = data.intervalSeconds || 1800;
|
||||||
lastCheckUnix = data.lastCheckUnix || 0;
|
lastCheckUnix = data.lastCheckUnix || 0;
|
||||||
nextCheckUnix = data.nextCheckUnix || 0;
|
nextCheckUnix = data.nextCheckUnix || 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user