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

Commit 64a406e

Browse files
authored
feat(cli): compare hotfix version metadata (#450)
* refactor: stop hard-coding tgz and zip in unit tests * feat(cli): compare cli.N build metadata to support hotfixes, add hotfix versions to asset urls * chore: update unit tests
1 parent 6640696 commit 64a406e

File tree

2 files changed

+265
-38
lines changed

2 files changed

+265
-38
lines changed

internal/cmd/update.go

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import (
1717
"os/exec"
1818
"path"
1919
"path/filepath"
20+
"regexp"
2021
"runtime"
22+
"strconv"
2123
"strings"
2224
"time"
2325

@@ -139,16 +141,26 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi
139141
currentVersion, err := semver.NewVersion(u.versionF())
140142
if err != nil {
141143
clog.LogWarn("failed to determine current version of coder-cli", clog.Causef(err.Error()))
142-
} else if currentVersion.Compare(desiredVersion) == 0 {
144+
} else if compareVersions(currentVersion, desiredVersion) == 0 {
143145
clog.LogInfo("Up to date!")
144146
return nil
145147
}
146148

147149
if !force {
148-
label := fmt.Sprintf("Do you want to download version %d.%d.%d instead",
150+
prerelease := ""
151+
if desiredVersion.Prerelease() != "" {
152+
prerelease = "-" + desiredVersion.Prerelease()
153+
}
154+
hotfix := ""
155+
if hotfixVersion(desiredVersion) != "" {
156+
hotfix = hotfixVersion(desiredVersion)
157+
}
158+
label := fmt.Sprintf("Do you want to download version %d.%d.%d%s%s instead",
149159
desiredVersion.Major(),
150160
desiredVersion.Minor(),
151161
desiredVersion.Patch(),
162+
prerelease,
163+
hotfix,
152164
)
153165
if _, err := u.confirmF(label); err != nil {
154166
return clog.Fatal("user cancelled operation", clog.Tipf(`use "--force" to update without confirmation`))
@@ -218,7 +230,7 @@ func (u *updater) Run(ctx context.Context, force bool, coderURLArg string, versi
218230
return clog.Fatal("failed to update coder binary", clog.Causef(err.Error()))
219231
}
220232

221-
clog.LogSuccess("Updated coder CLI to version " + desiredVersion.String())
233+
clog.LogSuccess("Updated coder CLI")
222234
return nil
223235
}
224236

@@ -308,6 +320,7 @@ func queryGithubAssetURL(httpClient getter, version *semver.Version, ostype stri
308320
fmt.Fprint(&b, "-")
309321
fmt.Fprint(&b, version.Prerelease())
310322
}
323+
fmt.Fprintf(&b, "%s", hotfixVersion(version)) // this will be empty if no hotfix
311324

312325
urlString := fmt.Sprintf("https://api.github.com/repos/cdr/coder-cli/releases/tags/v%s", b.String())
313326
clog.LogInfo("query github releases", fmt.Sprintf("url: %q", urlString))
@@ -493,3 +506,76 @@ func HasFilePathPrefix(s, prefix string) bool {
493506
func defaultExec(ctx context.Context, cmd string, args ...string) ([]byte, error) {
494507
return exec.CommandContext(ctx, cmd, args...).CombinedOutput()
495508
}
509+
510+
// hotfixExpr matches the build metadata used for identifying CLI hotfixes.
511+
var hotfixExpr = regexp.MustCompile(`(?i)^.*?cli\.(\d+).*?$`)
512+
513+
// hotfixVersion returns the hotfix build metadata tag if it is present in v
514+
// and an empty string otherwise.
515+
func hotfixVersion(v *semver.Version) string {
516+
match := hotfixExpr.FindStringSubmatch(v.Metadata())
517+
if len(match) < 2 {
518+
return ""
519+
}
520+
521+
return fmt.Sprintf("+cli.%s", match[1])
522+
}
523+
524+
// compareVersions performs a NON-SEMVER-COMPLIANT comparison of two versions.
525+
// If the two versions differ as per SemVer, then that result is returned.
526+
// Otherwise, the build metadata of the two versions are compared based on
527+
// the `cli.N` hotfix metadata.
528+
//
529+
// Examples:
530+
// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.0"))
531+
// 0
532+
// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.1"))
533+
// 1
534+
// compareVersions(semver.MustParse("v1.0.1"), semver.MustParse("v1.0.0"))
535+
// -1
536+
// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0"))
537+
// 1
538+
// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0+cli.0"))
539+
// 0
540+
// compareVersions(semver.MustParse("v1.0.0"), semver.MustParse("v1.0.0+cli.0"))
541+
// -1
542+
// compareVersions(semver.MustParse("v1.0.0+cli.1"), semver.MustParse("v1.0.0+cli.0"))
543+
// 1
544+
// compareVersions(semver.MustParse("v1.0.0+cli.0"), semver.MustParse("v1.0.0+cli.1"))
545+
// -1
546+
//
547+
func compareVersions(a, b *semver.Version) int {
548+
semverComparison := a.Compare(b)
549+
if semverComparison != 0 {
550+
return semverComparison
551+
}
552+
553+
matchA := hotfixExpr.FindStringSubmatch(a.Metadata())
554+
matchB := hotfixExpr.FindStringSubmatch(b.Metadata())
555+
556+
hotfixA := -1
557+
hotfixB := -1
558+
559+
// extract hotfix versions from the metadata of a and b
560+
if len(matchA) > 1 {
561+
if n, err := strconv.Atoi(matchA[1]); err == nil {
562+
hotfixA = n
563+
}
564+
}
565+
if len(matchB) > 1 {
566+
if n, err := strconv.Atoi(matchB[1]); err == nil {
567+
hotfixB = n
568+
}
569+
}
570+
571+
// compare hotfix versions
572+
if hotfixA < hotfixB {
573+
return -1
574+
}
575+
if hotfixA > hotfixB {
576+
return 1
577+
}
578+
// both versions are the same if their semver and hotfix
579+
// metadata are the same.
580+
return 0
581+
}

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