-
-
Notifications
You must be signed in to change notification settings - Fork 81
/
self_updater.go
168 lines (137 loc) · 3.72 KB
/
self_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
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Installer, a cross platform gui/cli app for installing Vencord
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
package main
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"runtime"
"time"
"vencordinstaller/buildinfo"
)
var IsSelfOutdated = false
var SelfUpdateCheckDoneChan = make(chan bool, 1)
func init() {
//goland:noinspection GoBoolExpressions
if buildinfo.InstallerTag == buildinfo.VersionUnknown {
Log.Debug("Disabling self updater as this is not a release build")
return
}
go DeleteOldExecutable()
go func() {
Log.Debug("Checking for Installer Updates...")
res, err := GetGithubRelease(InstallerReleaseUrl, InstallerReleaseUrlFallback)
if err != nil {
Log.Warn("Failed to check for self updates:", err)
SelfUpdateCheckDoneChan <- false
} else {
IsSelfOutdated = res.TagName != buildinfo.InstallerTag
Log.Debug("Is self outdated?", IsSelfOutdated)
SelfUpdateCheckDoneChan <- true
}
}()
}
func GetInstallerDownloadLink() string {
const BaseUrl = "https://github.com/Vencord/Installer/releases/latest/download/"
switch runtime.GOOS {
case "windows":
filename := Ternary(buildinfo.UiType == buildinfo.UiTypeCli, "VencordInstallerCli.exe", "VencordInstaller.exe")
return BaseUrl + filename
case "darwin":
return BaseUrl + "VencordInstaller.MacOS.zip"
case "linux":
return BaseUrl + "VencordInstallerCli-linux"
default:
return ""
}
}
func CanUpdateSelf() bool {
//goland:noinspection GoBoolExpressions
return IsSelfOutdated && runtime.GOOS != "darwin"
}
func UpdateSelf() error {
if !CanUpdateSelf() {
return errors.New("Cannot update self. Either no update available or macos")
}
url := GetInstallerDownloadLink()
if url == "" {
return errors.New("Failed to get installer download link")
}
Log.Debug("Updating self from", url)
ownExePath, err := os.Executable()
if err != nil {
return err
}
ownExeDir := path.Dir(ownExePath)
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
tmp, err := os.CreateTemp(ownExeDir, "VencordInstallerUpdate")
if err != nil {
return fmt.Errorf("Failed to create tempfile: %w", err)
}
defer func() {
_ = tmp.Close()
_ = os.Remove(tmp.Name())
}()
if err = tmp.Chmod(0o755); err != nil {
return fmt.Errorf("Failed to chmod 755", tmp.Name()+":", err)
}
if _, err = io.Copy(tmp, res.Body); err != nil {
return err
}
if err = tmp.Close(); err != nil {
return err
}
if err = os.Remove(ownExePath); err != nil {
if err = os.Rename(ownExePath, ownExePath+".old"); err != nil {
return fmt.Errorf("Failed to remove/rename own executable: %w", err)
}
}
if err = os.Rename(tmp.Name(), ownExePath); err != nil {
return fmt.Errorf("Failed to replace self with updated executable. Please manually redownload the installer: %w", err)
}
return nil
}
func DeleteOldExecutable() {
ownExePath, err := os.Executable()
if err != nil {
return
}
for attempts := 0; attempts < 10; attempts += 1 {
err = os.Remove(ownExePath + ".old")
if err == nil || errors.Is(err, os.ErrNotExist) {
break
}
Log.Warn("Failed to remove old executable. Retrying in 1 second.", err)
time.Sleep(1 * time.Second)
}
}
func RelaunchSelf() error {
attr := new(os.ProcAttr)
attr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
var argv []string
if len(os.Args) > 1 {
argv = os.Args[1:]
} else {
argv = []string{}
}
Log.Debug("Restarting self with exe", os.Args[0], "and args", argv)
proc, err := os.StartProcess(os.Args[0], argv, attr)
if err != nil {
return fmt.Errorf("Failed to start new process: %w", err)
}
if err = proc.Release(); err != nil {
return fmt.Errorf("Failed to release new process: %w", err)
}
os.Exit(0)
return nil
}