返回 DeepSeek-Reasonix
updater_install.go
根目录 / desktop / updater_install.go
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "runtime"
7 "strings"
8
9 "reasonix/desktop/internal/update"
10 )
11
12 // Install modes reported to the frontend and used to pick the update asset.
13 const (
14 installModePortable = "portable"
15 installModeDeb = "deb"
16 installModeManual = "manual"
17
18 artifactKindTarball = "tarball"
19 artifactKindDeb = "deb"
20
21 linuxUpdateHelperPath = "/usr/lib/reasonix/reasonix-update-helper"
22 linuxPkexecPath = "/usr/bin/pkexec"
23 linuxDebPackageName = "reasonix-desktop"
24 )
25
26 // installProfile is the runtime install classification used by evaluate and install.
27 type installProfile struct {
28 Mode string
29 CanSelfUpdate bool
30 RequiresElev bool
31 ManualReason string
32 ArtifactKind string // artifactKindTarball | artifactKindDeb | ""
33 }
34
35 // detectInstallProfile classifies how this binary was installed and which update
36 // path is available. profileForManifest then may downgrade deb → manual when the
37 // manifest has no native package entry.
38 func detectInstallProfile() installProfile {
39 switch runtime.GOOS {
40 case "linux":
41 return detectLinuxInstallProfile()
42 case "windows":
43 return installProfile{
44 Mode: installModePortable,
45 CanSelfUpdate: true,
46 ArtifactKind: artifactKindTarball,
47 }
48 case "darwin":
49 if canSelfUpdate() {
50 return installProfile{
51 Mode: installModePortable,
52 CanSelfUpdate: true,
53 ArtifactKind: artifactKindTarball,
54 }
55 }
56 return installProfile{
57 Mode: installModeManual,
58 CanSelfUpdate: false,
59 ManualReason: manualUpdateReason(),
60 }
61 default:
62 return installProfile{
63 Mode: installModeManual,
64 CanSelfUpdate: false,
65 ManualReason: fmt.Sprintf("self-update unsupported on %s", runtime.GOOS),
66 }
67 }
68 }
69
70 // profileForManifest adjusts a detected profile against the published assets:
71 // deb installs need native_packages; portable needs platforms.
72 func profileForManifest(base installProfile, m *update.Manifest) installProfile {
73 if m == nil {
74 return base
75 }
76 switch base.Mode {
77 case installModeDeb:
78 if _, ok := m.NativePackage(); !ok {
79 base.Mode = installModeManual
80 base.CanSelfUpdate = false
81 base.RequiresElev = false
82 base.ArtifactKind = ""
83 base.ManualReason = "this update does not include a Debian package; install manually from the download page"
84 return base
85 }
86 if !linuxDebHelperReady() {
87 base.Mode = installModeManual
88 base.CanSelfUpdate = false
89 base.RequiresElev = false
90 base.ArtifactKind = ""
91 base.ManualReason = "system update helper is unavailable; install with: sudo apt install ./Reasonix-linux-amd64.deb"
92 return base
93 }
94 base.ArtifactKind = artifactKindDeb
95 base.CanSelfUpdate = true
96 base.RequiresElev = true
97 return base
98 case installModePortable:
99 if _, ok := m.Asset(); !ok {
100 base.Mode = installModeManual
101 base.CanSelfUpdate = false
102 base.ArtifactKind = ""
103 base.ManualReason = "no update artifact is published for this platform"
104 return base
105 }
106 base.ArtifactKind = artifactKindTarball
107 return base
108 default:
109 return base
110 }
111 }
112
113 // selectUpdateAsset returns the downloadable asset for the active install profile.
114 func selectUpdateAsset(m *update.Manifest, profile installProfile) (update.Asset, string, bool) {
115 switch profile.Mode {
116 case installModeDeb:
117 if a, ok := m.NativePackage(); ok {
118 return a, artifactKindDeb, true
119 }
120 case installModePortable:
121 if a, ok := m.Asset(); ok {
122 return a, artifactKindTarball, true
123 }
124 }
125 return update.Asset{}, "", false
126 }
127
128 func linuxDebHelperReady() bool {
129 if runtime.GOOS != "linux" {
130 return false
131 }
132 if _, err := os.Stat(linuxUpdateHelperPath); err != nil {
133 return false
134 }
135 if _, err := os.Stat(linuxPkexecPath); err != nil {
136 return false
137 }
138 return true
139 }
140
141 func manualDebInstallHint() string {
142 return "Install manually with: sudo apt install ./Reasonix-linux-amd64.deb"
143 }
144
145 func artifactKindFromMeta(kind string) string {
146 switch strings.ToLower(strings.TrimSpace(kind)) {
147 case artifactKindDeb:
148 return artifactKindDeb
149 case artifactKindTarball, "":
150 // Empty means legacy portable cache written before artifactKind existed.
151 return artifactKindTarball
152 default:
153 return kind
154 }
155 }
156
156 lines GO