-
Notifications
You must be signed in to change notification settings - Fork 0
/
updates.go
302 lines (245 loc) · 8.2 KB
/
updates.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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package updates
import (
"errors"
"fmt"
"os"
"time"
config "github.com/ipfs/go-ipfs/repo/config"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
u "github.com/ipfs/go-ipfs/util"
semver "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
update "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update"
check "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update/check"
)
const (
// Version is the current application's version literal
Version = config.CurrentVersionNumber
updateEndpointURL = "https://api.equinox.io/1/Updates"
updateAppID = "ap_YM8nz6rGm1UPg_bf63Lw6Vjz49"
// this is @jbenet's equinox.io public key.
updatePubKey = `-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxnwPPE4LNMjTfW/NRz1z
8uAPpwGYSzac+cwZbHbL5xFOxeX301GCdISaMm+Q8OEJqLyXfjYSuRwx00fDzWDD
ajBQOsxO08gTy1i/ow5YdEO+nYeVKO08fQFqVqdTz09BCgzt9iQJTEMeiq1kSWNo
al8usHD4SsNTxwDpSlok5UKWCHcr7D/TWX5A4B5A6ae9HSEcMB4Aum83k63Vzgm1
WTUvK0ed1zd0/KcHqIU36VZpVg4PeV4SWnOBnldQ98CWg/Mnqp3+lXMWYWTmXeX6
xj8JqOGpebzlxeISKE6fDBtrLxUbFTt3DNshl7S5CUGuc5H1MF1FTAyi+8u/nEZB
cQIDAQAB
-----END RSA PUBLIC KEY-----`
/*
You can verify the key above (updatePubKey) is indeed controlled
by @jbenet, ipfs author, with the PGP signed message below. You
can verify it in the commandline, or keybase.io.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
I hereby certify that I control the private key matching the
following public key. This is a key used for go-ipfs auto-updates
over equinox.io. - @jbenet
- -----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxnwPPE4LNMjTfW/NRz1z
8uAPpwGYSzac+cwZbHbL5xFOxeX301GCdISaMm+Q8OEJqLyXfjYSuRwx00fDzWDD
ajBQOsxO08gTy1i/ow5YdEO+nYeVKO08fQFqVqdTz09BCgzt9iQJTEMeiq1kSWNo
al8usHD4SsNTxwDpSlok5UKWCHcr7D/TWX5A4B5A6ae9HSEcMB4Aum83k63Vzgm1
WTUvK0ed1zd0/KcHqIU36VZpVg4PeV4SWnOBnldQ98CWg/Mnqp3+lXMWYWTmXeX6
xj8JqOGpebzlxeISKE6fDBtrLxUbFTt3DNshl7S5CUGuc5H1MF1FTAyi+8u/nEZB
cQIDAQAB
- -----END RSA PUBLIC KEY-----
-----BEGIN PGP SIGNATURE-----
Version: Keybase OpenPGP v1.1.3
Comment: https://keybase.io/crypto
wsFcBAABCgAGBQJUSCX8AAoJEFYC7bhkX9ftBcwQAJuYGSECSKFATJ1wK+zAGUH5
xEbX+yaCYj0PwzJO4Ntu2ifK68ANacKy/GiXdJYeQk7pq21UT0fcn0Uq39URu+Xb
lk3t1YZazjY7wB03jBjcMIaO2TUsWbGIBZAEZjyVDDctDUM0krCd1GIOw6Fbndva
pevlGIA55ewvXYxcWdRyOGWiqd9DKNnmi9UF0XsdpCtDFSkdjnqkqbTRxF6Jw5gI
EAF2E7mU8emDTNgtpCs0ACmEUXVVEEhF9TuR/YdX1m/715TYkkYCii6uV9vSVQd8
nOrDDTrWSjlF6Ms+dYGCheWIjKQcykn9IW021AzVN1P7Mt9qtmDNfZ0VQL3zl/fs
zZ1IHBW7BzriQ4GzWXg5GWpTSz/REvUEfKNVuDV9jX7hv67B5H6qTL5+2zljPEKv
lCas04cCMmEpJUj4qK95hdKQzKJ8b7MrRf/RFYyViRGdxvR+lgGqJ7Yca8es2kCe
XV6c+i6a7X89YL6ZVU+1MlvPwngu0VG+VInH/w9KrNYrLFhfVRiruRbkBkHDXjnU
b4kPqaus+7g0DynCk7A2kTMa3cgtO20CZ9MBJFEPqRRHHksjHVmlxPb42bB348aR
UVsWkRRYOmRML7avTgkX8WFsmdZ1d7E7aQLYnCIel85+5iP7hWyNtEMsAHk02XCL
AAb7RaEDNJOa7qvUFecB
=mzPY
-----END PGP SIGNATURE-----
*/
)
var log = u.Logger("updates")
var currentVersion *semver.Version
// ErrNoUpdateAvailable returned when a check fails to find a newer update.
var ErrNoUpdateAvailable = check.NoUpdateAvailable
func init() {
var err error
currentVersion, err = parseVersion()
if err != nil {
log.Fatalf("invalid version number in code (must be semver): %q", Version)
}
log.Infof("go-ipfs Version: %s", currentVersion)
}
func parseVersion() (*semver.Version, error) {
return semver.NewVersion(Version)
}
// CheckForUpdate checks the equinox.io api if there is an update available
// NOTE: if equinox says there is a new update, but the version number IS NOT
// larger, we interpret that as no update (you may have gotten a newer version
// by building it yourself).
func CheckForUpdate() (*check.Result, error) {
param := check.Params{
AppVersion: Version,
AppId: updateAppID,
Channel: "stable",
}
up, err := update.New().VerifySignatureWithPEM([]byte(updatePubKey))
if err != nil {
return nil, fmt.Errorf("Failed to parse public key: %v", err)
}
res, err := param.CheckForUpdate(updateEndpointURL, up)
if err != nil {
return res, err
}
newer, err := versionIsNewer(res.Version)
if err != nil {
return nil, err
}
if !newer {
return nil, ErrNoUpdateAvailable
}
return res, err
}
// Apply cheks if the running process is able to update itself
// and than updates to the passed release
func Apply(rel *check.Result) error {
if err := update.New().CanUpdate(); err != nil {
return err
}
if err, errRecover := rel.Update(); err != nil {
err = fmt.Errorf("Update failed: %v\n", err)
if errRecover != nil {
err = fmt.Errorf("%s\nRecovery failed! Cause: %v\nYou may need to recover manually", err, errRecover)
}
return err
}
return nil
}
// ShouldAutoUpdate decides wether a new version should be applied
// checks against config setting and new version string. returns false in case of error
func ShouldAutoUpdate(setting config.AutoUpdateSetting, newVer string) bool {
if setting == config.AutoUpdateNever {
return false
}
nv, err := semver.NewVersion(newVer)
if err != nil {
log.Infof("could not parse version string: %s", err)
return false
}
n := nv.Slice()
c := currentVersion.Slice()
switch setting {
case config.AutoUpdatePatch:
if n[0] < c[0] {
return false
}
if n[1] < c[1] {
return false
}
return n[2] > c[2]
case config.AutoUpdateMinor:
if n[0] != c[0] {
return false
}
return n[1] > c[1] || (n[1] == c[1] && n[2] > c[2])
case config.AutoUpdateMajor:
for i := 0; i < 3; i++ {
if n[i] < c[i] {
return false
}
}
return true
}
return false
}
// CliCheckForUpdates is the automatic update check from the commandline.
func CliCheckForUpdates(cfg *config.Config, repoPath string) error {
// if config says not to, don't check for updates
if !cfg.Version.ShouldCheckForUpdate() {
log.Info("update check skipped.")
return nil
}
log.Info("checking for update")
u, err := CheckForUpdate()
// if there is no update available, record it, and exit. NB: only record
// if we checked successfully.
if err == ErrNoUpdateAvailable {
log.Infof("No update available, checked on %s", time.Now())
r, err := fsrepo.Open(repoPath)
if err != nil {
return err
}
if err := recordUpdateCheck(cfg); err != nil {
return err
}
// NB: r's Config may be newer than cfg. This overwrites regardless.
r.SetConfig(cfg)
if err := r.Close(); err != nil {
return err
}
return nil
}
// if another, unexpected error occurred, note it.
if err != nil {
log.Debugf("Error while checking for update: %v", err)
return nil
}
// there is an update available
// if we autoupdate
if cfg.Version.AutoUpdate != config.AutoUpdateNever {
// and we should auto update
if ShouldAutoUpdate(cfg.Version.AutoUpdate, u.Version) {
log.Infof("Applying update %s", u.Version)
if err = Apply(u); err != nil {
log.Debug(err)
return nil
}
// BUG(cryptix): no good way to restart yet. - tracking https://github.com/inconshreveable/go-update/issues/5
fmt.Printf("update %v applied. please restart.\n", u.Version)
os.Exit(0)
}
}
// autoupdate did not exit, so regular notices.
switch cfg.Version.Check {
case config.CheckError:
return fmt.Errorf(errShouldUpdate, Version, u.Version)
case config.CheckWarn:
// print the warning
fmt.Printf("New version available: %s\n", u.Version)
default: // ignore
}
return nil
}
func versionIsNewer(version string) (bool, error) {
nv, err := semver.NewVersion(version)
if err != nil {
return false, fmt.Errorf("could not parse version string: %s", err)
}
cv := currentVersion
newer := !nv.LessThan(*cv) && nv.String() != cv.String()
return newer, nil
}
var errShouldUpdate = `
Your go-ipfs version is: %s
There is a new version available: %s
Since this is alpha software, it is strongly recommended you update.
To update, run:
ipfs update apply
To disable this notice, run:
ipfs config Version.Check warn
`
// recordUpdateCheck is called to record that an update check was performed,
// showing that the running version is the most recent one.
func recordUpdateCheck(cfg *config.Config) error {
cfg.Version.CheckDate = time.Now()
if cfg.Version.CheckPeriod == "" {
// CheckPeriod was not initialized for some reason (e.g. config file broken)
return errors.New("config.Version.CheckPeriod not set. config broken?")
}
return nil
}