Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 3ba1bed

Browse files
committed
internal/cmd/update.go: query github releases api for assets
1 parent 2002876 commit 3ba1bed

File tree

2 files changed

+96
-35
lines changed

2 files changed

+96
-35
lines changed

internal/cmd/update.go

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ import (
3232
"github.com/spf13/cobra"
3333
)
3434

35+
const (
36+
goosWindows = "windows"
37+
goosLinux = "linux"
38+
apiPrivateVersion = "/api/private/version"
39+
)
40+
3541
// updater updates coder-cli.
3642
type updater struct {
3743
confirmF func(string) (string, error)
@@ -143,7 +149,10 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi
143149
}
144150
}
145151

146-
downloadURL := makeDownloadURL(desiredVersion, u.osF(), runtime.GOARCH)
152+
downloadURL, err := queryGithubAssetURL(u.httpClient, desiredVersion, u.osF())
153+
if err != nil {
154+
return clog.Fatal("failed to query github assets url", clog.Causef(err.Error()))
155+
}
147156

148157
var downloadBuf bytes.Buffer
149158
memWriter := bufio.NewWriter(&downloadBuf)
@@ -262,15 +271,7 @@ func defaultConfirm(label string) (string, error) {
262271
return p.Run()
263272
}
264273

265-
func makeDownloadURL(version *semver.Version, ostype, arch string) string {
266-
const template = "https://github.com/cdr/coder-cli/releases/download/v%s/coder-cli-%s-%s.%s"
267-
var ext string
268-
switch ostype {
269-
case "linux":
270-
ext = "tar.gz"
271-
default:
272-
ext = "zip"
273-
}
274+
func queryGithubAssetURL(httpClient getter, version *semver.Version, ostype string) (string, error) {
274275
var b bytes.Buffer
275276
fmt.Fprintf(&b, "%d", version.Major())
276277
fmt.Fprint(&b, ".")
@@ -282,7 +283,41 @@ func makeDownloadURL(version *semver.Version, ostype, arch string) string {
282283
fmt.Fprint(&b, version.Prerelease())
283284
}
284285

285-
return fmt.Sprintf(template, b.String(), ostype, arch, ext)
286+
urlString := fmt.Sprintf("https://api.github.com/repos/cdr/coder-cli/releases/tags/v%s", b.String())
287+
clog.LogInfo("query github releases", fmt.Sprintf("url: %q", urlString))
288+
289+
type asset struct {
290+
BrowserDownloadURL string `json:"browser_download_url"`
291+
Name string `json:"name"`
292+
}
293+
type release struct {
294+
Assets []asset `json:"assets"`
295+
}
296+
var r release
297+
298+
resp, err := httpClient.Get(urlString)
299+
if err != nil {
300+
return "", xerrors.Errorf("query github release url %s: %w", urlString, err)
301+
}
302+
defer resp.Body.Close()
303+
304+
err = json.NewDecoder(resp.Body).Decode(&r)
305+
if err != nil {
306+
return "", xerrors.Errorf("unmarshal github releases api response: %w", err)
307+
}
308+
309+
var assetURLStr string
310+
for _, a := range r.Assets {
311+
if strings.HasPrefix(a.Name, "coder-cli-"+ostype) {
312+
assetURLStr = a.BrowserDownloadURL
313+
}
314+
}
315+
316+
if assetURLStr == "" {
317+
return "", xerrors.Errorf("could not find release for ostype %s", ostype)
318+
}
319+
320+
return assetURLStr, nil
286321
}
287322

288323
func extractFromArchive(path string, archive []byte) ([]byte, error) {

internal/cmd/update_test.go

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,24 @@ import (
2222
)
2323

2424
const (
25-
fakeExePathLinux = "/home/user/bin/coder"
26-
fakeExePathWindows = `C:\Users\user\bin\coder.exe`
27-
fakeCoderURL = "https://my.cdr.dev"
28-
fakeNewVersion = "1.23.4-rc.5+678-gabcdef-12345678"
29-
fakeOldVersion = "1.22.4-rc.5+678-gabcdef-12345678"
30-
fakeReleaseURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/coder-cli-linux-amd64.tar.gz"
31-
fakeReleaseURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/coder-cli-windows-amd64.zip"
32-
goosWindows = "windows"
33-
goosLinux = "linux"
25+
fakeExePathLinux = "/home/user/bin/coder"
26+
fakeExePathWindows = `C:\Users\user\bin\coder.exe`
27+
fakeCoderURL = "https://my.cdr.dev"
28+
fakeNewVersion = "1.23.4-rc.5+678-gabcdef-12345678"
29+
fakeOldVersion = "1.22.4-rc.5+678-gabcdef-12345678"
30+
filenameLinux = "coder-cli-linux-amd64.tar.gz"
31+
filenameWindows = "coder-cli-windows.zip"
32+
fakeGithubReleaseURL = "https://api.github.com/repos/cdr/coder-cli/releases/tags/v1.23.4-rc.5"
3433
)
3534

3635
var (
37-
apiPrivateVersionURL = fakeCoderURL + "/api/private/version"
38-
fakeError = xerrors.New("fake error for testing")
39-
fakeNewVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeNewVersion)
40-
fakeOldVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeOldVersion)
36+
apiPrivateVersionURL = fakeCoderURL + apiPrivateVersion
37+
fakeError = xerrors.New("fake error for testing")
38+
fakeNewVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeNewVersion)
39+
fakeOldVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeOldVersion)
40+
fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux
41+
fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows
42+
fakeGithubReleaseJSON = fmt.Sprintf(`{"assets":[{"name":%q,"browser_download_url":%q},{"name":%q,"browser_download_url":%q}]}`, filenameLinux, fakeAssetURLLinux, filenameWindows, fakeAssetURLWindows)
4143
)
4244

