forked from sealdice/sealdice-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
update_updater.go
208 lines (179 loc) · 4.78 KB
/
update_updater.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package main
import (
"context"
"errors"
"net/http"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"time"
"go.uber.org/zap"
"sealdice-core/dice"
"sealdice-core/utils"
)
const updaterVersion = "0.1.1"
func checkURLOne(url string, wg *sync.WaitGroup, resultChan chan string) {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), 7*time.Second)
defer cancel()
resp, err := http.Get(url) //nolint:gosec
if err != nil {
// URL 请求异常
return
}
defer resp.Body.Close()
<-ctx.Done()
// URL 可用,但已经超过 7 秒,强制中断
resultChan <- url
}
// 检查一组URL是否可用,返回可用的URL
func checkURLs(urls []string) []string {
var wg sync.WaitGroup
resultChan := make(chan string, len(urls))
for _, url := range urls {
wg.Add(1)
go checkURLOne(url, &wg, resultChan)
}
wg.Wait()
close(resultChan)
var availableURLs []string
for result := range resultChan {
availableURLs = append(availableURLs, result)
}
return availableURLs
}
func getUpdaterFn() string {
fn := "./seal-updater.exe"
if runtime.GOOS != "windows" {
fn = "./seal-updater"
}
return fn
}
func CheckUpdater(dm *dice.DiceManager) error {
// 检查updater是否存在
exists := false
fn := getUpdaterFn()
if _, err := os.Stat(fn); err == nil {
logger.Info("检测到海豹更新程序")
exists = true
}
// 获取updater版本
isUpdaterOk := false
if exists {
err := os.Chmod(fn, 0o755)
if err != nil {
logger.Error("设置升级程序执行权限失败", err.Error())
}
cmd := exec.Command(fn, "--version")
out, err := cmd.Output()
if err != nil {
logger.Error("获取升级程序版本失败")
} else {
ver := strings.TrimSpace(string(out))
logger.Info("升级程序版本:", ver)
if ver == "seal-updater "+updaterVersion {
isUpdaterOk = true
}
}
}
// 如果升级程序不可用,那么下载一个
if !isUpdaterOk {
logger.Info("未检测到可用更新程序,开始下载")
err := downloadUpdater(dm)
if err != nil {
logger.Error("下载更新程序失败")
return errors.New("下载更新程序失败,无可用更新程序")
} else {
logger.Info("下载更新程序成功")
err := os.Chmod(fn, 0o755)
if err != nil {
logger.Error("设置升级程序执行权限失败", err.Error())
}
}
}
return nil
}
func downloadUpdater(dm *dice.DiceManager) error {
ver := dm.AppVersionOnline
platform := runtime.GOOS
arch := runtime.GOARCH
prefix := "http://dice.weizaima.com/u/v" + updaterVersion
if ver != nil {
prefix = ver.UpdaterURLPrefix
}
link := prefix + "/" + "seal-updater-" + platform + "-" + arch
// 如无法访问,尝试使用备用地址,但此地址不保证可用
if len(checkURLs([]string{link})) == 0 {
prefix := "https://d1.sealdice.com/u/v" + updaterVersion
link = prefix + "/" + "seal-updater-" + platform + "-" + arch
}
fn := "./seal-updater"
if platform == "windows" {
fn += ".exe"
link += ".exe"
}
err := utils.DownloadFile(fn, link)
if err != nil {
return err
}
return nil
}
func UpdateByFile(dm *dice.DiceManager, log *zap.SugaredLogger, packName string, syncMode bool) bool {
// 注意: 当执行完就立即退进程的情况下,需要使用 syncMode 为true
if log == nil {
log = logger
}
fn := getUpdaterFn()
err := os.Chmod(fn, 0o755)
if err != nil {
log.Error("设置升级程序执行权限失败", err.Error())
}
log.Infof("升级程序: 预计使用 %s 进行升级", packName)
// 创建一个具有5秒超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // 确保在函数结束时取消上下文
args := []string{"--upgrade", packName, "--pid", strconv.Itoa(os.Getpid())}
cmd := exec.CommandContext(ctx, fn, args...)
out, err := cmd.CombinedOutput()
if err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
log.Info("升级程序: 验证成功,进入下一阶段,即将退出进程")
updateFunc := func() {
if runtime.GOOS == "windows" {
log.Info("升级程序: 参数 ", args)
cmd := executeWin(fn, args...)
errStart := cmd.Start()
if errStart != nil {
log.Error("升级程序: 执行失败 ", errStart.Error())
return
}
} else {
args = append([]string{fn}, args...)
log.Info("升级程序: 参数 ", args)
errStart := syscall.Exec(fn, args, os.Environ())
if errStart != nil {
log.Error("升级程序: 执行失败 ", errStart.Error())
return
}
}
time.Sleep(5 * time.Second)
cleanupCreate(dm)()
os.Exit(0)
}
if syncMode {
updateFunc()
} else {
go updateFunc()
}
return true
} else {
log.Info("升级程序: 命令执行失败 ", err.Error())
log.Info("升级程序: 详情 ", string(out))
}
}
return false
}