From 95e854a63939335a241e3a175b94fa43aa9ff569 Mon Sep 17 00:00:00 2001 From: novahow Date: Mon, 10 Jun 2024 22:54:36 +0800 Subject: [PATCH] reimplement functions in go-mouff-update, use ghreposervice Signed-off-by: novahow --- flytectl/cmd/upgrade/upgrade.go | 7 +- flytectl/pkg/github/githubutil.go | 3 +- flytectl/pkg/github/provider.go | 170 ++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 flytectl/pkg/github/provider.go diff --git a/flytectl/cmd/upgrade/upgrade.go b/flytectl/cmd/upgrade/upgrade.go index 5d91848720..deb88db5b8 100644 --- a/flytectl/cmd/upgrade/upgrade.go +++ b/flytectl/cmd/upgrade/upgrade.go @@ -114,19 +114,20 @@ func upgrade(u *updater.Updater) (string, error) { } func isUpgradeSupported(goos platformutil.Platform) (bool, error) { - latest, err := github.FlytectlReleaseConfig.GetLatestVersion() + latest, err := github.FlytectlReleaseConfig.Provider.GetLatestVersion() if err != nil { return false, err } - if isGreater, err := util.IsVersionGreaterThan(latest, stdlibversion.Version); err != nil { + compatible_version := strings.TrimPrefix(latest, fmt.Sprintf("%s/", github.FlytectlReleaseConfig.ExecutableName)) + if isGreater, err := util.IsVersionGreaterThan(compatible_version, stdlibversion.Version); err != nil { return false, err } else if !isGreater { fmt.Println("You already have the latest version of Flytectl") return false, nil } - message, err := github.GetUpgradeMessage(latest, goos) + message, err := github.GetUpgradeMessage(compatible_version, goos) if err != nil { return false, err } diff --git a/flytectl/pkg/github/githubutil.go b/flytectl/pkg/github/githubutil.go index e439df44f8..845f7f75b6 100644 --- a/flytectl/pkg/github/githubutil.go +++ b/flytectl/pkg/github/githubutil.go @@ -15,7 +15,6 @@ import ( stdlibversion "github.com/flyteorg/flyte/flytestdlib/version" "github.com/google/go-github/v42/github" - "github.com/mouuff/go-rocket-update/pkg/provider" "github.com/mouuff/go-rocket-update/pkg/updater" "golang.org/x/oauth2" "golang.org/x/text/cases" @@ -40,7 +39,7 @@ var Client GHRepoService // FlytectlReleaseConfig represent the updater config for flytectl binary var FlytectlReleaseConfig = &updater.Updater{ - Provider: &provider.Github{ + Provider: &GitHubProvider{ RepositoryURL: flytectlRepository, ArchiveName: getFlytectlAssetName(), }, diff --git a/flytectl/pkg/github/provider.go b/flytectl/pkg/github/provider.go new file mode 100644 index 0000000000..0e37cee284 --- /dev/null +++ b/flytectl/pkg/github/provider.go @@ -0,0 +1,170 @@ +package github + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + go_github "github.com/google/go-github/v42/github" + "github.com/mouuff/go-rocket-update/pkg/provider" +) + +// type GitHubProvider struct { +// provider.Github +// } + +// Github provider finds a archive file in the repository's releases to provide files +type GitHubProvider struct { + RepositoryURL string // Repository URL, example github.com/mouuff/go-rocket-update + ArchiveName string // Archive name (the zip/tar.gz you upload for a release on github), example: binaries.zip + + tmpDir string // temporary directory this is used internally + decompressProvider provider.Provider // provider used to decompress the downloaded archive + archivePath string // path to the downloaded archive (should be in tmpDir) +} + +// githubTag struct used to unmarshal response from github +// https://api.github.com/repos/ownerName/projectName/tags +type githubTag struct { + Name string `json:"name"` +} + +// githubRepositoryInfo is used to get the name of the project and the owner name +// from this fields we are able to get other links (such as the release and tags link) +type githubRepositoryInfo struct { + RepositoryOwner string + RepositoryName string +} + +// getRepositoryInfo parses the github repository URL +func (c *GitHubProvider) repositoryInfo() (*githubRepositoryInfo, error) { + re := regexp.MustCompile(`github\.com/(.*?)/(.*?)$`) + submatches := re.FindAllStringSubmatch(c.RepositoryURL, 1) + if len(submatches) < 1 { + return nil, errors.New("Invalid github URL:" + c.RepositoryURL) + } + return &githubRepositoryInfo{ + RepositoryOwner: submatches[0][1], + RepositoryName: submatches[0][2], + }, nil +} + +// getArchiveURL get the archive URL for the github repository +// If no tag is provided then the latest version is selected +func (c *GitHubProvider) getArchiveURL(tag string) (string, error) { + if len(tag) == 0 { + // Get latest version if no tag is provided + var err error + tag, err = c.GetLatestVersion() + if err != nil { + return "", err + } + } + + info, err := c.repositoryInfo() + if err != nil { + return "", err + } + return fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s", + info.RepositoryOwner, + info.RepositoryName, + tag, + c.ArchiveName, + ), nil +} + +// Open opens the provider +func (c *GitHubProvider) Open() (err error) { + archiveURL, err := c.getArchiveURL("") // get archive url for latest version + if err != nil { + return + } + resp, err := http.Get(archiveURL) + if err != nil { + return + } + defer resp.Body.Close() + + c.tmpDir, err = os.MkdirTemp("", "rocket-update") + if err != nil { + return + } + + c.archivePath = filepath.Join(c.tmpDir, c.ArchiveName) + archiveFile, err := os.Create(c.archivePath) + if err != nil { + return + } + _, err = io.Copy(archiveFile, resp.Body) + archiveFile.Close() + if err != nil { + return + } + c.decompressProvider, err = provider.Decompress(c.archivePath) + if err != nil { + return nil + } + return c.decompressProvider.Open() +} + +// Close closes the provider +func (c *GitHubProvider) Close() error { + if c.decompressProvider != nil { + c.decompressProvider.Close() + c.decompressProvider = nil + } + + if len(c.tmpDir) > 0 { + os.RemoveAll(c.tmpDir) + c.tmpDir = "" + c.archivePath = "" + } + return nil +} + +// GetLatestVersion gets the latest version +func (c *GitHubProvider) GetLatestVersion() (string, error) { + tags, err := c.getReleases() + if err != nil { + return "", err + } + latest_tag := tags[0].GetTagName() + return latest_tag, err +} + +func (c *GitHubProvider) getReleases() ([]*go_github.RepositoryRelease, error) { + g := GetGHRepoService() + releases, _, err := g.ListReleases(context.Background(), owner, flyte, &go_github.ListOptions{ + PerPage: 100, + }) + if err != nil { + return nil, err + } + var filteredReleases []*go_github.RepositoryRelease + for _, release := range releases { + if strings.HasPrefix(release.GetTagName(), flytectl) { + filteredReleases = append(filteredReleases, release) + } + } + return filteredReleases, err +} + +// Walk walks all the files provided +func (c *GitHubProvider) Walk(walkFn provider.WalkFunc) error { + if c.decompressProvider == nil { + // TODO specify error + return provider.ErrNotOpenned + } + return c.decompressProvider.Walk(walkFn) +} + +// Retrieve file relative to "provider" to destination +func (c *GitHubProvider) Retrieve(src string, dest string) error { + return c.decompressProvider.Retrieve(src, dest) +}