From b1c8505c57f147aaa3ae7525fc8c28bae2aa3c7c Mon Sep 17 00:00:00 2001 From: Jorge Carpio Date: Tue, 19 Sep 2023 11:00:49 +0200 Subject: [PATCH] Implement ListReleases --- selfupdate/detect.go | 50 ++++++++++++----------------------------- selfupdate/list.go | 49 ++++++++++++++++++++++++++++++++++++++++ selfupdate/list_test.go | 20 +++++++++++++++++ selfupdate/release.go | 21 +++++++++++++++++ selfupdate/repoinfo.go | 22 ++++++++++++++++++ selfupdate/util.go | 21 +++++++++++++++++ 6 files changed, 147 insertions(+), 36 deletions(-) create mode 100644 selfupdate/list.go create mode 100644 selfupdate/list_test.go create mode 100644 selfupdate/repoinfo.go create mode 100644 selfupdate/util.go diff --git a/selfupdate/detect.go b/selfupdate/detect.go index 6ca1af7..c1b469a 100644 --- a/selfupdate/detect.go +++ b/selfupdate/detect.go @@ -91,21 +91,12 @@ func findReleaseAndAsset(rels []*github.RepositoryRelease, targetVersion string, filters []*regexp.Regexp) (*github.RepositoryRelease, *github.ReleaseAsset, semver.Version, bool) { // 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) - } - } - } - - var ver semver.Version - var asset *github.ReleaseAsset - var release *github.RepositoryRelease + var ( + suffixes = assetSuffixes() + ver semver.Version + asset *github.ReleaseAsset + release *github.RepositoryRelease + ) // Find the latest version from the list of releases. // Returned list from GitHub API is in the order of the date when created. @@ -143,12 +134,12 @@ func (up *Updater) DetectLatest(slug string) (release *Release, found bool, err // DetectVersion tries to get the given version of the repository on Github. `slug` means `owner/name` formatted string. // And version indicates the required version. func (up *Updater) DetectVersion(slug string, version string) (release *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) + repo, err := parseRepo(slug) + if err != nil { + return nil, false, fmt.Errorf("parse slug: %v", err) } - rels, res, err := up.api.Repositories.ListReleases(up.apiCtx, repo[0], repo[1], nil) + rels, res, err := up.api.Repositories.ListReleases(up.apiCtx, repo.owner, repo.name, nil) if err != nil { log.Println("API returned an error response:", err) if res != nil && res.StatusCode == 404 { @@ -164,23 +155,10 @@ func (up *Updater) DetectVersion(slug string, version string) (release *Release, return nil, false, nil } - url := asset.GetBrowserDownloadURL() - log.Println("Successfully fetched the latest release. tag:", rel.GetTagName(), ", name:", rel.GetName(), ", URL:", rel.GetURL(), ", Asset:", url) - - publishedAt := rel.GetPublishedAt().Time - release = &Release{ - ver, - url, - asset.GetSize(), - asset.GetID(), - -1, - rel.GetHTMLURL(), - rel.GetBody(), - rel.GetName(), - &publishedAt, - repo[0], - repo[1], - } + log.Println("Successfully fetched the latest release. tag:", rel.GetTagName(), + ", name:", rel.GetName(), ", URL:", rel.GetURL(), ", Asset:", asset.GetBrowserDownloadURL()) + + release = newRelease(repo, rel, asset, &ver) if up.validator != nil { validationName := asset.GetName() + up.validator.Suffix() diff --git a/selfupdate/list.go b/selfupdate/list.go new file mode 100644 index 0000000..7e1cf9b --- /dev/null +++ b/selfupdate/list.go @@ -0,0 +1,49 @@ +package selfupdate + +import ( + "fmt" + "regexp" + "runtime" + + "github.com/google/go-github/v30/github" +) + +func findReleases(repo *repoInfo, rels []*github.RepositoryRelease, filters []*regexp.Regexp) ([]*Release, error) { + var ( + suffixes = assetSuffixes() + found = make([]*Release, 0) + ) + for _, rel := range rels { + asset, ver, ok := findAssetFromRelease(rel, suffixes, "", filters) + if ok { + found = append(found, newRelease(repo, rel, asset, &ver)) + } + } + if len(found) == 0 { + return nil, fmt.Errorf("could not find any release for %s and %s", runtime.GOOS, runtime.GOARCH) + } + return found, nil +} + +func (up *Updater) ListReleases(slug string) ([]*Release, bool, error) { + repo, err := parseRepo(slug) + if err != nil { + return nil, false, fmt.Errorf("parse slug: %v", err) + } + + rels, _, err := up.api.Repositories.ListReleases(up.apiCtx, repo.owner, repo.name, nil) + if err != nil { + return nil, false, fmt.Errorf("list GitHub releases (owner=%q, name=%q): %v", repo.owner, repo.name, err) + } + + releases, err := findReleases(repo, rels, up.filters) + if err != nil { + return nil, false, fmt.Errorf("find releases: %v", err) + } + + return releases, true, nil +} + +func ListReleases(slug string) ([]*Release, bool, error) { + return DefaultUpdater().ListReleases(slug) +} diff --git a/selfupdate/list_test.go b/selfupdate/list_test.go new file mode 100644 index 0000000..ac967e8 --- /dev/null +++ b/selfupdate/list_test.go @@ -0,0 +1,20 @@ +package selfupdate + +import "testing" + +func TestListReleases(t *testing.T) { + r, ok, err := ListReleases("rhysd/github-clone-all") + if err != nil { + t.Fatal("List releases:", err) + } + if !ok { + t.Fatalf("Failed to list releases") + } + if r == nil { + t.Fatal("Release detected but nil returned for it") + } + if len(r) != 7 { + t.Fatalf("The of releases does not match: got %d, expected %d", len(r), 7) + } + t.Logf("Found releases: %#v", r) +} diff --git a/selfupdate/release.go b/selfupdate/release.go index 014ac47..a67e407 100644 --- a/selfupdate/release.go +++ b/selfupdate/release.go @@ -4,6 +4,7 @@ import ( "time" "github.com/blang/semver" + "github.com/google/go-github/v30/github" ) // Release represents a release asset for current OS and arch. @@ -31,3 +32,23 @@ type Release struct { // RepoName is the name of the repository of the release RepoName string } + +func newRelease(repo *repoInfo, release *github.RepositoryRelease, + asset *github.ReleaseAsset, version *semver.Version) *Release { + + publishedAt := release.GetPublishedAt().Time + + return &Release{ + Version: *version, + AssetURL: asset.GetBrowserDownloadURL(), + AssetByteSize: asset.GetSize(), + AssetID: asset.GetID(), + ValidationAssetID: -1, + URL: release.GetHTMLURL(), + ReleaseNotes: release.GetBody(), + Name: release.GetName(), + PublishedAt: &publishedAt, + RepoOwner: repo.owner, + RepoName: repo.name, + } +} diff --git a/selfupdate/repoinfo.go b/selfupdate/repoinfo.go new file mode 100644 index 0000000..11b4825 --- /dev/null +++ b/selfupdate/repoinfo.go @@ -0,0 +1,22 @@ +package selfupdate + +import ( + "fmt" + "strings" +) + +type repoInfo struct { + owner string + name string +} + +func parseRepo(slug string) (*repoInfo, error) { + parts := strings.Split(slug, "/") + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return nil, fmt.Errorf("invalid slug %q: it should be in the format %q", slug, "owner/name") + } + return &repoInfo{ + owner: parts[0], + name: parts[1], + }, nil +} diff --git a/selfupdate/util.go b/selfupdate/util.go new file mode 100644 index 0000000..72f85a6 --- /dev/null +++ b/selfupdate/util.go @@ -0,0 +1,21 @@ +package selfupdate + +import ( + "fmt" + "runtime" +) + +func assetSuffixes() []string { + 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) + } + } + } + return suffixes +}