Skip to content

Commit

Permalink
bugfix(cli): change process of finding specified binary releases
Browse files Browse the repository at this point in the history
  • Loading branch information
neogopher committed Dec 3, 2024
1 parent 93bc3fd commit 5eb2db9
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 1 deletion.
105 changes: 105 additions & 0 deletions pkg/upgrade/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package upgrade

import (
"context"
"fmt"
"net/http"
"os"
"regexp"
"runtime"
"strings"

"github.com/blang/semver"
"github.com/google/go-github/v30/github"
gitconfig "github.com/tcnksm/go-gitconfig"
"golang.org/x/oauth2"
)

func fetchReleaseByTag(owner, repo, tag string) (*github.RepositoryRelease, error) {
var (
ctx = context.Background()
token string
hc *http.Client

release *github.RepositoryRelease
)

if os.Getenv("GITHUB_TOKEN") != "" {
token = os.Getenv("GITHUB_TOKEN")
}
if token == "" {
token, _ = gitconfig.GithubToken()
}

if token == "" {
hc = http.DefaultClient
} else {
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
hc = oauth2.NewClient(ctx, src)
}

client := github.NewClient(hc)

// Fetch the release by tag
release, _, err := client.Repositories.GetReleaseByTag(ctx, owner, repo, tag)
if err != nil {
return release, fmt.Errorf("error fetching release by tag: %w", err)
}

return release, nil
}

func findAssetFromRelease(rel *github.RepositoryRelease) (*github.ReleaseAsset, semver.Version, bool, error) {
// Generate candidates
suffixes := make([]string, 0, 2*7*2)
for _, sep := range []rune{'_', '-'} {
for _, ext := range []string{".zip", ".tar.gz", ".tgz", ".gzip", ".gz", ".tar.xz", ".xz", ""} {
suffix := fmt.Sprintf("%s%c%s%s", runtime.GOOS, sep, runtime.GOARCH, ext)
suffixes = append(suffixes, suffix)
if runtime.GOOS == "windows" {
suffix = fmt.Sprintf("%s%c%s.exe%s", runtime.GOOS, sep, runtime.GOARCH, ext)
suffixes = append(suffixes, suffix)
}
}
}

verText := rel.GetTagName()
indices := reVersion.FindStringIndex(verText)
if indices == nil {
return nil, semver.Version{}, false, fmt.Errorf("skip version not adopting semver", verText)

Check failure on line 69 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / lint

printf: fmt.Errorf call has arguments but no formatting directives (govet)

Check failure on line 69 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / Execute all go tests

fmt.Errorf call has arguments but no formatting directives
}
if indices[0] > 0 {
// Strip prefix of version
verText = verText[indices[0]:]
}

// If semver cannot parse the version text, it means that the text is not adopting
// the semantic versioning. So it should be skipped.
ver, err := semver.Make(verText)
if err != nil {
return nil, semver.Version{}, false, fmt.Errorf("failed to parse a semantic version", verText)

Check failure on line 80 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / lint

printf: fmt.Errorf call has arguments but no formatting directives (govet)

Check failure on line 80 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / Execute all go tests

fmt.Errorf call has arguments but no formatting directives
}

filterRe, err := regexp.Compile("vcluster")
if err != nil {
return nil, semver.Version{}, false, fmt.Errorf("failed to compile regexp")
}

for _, asset := range rel.Assets {
name := asset.GetName()

// Skipping asset not matching filter
if !filterRe.MatchString(name) {
continue
}

for _, s := range suffixes {
if strings.HasSuffix(name, s) { // require version, arch etc
// default: assume single artifact
return asset, ver, true, nil
}
}
}

return nil, semver.Version{}, false, fmt.Errorf("no suitable asset was found in release", rel.GetTagName())

Check failure on line 104 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / lint

printf: fmt.Errorf call has arguments but no formatting directives (govet)

Check failure on line 104 in pkg/upgrade/helpers.go

View workflow job for this annotation

GitHub Actions / Execute all go tests

fmt.Errorf call has arguments but no formatting directives
}
43 changes: 42 additions & 1 deletion pkg/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"regexp"
"strings"
"sync"

"github.com/loft-sh/log"
Expand Down Expand Up @@ -123,7 +124,7 @@ func Upgrade(flagVersion string, log log.Logger) error {
return fmt.Errorf("failed to initialize updater: %w", err)
}
if flagVersion != "" {
release, found, err := updater.DetectVersion(githubSlug, flagVersion)
release, found, err := DetectVersion(githubSlug, flagVersion)
if err != nil {
return errors.Wrap(err, "find version")
} else if !found {
Expand Down Expand Up @@ -186,3 +187,43 @@ func Upgrade(flagVersion string, log log.Logger) error {

return nil
}

func DetectVersion(slug string, version string) (*selfupdate.Release, bool, error) {
var (
release *selfupdate.Release
found bool
err error
)

repo := strings.Split(slug, "/")
if len(repo) != 2 || repo[0] == "" || repo[1] == "" {
return nil, false, fmt.Errorf("invalid slug format. It should be 'owner/name': %s", slug)
}

githubRelease, err := fetchReleaseByTag(repo[0], repo[1], version)
if err != nil {
return nil, false, fmt.Errorf("repository or release not found: %w", err)
}

asset, semVer, found, err := findAssetFromRelease(githubRelease)
if !found {
return nil, false, fmt.Errorf("release asset not found: %w", err)
}

publishedAt := githubRelease.GetPublishedAt().Time
release = &selfupdate.Release{
Version: semVer,
AssetURL: asset.GetBrowserDownloadURL(),
AssetByteSize: asset.GetSize(),
AssetID: asset.GetID(),
ValidationAssetID: -1,
URL: githubRelease.GetHTMLURL(),
ReleaseNotes: githubRelease.GetBody(),
Name: githubRelease.GetName(),
PublishedAt: &publishedAt,
RepoOwner: repo[0],
RepoName: repo[1],
}

return release, true, nil
}

0 comments on commit 5eb2db9

Please sign in to comment.