From 054f1fb8833589f755557bf2abb59c25ef66ca7c Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 10:39:32 +0000 Subject: [PATCH 1/7] refactor: stop hard-coding tgz bytes in unit test --- internal/cmd/update_test.go | 75 ++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/internal/cmd/update_test.go b/internal/cmd/update_test.go index 6f0a63c1..e6d67a88 100644 --- a/internal/cmd/update_test.go +++ b/internal/cmd/update_test.go @@ -1,10 +1,13 @@ package cmd import ( + "archive/tar" "bytes" + "compress/gzip" "context" "encoding/base64" "fmt" + "io" "io/fs" "io/ioutil" "net/http" @@ -39,6 +42,7 @@ var ( fakeError = xerrors.New("fake error for testing") fakeNewVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeNewVersion) fakeOldVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeOldVersion) + fakeNewVersionTgz = mustValidTgz("coder", []byte(fakeNewVersion), 0751) fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows fakeGithubReleaseJSON = fmt.Sprintf(`{"assets":[{"name":%q,"browser_download_url":%q},{"name":%q,"browser_download_url":%q}]}`, filenameLinux, fakeAssetURLLinux, filenameWindows, fakeAssetURLWindows) @@ -133,7 +137,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeOldVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -150,7 +154,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeOldVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -165,7 +169,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -182,7 +186,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -198,7 +202,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, p.ExecutablePath, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -230,7 +234,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} u := fromParams(p) @@ -375,7 +379,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, rwfs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes u := fromParams(p) @@ -389,7 +393,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{nil, fakeError} @@ -574,12 +578,6 @@ func assertCLIError(t *testing.T, name string, err error, expectedHeader, expect } } -// this is a valid tgz archive containing a single file named 'coder' with permissions 0751 -// containing the string "1.23.4-rc.5+678-gabcdef-12345678". -var fakeValidTgzBytes, _ = base64.StdEncoding.DecodeString(`H4sIAAAAAAAAA+3QsQ4CIRCEYR6F3oC7wIqvc3KnpQnq+3tGCwsTK3LN/zWTTDWZuG/XeeluJFlV -s1dqNfnOtyJOi4qllHOuTlSTqPMydNXH43afuvfu3w3jb9qExpRjCb1F2x3qMVymU5uXc9CUi63F -1vsAAAAAAAAAAAAAAAAAAL89AYuL424AKAAA`) - // this is a valid zip archive containing a single file named 'coder.exe' with permissions 0751 // containing the string "1.23.4-rc.5+678-gabcdef-12345678". var fakeValidZipBytes, _ = base64.StdEncoding.DecodeString(`UEsDBAoAAAAAAAtfDVNCHNDCIAAAACAAAAAJABwAY29kZXIuZXhlVVQJAAPmXRZh/10WYXV4CwAB @@ -587,6 +585,55 @@ BOgDAAAE6AMAADEuMjMuNC1yYy41KzY3OC1nYWJjZGVmLTEyMzQ1Njc4UEsBAh4DCgAAAAAAC18N U0Ic0MIgAAAAIAAAAAkAGAAAAAAAAQAAAO2BAAAAAGNvZGVyLmV4ZVVUBQAD5l0WYXV4CwABBOgD AAAE6AMAAFBLBQYAAAAAAQABAE8AAABjAAAAAAA=`) +// mustValidTgz creates a valid tgz file and panics if any error is encountered. +// only for use in unit tests. +func mustValidTgz(filename string, data []byte, perms os.FileMode) []byte { + must := func(err error, msg string) { + if err != nil { + panic(xerrors.Errorf("%s: %w", msg, err)) + } + } + fs := afero.NewMemMapFs() + // populate memfs with file + f, err := fs.Create(filename) + must(err, "create file") + _, err = f.Write(data) + must(err, "write data") + err = f.Close() + must(err, "close file") + err = fs.Chmod(filename, perms) + must(err, "set perms") + + // create archive from fs + + f, err = fs.Open(filename) + must(err, "open file") + fsinfo, err := f.Stat() + must(err, "stat file") + header, err := tar.FileInfoHeader(fsinfo, fsinfo.Name()) + must(err, "create tar header") + header.Name = filename + + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + tw := tar.NewWriter(gw) + + err = tw.WriteHeader(header) + must(err, "write header") + _, err = io.Copy(tw, f) + must(err, "write file") + err = f.Close() + must(err, "close file") + err = tw.Close() + must(err, "close tar writer") + err = gw.Close() + must(err, "close gzip writer") + + return buf.Bytes() +} + +var _ = mustValidTgz("testing", []byte("testing"), 0777) + type fakeExecer struct { M map[string]fakeExecerResult T *testing.T From 66d7fb9367969dda46faf6c0f1a4868b9a7a5f87 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 10:48:48 +0000 Subject: [PATCH 2/7] refactor: stop hard-coding zip data in unit tests --- internal/cmd/update_test.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/cmd/update_test.go b/internal/cmd/update_test.go index e6d67a88..87b1bda2 100644 --- a/internal/cmd/update_test.go +++ b/internal/cmd/update_test.go @@ -2,10 +2,10 @@ package cmd import ( "archive/tar" + "archive/zip" "bytes" "compress/gzip" "context" - "encoding/base64" "fmt" "io" "io/fs" @@ -43,6 +43,7 @@ var ( fakeNewVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeNewVersion) fakeOldVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeOldVersion) fakeNewVersionTgz = mustValidTgz("coder", []byte(fakeNewVersion), 0751) + fakeNewVersionZip = mustValidZip("coder.exe", []byte(fakeNewVersion)) fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows fakeGithubReleaseJSON = fmt.Sprintf(`{"assets":[{"name":%q,"browser_download_url":%q},{"name":%q,"browser_download_url":%q}]}`, filenameLinux, fakeAssetURLLinux, filenameWindows, fakeAssetURLWindows) @@ -219,7 +220,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeValidZipBytes, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeNewVersionZip, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -578,13 +579,6 @@ func assertCLIError(t *testing.T, name string, err error, expectedHeader, expect } } -// this is a valid zip archive containing a single file named 'coder.exe' with permissions 0751 -// containing the string "1.23.4-rc.5+678-gabcdef-12345678". -var fakeValidZipBytes, _ = base64.StdEncoding.DecodeString(`UEsDBAoAAAAAAAtfDVNCHNDCIAAAACAAAAAJABwAY29kZXIuZXhlVVQJAAPmXRZh/10WYXV4CwAB -BOgDAAAE6AMAADEuMjMuNC1yYy41KzY3OC1nYWJjZGVmLTEyMzQ1Njc4UEsBAh4DCgAAAAAAC18N -U0Ic0MIgAAAAIAAAAAkAGAAAAAAAAQAAAO2BAAAAAGNvZGVyLmV4ZVVUBQAD5l0WYXV4CwABBOgD -AAAE6AMAAFBLBQYAAAAAAQABAE8AAABjAAAAAAA=`) - // mustValidTgz creates a valid tgz file and panics if any error is encountered. // only for use in unit tests. func mustValidTgz(filename string, data []byte, perms os.FileMode) []byte { @@ -632,7 +626,28 @@ func mustValidTgz(filename string, data []byte, perms os.FileMode) []byte { return buf.Bytes() } +// mustValidZip creates a valid zip file and panics if any error is encountered. +// only for use in unit tests. +func mustValidZip(filename string, data []byte) []byte { + must := func(err error, msg string) { + if err != nil { + panic(xerrors.Errorf("%s: %w", msg, err)) + } + } + var buf bytes.Buffer + zw := zip.NewWriter(&buf) + w, err := zw.Create(filename) + must(err, "create zip archive") + _, err = io.Copy(w, bytes.NewReader(data)) + must(err, "write file") + err = zw.Close() + must(err, "close gzip writer") + + return buf.Bytes() +} + var _ = mustValidTgz("testing", []byte("testing"), 0777) +var _ = mustValidZip("testing", []byte("testing")) type fakeExecer struct { M map[string]fakeExecerResult From 088e37812b6cbd2464d6aab108a34c40c231d821 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 10:58:55 +0000 Subject: [PATCH 3/7] feat(cli): update: handle cli hotfix versions: add red unit test --- internal/cmd/update_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/internal/cmd/update_test.go b/internal/cmd/update_test.go index 87b1bda2..ded18354 100644 --- a/internal/cmd/update_test.go +++ b/internal/cmd/update_test.go @@ -32,6 +32,7 @@ const ( fakeCoderURL = "https://my.cdr.dev" fakeNewVersion = "1.23.4-rc.5+678-gabcdef-12345678" fakeOldVersion = "1.22.4-rc.5+678-gabcdef-12345678" + fakeHotfixVersion = "1.23.4-rc.5+678-gabcdef-12345678.cli.2" filenameLinux = "coder-cli-linux-amd64.tar.gz" filenameWindows = "coder-cli-windows.zip" fakeGithubReleaseURL = "https://api.github.com/repos/cdr/coder-cli/releases/tags/v1.23.4-rc.5" @@ -42,7 +43,9 @@ var ( fakeError = xerrors.New("fake error for testing") fakeNewVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeNewVersion) fakeOldVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeOldVersion) + fakeHotfixVersionJSON = fmt.Sprintf(`{"version":%q}`, fakeHotfixVersion) fakeNewVersionTgz = mustValidTgz("coder", []byte(fakeNewVersion), 0751) + fakeHotfixVersionTgz = mustValidTgz("coder", []byte(fakeHotfixVersion), 0751) fakeNewVersionZip = mustValidZip("coder.exe", []byte(fakeNewVersion)) fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows @@ -198,6 +201,38 @@ func Test_updater_run(t *testing.T) { assertFileContent(t, p.Fakefs, fakeExePathLinux, strings.TrimPrefix(fakeNewVersion, "v")) // TODO: stop hard-coding this }) + run(t, "update coder - new to hotfix", func(t *testing.T, p *params) { + fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeNewVersion) + p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeHotfixVersionJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeHotfixVersionTgz, 200, variadicS(), nil) + p.VersionF = func() string { return fakeNewVersion } + p.ConfirmF = fakeConfirmYes + p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} + u := fromParams(p) + assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeNewVersion) + err := u.Run(p.Ctx, false, fakeCoderURL, "") + assert.Success(t, "update coder - new to hotfix", err) + assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeHotfixVersion) + }) + + run(t, "update coder - new to hotfix - windows", func(t *testing.T, p *params) { + p.OsF = func() string { return goosWindows } + p.ExecutablePath = fakeExePathWindows + fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeNewVersion) + p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeHotfixVersionJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeHotfixVersionTgz, 200, variadicS(), nil) + p.VersionF = func() string { return fakeNewVersion } + p.ConfirmF = fakeConfirmYes + p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} + u := fromParams(p) + assertFileContent(t, p.Fakefs, fakeExePathWindows, fakeNewVersion) + err := u.Run(p.Ctx, false, fakeCoderURL, "") + assert.Success(t, "update coder - new to hotfix", err) + assertFileContent(t, p.Fakefs, fakeExePathWindows, fakeHotfixVersion) + }) + run(t, "update coder - old to new - binary renamed", func(t *testing.T, p *params) { p.ExecutablePath = "/home/user/bin/coder-cli" fakeFile(t, p.Fakefs, p.ExecutablePath, 0755, fakeOldVersion) From 31e85d58d7422ce38c479f6f66b294f251b77882 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 12:19:44 +0000 Subject: [PATCH 4/7] feat(cli): compare cli.N build metadata to support hotfixes --- internal/cmd/update.go | 66 ++++++++++++++++++++++++++++++++++++- internal/cmd/update_test.go | 31 ++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/internal/cmd/update.go b/internal/cmd/update.go index c9b94d80..1c3193ec 100644 --- a/internal/cmd/update.go +++ b/internal/cmd/update.go @@ -17,7 +17,9 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "runtime" + "strconv" "strings" "time" @@ -139,7 +141,7 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi currentVersion, err := semver.NewVersion(u.versionF()) if err != nil { clog.LogWarn("failed to determine current version of coder-cli", clog.Causef(err.Error())) - } else if currentVersion.Compare(desiredVersion) == 0 { + } else if compareVersions(currentVersion, desiredVersion) == 0 { clog.LogInfo("Up to date!") return nil } @@ -493,3 +495,65 @@ func HasFilePathPrefix(s, prefix string) bool { func defaultExec(ctx context.Context, cmd string, args ...string) ([]byte, error) { return exec.CommandContext(ctx, cmd, args...).CombinedOutput() } + +// hotfixExpr matches the build metadata used for identifying CLI hotfixes. +var hotfixExpr = regexp.MustCompile(`(?i)^.*?cli\.(\d+).*?$`) + +// compareVersions performs a NON-SEMVER-COMPLIANT comparison of two versions. +// If the two versions differ as per SemVer, then that result is returned. +// Otherwise, the build metadata of the two versions are compared based on +// the `cli.N` hotfix metadata. +// +// Examples: +// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.0")) +// 0 +// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.1")) +// 1 +// compareVersions(semver.MustParse("v1.0.1"), semver.MustParse("v1.0.0")) +// -1 +// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0")) +// 1 +// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0+cli.0")) +// 0 +// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.0+cli.0")) +// -1 +// compareVersions(semver.MustParse("v1.0.0+cli.1"), semver.MustParse("v1.0.0+cli.0")) +// 1 +// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0+cli.1")) +// -1 +// +func compareVersions(a, b *semver.Version) int { + semverComparison := a.Compare(b) + if semverComparison != 0 { + return semverComparison + } + + matchA := hotfixExpr.FindStringSubmatch(a.Metadata()) + matchB := hotfixExpr.FindStringSubmatch(b.Metadata()) + + hotfixA := -1 + hotfixB := -1 + + // extract hotfix versions from the metadata of a and b + if len(matchA) > 1 { + if n, err := strconv.Atoi(matchA[1]); err == nil { + hotfixA = n + } + } + if len(matchB) > 1 { + if n, err := strconv.Atoi(matchB[1]); err == nil { + hotfixB = n + } + } + + // compare hotfix versions + if hotfixA < hotfixB { + return -1 + } + if hotfixA > hotfixB { + return 1 + } + // both versions are the same if their semver and hotfix + // metadata are the same. + return 0 +} diff --git a/internal/cmd/update_test.go b/internal/cmd/update_test.go index ded18354..4de384b0 100644 --- a/internal/cmd/update_test.go +++ b/internal/cmd/update_test.go @@ -47,6 +47,7 @@ var ( fakeNewVersionTgz = mustValidTgz("coder", []byte(fakeNewVersion), 0751) fakeHotfixVersionTgz = mustValidTgz("coder", []byte(fakeHotfixVersion), 0751) fakeNewVersionZip = mustValidZip("coder.exe", []byte(fakeNewVersion)) + fakeHotfixVersionZip = mustValidZip("coder.exe", []byte(fakeHotfixVersion)) fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows fakeGithubReleaseJSON = fmt.Sprintf(`{"assets":[{"name":%q,"browser_download_url":%q},{"name":%q,"browser_download_url":%q}]}`, filenameLinux, fakeAssetURLLinux, filenameWindows, fakeAssetURLWindows) @@ -222,7 +223,7 @@ func Test_updater_run(t *testing.T) { fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeNewVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeHotfixVersionJSON), 200, variadicS(), nil) p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeHotfixVersionTgz, 200, variadicS(), nil) + p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeHotfixVersionZip, 200, variadicS(), nil) p.VersionF = func() string { return fakeNewVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -501,6 +502,34 @@ func Test_getDesiredVersion(t *testing.T) { }) } +func Test_compareVersions(t *testing.T) { + t.Parallel() + + testCases := []struct { + Name string + V1 string + V2 string + Expected int + }{ + {"old vs old", fakeOldVersion, fakeOldVersion, 0}, + {"old vs new", fakeOldVersion, fakeNewVersion, -1}, + {"old vs hotfix", fakeOldVersion, fakeHotfixVersion, -1}, + {"new vs old", fakeNewVersion, fakeOldVersion, 1}, + {"new vs new", fakeNewVersion, fakeNewVersion, 0}, + {"new vs hotfix", fakeNewVersion, fakeHotfixVersion, -1}, + {"hotfix vs old", fakeHotfixVersion, fakeOldVersion, 1}, + {"hotfix vs new", fakeHotfixVersion, fakeNewVersion, 1}, + {"hotfix vs hotfix", fakeHotfixVersion, fakeHotfixVersion, 0}, + } + for _, testCase := range testCases { + testCase := testCase + v1 := semver.MustParse(testCase.V1) + v2 := semver.MustParse(testCase.V2) + actual := compareVersions(v1, v2) + assert.Equal(t, testCase.Name+": expected comparison differs", testCase.Expected, actual) + } +} + // fakeGetter mocks HTTP requests. type fakeGetter struct { M map[string]*fakeGetterResponse From a618b47b8b355780a0518ca1e6bfc26c000e090e Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 13:00:22 +0000 Subject: [PATCH 5/7] add hotfix versions to asset urls --- internal/cmd/update.go | 24 ++++++++++++++++- internal/cmd/update_test.go | 51 ++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/internal/cmd/update.go b/internal/cmd/update.go index 1c3193ec..4ab75996 100644 --- a/internal/cmd/update.go +++ b/internal/cmd/update.go @@ -147,10 +147,20 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi } if !force { - label := fmt.Sprintf("Do you want to download version %d.%d.%d instead", + prerelease := "" + if desiredVersion.Prerelease() != "" { + prerelease = "-" + desiredVersion.Prerelease() + } + hotfix := "" + if hotfixVersion(desiredVersion) != "" { + hotfix = "+" + hotfixVersion(desiredVersion) + } + label := fmt.Sprintf("Do you want to download version %d.%d.%d%s%s instead", desiredVersion.Major(), desiredVersion.Minor(), desiredVersion.Patch(), + prerelease, + hotfix, ) if _, err := u.confirmF(label); err != nil { return clog.Fatal("user cancelled operation", clog.Tipf(`use "--force" to update without confirmation`)) @@ -310,6 +320,7 @@ func queryGithubAssetURL(httpClient getter, version *semver.Version, ostype stri fmt.Fprint(&b, "-") fmt.Fprint(&b, version.Prerelease()) } + fmt.Fprintf(&b, "%s", hotfixVersion(version)) // this will be empty if no hotfix urlString := fmt.Sprintf("https://api.github.com/repos/cdr/coder-cli/releases/tags/v%s", b.String()) clog.LogInfo("query github releases", fmt.Sprintf("url: %q", urlString)) @@ -499,6 +510,17 @@ func defaultExec(ctx context.Context, cmd string, args ...string) ([]byte, error // hotfixExpr matches the build metadata used for identifying CLI hotfixes. var hotfixExpr = regexp.MustCompile(`(?i)^.*?cli\.(\d+).*?$`) +// hotfixVersion returns the hotfix build metadata tag if it is present in v +// and an empty string otherwise. +func hotfixVersion(v *semver.Version) string { + match := hotfixExpr.FindStringSubmatch(v.Metadata()) + if len(match) < 2 { + return "" + } + + return fmt.Sprintf("+cli.%s", match[1]) +} + // compareVersions performs a NON-SEMVER-COMPLIANT comparison of two versions. // If the two versions differ as per SemVer, then that result is returned. // Otherwise, the build metadata of the two versions are compared based on diff --git a/internal/cmd/update_test.go b/internal/cmd/update_test.go index 4de384b0..00855a72 100644 --- a/internal/cmd/update_test.go +++ b/internal/cmd/update_test.go @@ -36,6 +36,7 @@ const ( filenameLinux = "coder-cli-linux-amd64.tar.gz" filenameWindows = "coder-cli-windows.zip" fakeGithubReleaseURL = "https://api.github.com/repos/cdr/coder-cli/releases/tags/v1.23.4-rc.5" + fakeGithubHotfixURL = fakeGithubReleaseURL + "+cli.2" ) var ( @@ -50,7 +51,8 @@ var ( fakeHotfixVersionZip = mustValidZip("coder.exe", []byte(fakeHotfixVersion)) fakeAssetURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameLinux fakeAssetURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/" + filenameWindows - fakeGithubReleaseJSON = fmt.Sprintf(`{"assets":[{"name":%q,"browser_download_url":%q},{"name":%q,"browser_download_url":%q}]}`, filenameLinux, fakeAssetURLLinux, filenameWindows, fakeAssetURLWindows) + fakeHotfixURLLinux = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5+cli.2/" + filenameLinux + fakeHotfixURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5+cli.2/" + filenameWindows ) func Test_updater_run(t *testing.T) { @@ -141,7 +143,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - explicit version specified", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeOldVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -158,7 +160,7 @@ func Test_updater_run(t *testing.T) { fakeOldVersion := "v" + fakeOldVersion fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeOldVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -173,7 +175,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - old to new", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -190,7 +192,7 @@ func Test_updater_run(t *testing.T) { fakeOldVersion := "v" + fakeOldVersion fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -205,8 +207,8 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - new to hotfix", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeNewVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeHotfixVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeHotfixVersionTgz, 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubHotfixURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeHotfixURLLinux), 200, variadicS(), nil) + p.HTTPClient.M[fakeHotfixURLLinux] = newFakeGetterResponse(fakeHotfixVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeNewVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -222,8 +224,8 @@ func Test_updater_run(t *testing.T) { p.ExecutablePath = fakeExePathWindows fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeNewVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeHotfixVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeHotfixVersionZip, 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubHotfixURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameWindows, fakeHotfixURLWindows), 200, variadicS(), nil) + p.HTTPClient.M[fakeHotfixURLWindows] = newFakeGetterResponse(fakeHotfixVersionZip, 200, variadicS(), nil) p.VersionF = func() string { return fakeNewVersion } p.ConfirmF = fakeConfirmYes p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -238,7 +240,7 @@ func Test_updater_run(t *testing.T) { p.ExecutablePath = "/home/user/bin/coder-cli" fakeFile(t, p.Fakefs, p.ExecutablePath, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -255,7 +257,7 @@ func Test_updater_run(t *testing.T) { p.ExecutablePath = fakeExePathWindows fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameWindows, fakeAssetURLWindows), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse(fakeNewVersionZip, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -270,7 +272,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - old to new forced", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.Execer.M[p.ExecutablePath+".new --version"] = fakeExecerResult{[]byte(fakeNewVersion), nil} @@ -355,7 +357,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - failed to fetch URL", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 0, variadicS(), fakeError) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -369,7 +371,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - release URL 404", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 404, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -383,7 +385,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - invalid tgz archive", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -399,7 +401,7 @@ func Test_updater_run(t *testing.T) { p.ExecutablePath = fakeExePathWindows fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameWindows, fakeAssetURLWindows), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLWindows] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -415,7 +417,7 @@ func Test_updater_run(t *testing.T) { p.Fakefs = afero.NewReadOnlyFs(rwfs) fakeFile(t, rwfs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -429,7 +431,7 @@ func Test_updater_run(t *testing.T) { run(t, "update coder - cannot exec new binary", func(t *testing.T, p *params) { fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion) p.HTTPClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJSON), 200, variadicS(), nil) - p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse([]byte(fakeGithubReleaseJSON), 200, variadicS(), nil) + p.HTTPClient.M[fakeGithubReleaseURL] = newFakeGetterResponse(fakeGithubReleaseJSON(filenameLinux, fakeAssetURLLinux), 200, variadicS(), nil) p.HTTPClient.M[fakeAssetURLLinux] = newFakeGetterResponse(fakeNewVersionTgz, 200, variadicS(), nil) p.VersionF = func() string { return fakeOldVersion } p.ConfirmF = fakeConfirmYes @@ -740,3 +742,16 @@ type fakeExecerResult struct { Output []byte Err error } + +func fakeGithubReleaseJSON(filename, assetURL string) []byte { + jsonStr := fmt.Sprintf(` + {"assets": + [ + { + "name": %q, + "browser_download_url": %q + } + ] + }`, filename, assetURL) + return []byte(jsonStr) +} From a9ebe5c24ec186d51d6a2002334c5d6dbbf290cf Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 13:41:15 +0000 Subject: [PATCH 6/7] remove duplicated version output --- internal/cmd/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/update.go b/internal/cmd/update.go index 4ab75996..838a29a7 100644 --- a/internal/cmd/update.go +++ b/internal/cmd/update.go @@ -230,7 +230,7 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi return clog.Fatal("failed to update coder binary", clog.Causef(err.Error())) } - clog.LogSuccess("Updated coder CLI to version " + desiredVersion.String()) + clog.LogSuccess("Updated coder CLI") return nil } From 3279b10b1161bb3138a1c25a4139107318582159 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 8 Oct 2021 13:44:34 +0000 Subject: [PATCH 7/7] fix confirm msg --- internal/cmd/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/update.go b/internal/cmd/update.go index 838a29a7..124662eb 100644 --- a/internal/cmd/update.go +++ b/internal/cmd/update.go @@ -153,7 +153,7 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi } hotfix := "" if hotfixVersion(desiredVersion) != "" { - hotfix = "+" + hotfixVersion(desiredVersion) + hotfix = hotfixVersion(desiredVersion) } label := fmt.Sprintf("Do you want to download version %d.%d.%d%s%s instead", desiredVersion.Major(),