Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reimplement functions in go-mouff-update, use ghreposervice #5470

Merged
merged 7 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion flytectl/cmd/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func upgrade(u *updater.Updater) (string, error) {
}

func isUpgradeSupported(goos platformutil.Platform) (bool, error) {
latest, err := github.FlytectlReleaseConfig.GetLatestVersion()
latest, err := github.FlytectlReleaseConfig.Provider.(*github.GHProvider).GetCleanLatestVersion()
if err != nil {
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion flytectl/cmd/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func GetVersionCommand(rootCmd *cobra.Command) map[string]cmdCore.CommandEntry {

func getVersion(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error {
goos := platformutil.Platform(runtime.GOOS)
version, err := github.FlytectlReleaseConfig.GetLatestVersion()
version, err := github.FlytectlReleaseConfig.Provider.(*github.GHProvider).GetCleanLatestVersion()
if err != nil {
logger.Error(ctx, "Unable to get the latest version because %v", err)
} else {
Expand Down
4 changes: 2 additions & 2 deletions flytectl/pkg/github/githubutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -40,9 +39,10 @@ var Client GHRepoService

// FlytectlReleaseConfig represent the updater config for flytectl binary
var FlytectlReleaseConfig = &updater.Updater{
Provider: &provider.Github{
Provider: &GHProvider{
RepositoryURL: flytectlRepository,
ArchiveName: getFlytectlAssetName(),
ghRepo: GetGHRepoService(),
},
ExecutableName: flytectl,
Version: stdlibversion.Version,
Expand Down
175 changes: 175 additions & 0 deletions flytectl/pkg/github/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
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"
)

// Github provider finds a archive file in the repository's releases to provide files
type GHProvider 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)
ghRepo GHRepoService // github repository service
}

// 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 *GHProvider) 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 *GHProvider) 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 *GHProvider) Open() (err error) {
archiveURL, err := c.getArchiveURL("") // get archive url for latest version
if err != nil {
return err
}
req, err := http.NewRequest("GET", archiveURL, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

c.tmpDir, err = os.MkdirTemp("", "rocket-update")
if err != nil {
return err
}

c.archivePath = filepath.Join(c.tmpDir, c.ArchiveName)
archiveFile, err := os.Create(c.archivePath)
if err != nil {
return err
}
_, err = io.Copy(archiveFile, resp.Body)
archiveFile.Close()
if err != nil {
return err
}
c.decompressProvider, err = provider.Decompress(c.archivePath)
if err != nil {
return err
}
return c.decompressProvider.Open()
}

// Close closes the provider
func (c *GHProvider) 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 *GHProvider) GetLatestVersion() (string, error) {
tags, err := c.getReleases()
if err != nil {
return "", err
}
latestTag := tags[0].GetTagName()
return latestTag, err
}

// GetCleanLatestVersion gets the latest version without the "flytectl/" prefix
func (c *GHProvider) GetCleanLatestVersion() (string, error) {
latest, err := c.GetLatestVersion()
if err != nil {
return "", err
}
clearVersion := strings.TrimPrefix(latest, fmt.Sprintf("%s/", flytectl))
return clearVersion, nil
}

func (c *GHProvider) getReleases() ([]*go_github.RepositoryRelease, error) {
g := c.ghRepo
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 *GHProvider) 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 *GHProvider) Retrieve(src string, dest string) error {
return c.decompressProvider.Retrieve(src, dest)
}
69 changes: 69 additions & 0 deletions flytectl/pkg/github/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package github

import (
"testing"

"github.com/flyteorg/flyte/flytectl/pkg/github/mocks"
go_github "github.com/google/go-github/v42/github"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestGetLatestFlytectlVersion(t *testing.T) {
t.Run("Get latest release", func(t *testing.T) {
mockGh := &mocks.GHRepoService{}
// return a list of github releases
mockGh.OnListReleasesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(
[]*go_github.RepositoryRelease{
{TagName: go_github.String("flytectl/1.2.4")},
{TagName: go_github.String("flytectl/1.2.3")},
{TagName: go_github.String("other-1.0.0")},
},
nil,
nil,
)
mockProvider := &GHProvider{
RepositoryURL: flytectlRepository,
ArchiveName: getFlytectlAssetName(),
ghRepo: mockGh,
}

latestVersion, err := mockProvider.GetLatestVersion()
assert.Nil(t, err)
assert.Equal(t, "flytectl/1.2.4", latestVersion)
cleanVersion, err := mockProvider.GetCleanLatestVersion()
assert.Nil(t, err)
assert.Equal(t, "1.2.4", cleanVersion)
})
}

func TestGetFlytectlReleases(t *testing.T) {
t.Run("Get releases", func(t *testing.T) {
mockGh := &mocks.GHRepoService{}
allReleases := []*go_github.RepositoryRelease{
{TagName: go_github.String("flytectl/1.2.4")},
{TagName: go_github.String("flytectl/1.2.3")},
{TagName: go_github.String("other-1.0.0")},
}
releases := []*go_github.RepositoryRelease{
{TagName: go_github.String("flytectl/1.2.4")},
{TagName: go_github.String("flytectl/1.2.3")},
}
// return a list of github releases
mockGh.OnListReleasesMatch(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(
allReleases,
nil,
nil,
)
mockProvider := &GHProvider{
RepositoryURL: flytectlRepository,
ArchiveName: getFlytectlAssetName(),
ghRepo: mockGh,
}

flytectlReleases, err := mockProvider.getReleases()
assert.Nil(t, err)
assert.Equal(t, releases, flytectlReleases)
})
}
Loading