4345
func Test_updater_run(t *testing.T) {
@@ -113,7 +115,8 @@ func Test_updater_run(t *testing.T) {
113115
run(t, "update coder - explicit version specified", func(t *testing.T, p *params) {
114116
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
115117
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeOldVersionJSON), 200, variadicS(), nil)
116-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
118+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
119+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
117120
p.VersionF = func() string { return fakeOldVersion }
118121
p.ConfirmF = fakeConfirmYes
119122
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil}
@@ -127,7 +130,8 @@ func Test_updater_run(t *testing.T) {
127130
run(t, "update coder - old to new", func(t *testing.T, p *params) {
128131
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
129132
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
130-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
133+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
134+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
131135
p.VersionF = func() string { return fakeOldVersion }
132136
p.ConfirmF = fakeConfirmYes
133137
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil}
@@ -142,7 +146,8 @@ func Test_updater_run(t *testing.T) {
142146
p.ExecutablePath = "/home/user/bin/coder-cli"
143147
fakeFile(t, p.Fakefs, p.ExecutablePath, 0755, fakeOldVersion)
144148
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
145-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
149+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
150+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
146151
p.VersionF = func() string { return fakeOldVersion }
147152
p.ConfirmF = fakeConfirmYes
148153
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil}
@@ -158,7 +163,8 @@ func Test_updater_run(t *testing.T) {
158163
p.ExecutablePath = fakeExePathWindows
159164
fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion)
160165
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
161-
p.HTTPClient.M[fakeReleaseURLWindows] = newFakeGetterResponse(fakeValidZipBytes, 200, variadicS(), nil)
166+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
167+
p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeValidZipBytes, 200, variadicS(), nil)
162168
p.VersionF = func() string { return fakeOldVersion }
163169
p.ConfirmF = fakeConfirmYes
164170
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil}
@@ -172,7 +178,8 @@ func Test_updater_run(t *testing.T) {
172178
run(t, "update coder - old to new forced", func(t *testing.T, p *params) {
173179
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
174180
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
175-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
181+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
182+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
176183
p.VersionF = func() string { return fakeOldVersion }
177184
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil}
178185
u := fromParams(p)
@@ -240,10 +247,24 @@ func Test_updater_run(t *testing.T) {
240247
assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeOldVersion)
241248
})
242249

250+
run(t, "update coder - failed to query github releases", func(t *testing.T, p *params) {
251+
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
252+
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
253+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte{}, 0, variadicS(), fakeError)
254+
p.VersionF = func() string { return fakeOldVersion }
255+
p.ConfirmF = fakeConfirmYes
256+
u := fromParams(p)
257+
assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeOldVersion)
258+
err := u.Run(p.Ctx, false, fakeCoderURL, "")
259+
assertCLIError(t, "update coder - failed to query github releases", err, "failed to query github assets", fakeError.Error())
260+
assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeOldVersion)
261+
})
262+
243263
run(t, "update coder - failed to fetch URL", func(t *testing.T, p *params) {
244264
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
245265
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
246-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 0, variadicS(), fakeError)
266+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
267+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 0, variadicS(), fakeError)
247268
p.VersionF = func() string { return fakeOldVersion }
248269
p.ConfirmF = fakeConfirmYes
249270
u := fromParams(p)
@@ -256,7 +277,8 @@ func Test_updater_run(t *testing.T) {
256277
run(t, "update coder - release URL 404", func(t *testing.T, p *params) {
257278
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
258279
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
259-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 404, variadicS(), nil)
280+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
281+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 404, variadicS(), nil)
260282
p.VersionF = func() string { return fakeOldVersion }
261283
p.ConfirmF = fakeConfirmYes
262284
u := fromParams(p)
@@ -269,7 +291,8 @@ func Test_updater_run(t *testing.T) {
269291
run(t, "update coder - invalid tgz archive", func(t *testing.T, p *params) {
270292
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
271293
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
272-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
294+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
295+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
273296
p.VersionF = func() string { return fakeOldVersion }
274297
p.ConfirmF = fakeConfirmYes
275298
u := fromParams(p)
@@ -284,7 +307,8 @@ func Test_updater_run(t *testing.T) {
284307
p.ExecutablePath = fakeExePathWindows
285308
fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion)
286309
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
287-
p.HTTPClient.M[fakeReleaseURLWindows] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
310+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
311+
p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
288312
p.VersionF = func() string { return fakeOldVersion }
289313
p.ConfirmF = fakeConfirmYes
290314
u := fromParams(p)
@@ -299,7 +323,8 @@ func Test_updater_run(t *testing.T) {
299323
p.Fakefs = afero.NewReadOnlyFs(rwfs)
300324
fakeFile(t, rwfs, fakeExePathLinux, 0755, fakeOldVersion)
301325
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
302-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
326+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
327+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
303328
p.VersionF = func() string { return fakeOldVersion }
304329
p.ConfirmF = fakeConfirmYes
305330
u := fromParams(p)
@@ -312,7 +337,8 @@ func Test_updater_run(t *testing.T) {
312337
run(t, "update coder - cannot exec new binary", func(t *testing.T, p *params) {
313338
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
314339
p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil)
315-
p.HTTPClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
340+
p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil)
341+
p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
316342
p.VersionF = func() string { return fakeOldVersion }
317343
p.ConfirmF = fakeConfirmYes
318344
p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{nil, fakeError}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy