detached 57043db

feat(updater): add specific messages for Flatpak/pacman installs

commit
57043dbba343504a766b797bfb735eb22c6d1fc1
author
bnema · · 2025-12-30 21:30 UTC
parent
283a119
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +54 -1
M internal/application/port/updater.go +17 -0
@@ -25,12 +25,29 @@ type UpdateDownloader interface {
2525 Extract(ctx context.Context, archivePath string, destDir string) (binaryPath string, err error)
2626 }
2727
28+// SelfUpdateBlockedReason indicates why self-update is not available.
29+type SelfUpdateBlockedReason string
30+
31+const (
32+ // SelfUpdateAllowed means self-update is possible.
33+ SelfUpdateAllowed SelfUpdateBlockedReason = ""
34+ // SelfUpdateBlockedFlatpak means updates are managed by Flatpak.
35+ SelfUpdateBlockedFlatpak SelfUpdateBlockedReason = "flatpak"
36+ // SelfUpdateBlockedPacman means updates are managed by pacman/AUR.
37+ SelfUpdateBlockedPacman SelfUpdateBlockedReason = "pacman"
38+ // SelfUpdateBlockedNotWritable means the binary is not writable.
39+ SelfUpdateBlockedNotWritable SelfUpdateBlockedReason = "not_writable"
40+)
41+
2842 // UpdateApplier stages and applies updates.
2943 type UpdateApplier interface {
3044 // CanSelfUpdate checks if the current binary is writable by the current user.
3145 // Returns true if auto-update is possible, false if user lacks write permission.
3246 CanSelfUpdate(ctx context.Context) bool
3347
48+ // SelfUpdateBlockedReason returns why self-update is blocked, or empty if allowed.
49+ SelfUpdateBlockedReason(ctx context.Context) SelfUpdateBlockedReason
50+
3451 // GetBinaryPath returns the path to the currently running binary.
3552 GetBinaryPath() (string, error)
3653
@@ -25,12 +25,29 @@ type UpdateDownloader interface {
25 Extract(ctx context.Context, archivePath string, destDir string) (binaryPath string, err error)25 Extract(ctx context.Context, archivePath string, destDir string) (binaryPath string, err error)
26 }26 }
27 27
28+// SelfUpdateBlockedReason indicates why self-update is not available.
29+type SelfUpdateBlockedReason string
30+
31+const (
32+ // SelfUpdateAllowed means self-update is possible.
33+ SelfUpdateAllowed SelfUpdateBlockedReason = ""
34+ // SelfUpdateBlockedFlatpak means updates are managed by Flatpak.
35+ SelfUpdateBlockedFlatpak SelfUpdateBlockedReason = "flatpak"
36+ // SelfUpdateBlockedPacman means updates are managed by pacman/AUR.
37+ SelfUpdateBlockedPacman SelfUpdateBlockedReason = "pacman"
38+ // SelfUpdateBlockedNotWritable means the binary is not writable.
39+ SelfUpdateBlockedNotWritable SelfUpdateBlockedReason = "not_writable"
40+)
41+
28 // UpdateApplier stages and applies updates.42 // UpdateApplier stages and applies updates.
29 type UpdateApplier interface {43 type UpdateApplier interface {
30 // CanSelfUpdate checks if the current binary is writable by the current user.44 // CanSelfUpdate checks if the current binary is writable by the current user.
31 // Returns true if auto-update is possible, false if user lacks write permission.45 // Returns true if auto-update is possible, false if user lacks write permission.
32 CanSelfUpdate(ctx context.Context) bool46 CanSelfUpdate(ctx context.Context) bool
33 47
48+ // SelfUpdateBlockedReason returns why self-update is blocked, or empty if allowed.
49+ SelfUpdateBlockedReason(ctx context.Context) SelfUpdateBlockedReason
50+
34 // GetBinaryPath returns the path to the currently running binary.51 // GetBinaryPath returns the path to the currently running binary.
35 GetBinaryPath() (string, error)52 GetBinaryPath() (string, error)
36 53
M internal/application/usecase/apply_update.go +11 -1
@@ -58,9 +58,19 @@ func (uc *ApplyUpdateUseCase) Execute(ctx context.Context, input ApplyUpdateInpu
5858
5959 // Check if we can self-update.
6060 if !uc.applier.CanSelfUpdate(ctx) {
61+ reason := uc.applier.SelfUpdateBlockedReason(ctx)
62+ var message string
63+ switch reason {
64+ case port.SelfUpdateBlockedFlatpak:
65+ message = "Cannot auto-update: installed via Flatpak (use 'flatpak update' instead)"
66+ case port.SelfUpdateBlockedPacman:
67+ message = "Cannot auto-update: installed via pacman/AUR (use your package manager instead)"
68+ default:
69+ message = "Cannot auto-update: binary is not writable"
70+ }
6171 return &ApplyUpdateOutput{
6272 Status: entity.UpdateStatusFailed,
63- Message: "Cannot auto-update: binary is not writable",
73+ Message: message,
6474 }, nil
6575 }
6676
@@ -58,9 +58,19 @@ func (uc *ApplyUpdateUseCase) Execute(ctx context.Context, input ApplyUpdateInpu
58 58
59 // Check if we can self-update.59 // Check if we can self-update.
60 if !uc.applier.CanSelfUpdate(ctx) {60 if !uc.applier.CanSelfUpdate(ctx) {
61+ reason := uc.applier.SelfUpdateBlockedReason(ctx)
62+ var message string
63+ switch reason {
64+ case port.SelfUpdateBlockedFlatpak:
65+ message = "Cannot auto-update: installed via Flatpak (use 'flatpak update' instead)"
66+ case port.SelfUpdateBlockedPacman:
67+ message = "Cannot auto-update: installed via pacman/AUR (use your package manager instead)"
68+ default:
69+ message = "Cannot auto-update: binary is not writable"
70+ }
61 return &ApplyUpdateOutput{71 return &ApplyUpdateOutput{
62 Status: entity.UpdateStatusFailed,72 Status: entity.UpdateStatusFailed,
63- Message: "Cannot auto-update: binary is not writable",73+ Message: message,
64 }, nil74 }, nil
65 }75 }
66 76
M internal/infrastructure/updater/applier.go +26 -0
@@ -8,6 +8,7 @@ import (
88
99 "golang.org/x/sys/unix"
1010
11+ "github.com/bnema/dumber/internal/application/port"
1112 "github.com/bnema/dumber/internal/infrastructure/config"
1213 "github.com/bnema/dumber/internal/infrastructure/env"
1314 "github.com/bnema/dumber/internal/logging"
@@ -96,6 +97,31 @@ func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
9697 return true
9798 }
9899
100+// SelfUpdateBlockedReason returns why self-update is blocked, or empty if allowed.
101+func (a *Applier) SelfUpdateBlockedReason(ctx context.Context) port.SelfUpdateBlockedReason {
102+ // Flatpak sandboxed apps should not self-update; Flatpak handles updates.
103+ if env.IsFlatpak() {
104+ return port.SelfUpdateBlockedFlatpak
105+ }
106+
107+ // Pacman/AUR packages should not self-update; use pacman or AUR helper.
108+ if env.IsPacman() {
109+ return port.SelfUpdateBlockedPacman
110+ }
111+
112+ binaryPath, err := a.GetBinaryPath()
113+ if err != nil {
114+ return port.SelfUpdateBlockedNotWritable
115+ }
116+
117+ // Check if we have write permission on the binary file.
118+ if err := unix.Access(binaryPath, unix.W_OK); err != nil {
119+ return port.SelfUpdateBlockedNotWritable
120+ }
121+
122+ return port.SelfUpdateAllowed
123+}
124+
99125 // GetBinaryPath returns the path to the currently running binary.
100126 func (*Applier) GetBinaryPath() (string, error) {
101127 path, err := os.Executable()
@@ -8,6 +8,7 @@ import (
8 8
9 "golang.org/x/sys/unix"9 "golang.org/x/sys/unix"
10 10
11+ "github.com/bnema/dumber/internal/application/port"
11 "github.com/bnema/dumber/internal/infrastructure/config"12 "github.com/bnema/dumber/internal/infrastructure/config"
12 "github.com/bnema/dumber/internal/infrastructure/env"13 "github.com/bnema/dumber/internal/infrastructure/env"
13 "github.com/bnema/dumber/internal/logging"14 "github.com/bnema/dumber/internal/logging"
@@ -96,6 +97,31 @@ func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
96 return true97 return true
97 }98 }
98 99
100+// SelfUpdateBlockedReason returns why self-update is blocked, or empty if allowed.
101+func (a *Applier) SelfUpdateBlockedReason(ctx context.Context) port.SelfUpdateBlockedReason {
102+ // Flatpak sandboxed apps should not self-update; Flatpak handles updates.
103+ if env.IsFlatpak() {
104+ return port.SelfUpdateBlockedFlatpak
105+ }
106+
107+ // Pacman/AUR packages should not self-update; use pacman or AUR helper.
108+ if env.IsPacman() {
109+ return port.SelfUpdateBlockedPacman
110+ }
111+
112+ binaryPath, err := a.GetBinaryPath()
113+ if err != nil {
114+ return port.SelfUpdateBlockedNotWritable
115+ }
116+
117+ // Check if we have write permission on the binary file.
118+ if err := unix.Access(binaryPath, unix.W_OK); err != nil {
119+ return port.SelfUpdateBlockedNotWritable
120+ }
121+
122+ return port.SelfUpdateAllowed
123+}
124+
99 // GetBinaryPath returns the path to the currently running binary.125 // GetBinaryPath returns the path to the currently running binary.
100 func (*Applier) GetBinaryPath() (string, error) {126 func (*Applier) GetBinaryPath() (string, error) {
101 path, err := os.Executable()127 path, err := os.Executable()

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close