Skip to content

Commit

Permalink
feat(service): add /tags fallback to github services
Browse files Browse the repository at this point in the history
some repositories use tags and not releases.
switch a service to the tags api if /releases gives []

closes #275
  • Loading branch information
JosephKav committed Jul 4, 2023
1 parent 31d5d3c commit 1e527f8
Show file tree
Hide file tree
Showing 13 changed files with 541 additions and 44 deletions.
1 change: 1 addition & 0 deletions service/latest_version/api_type/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Release struct {
URL string `json:"url,omitempty"`
AssetsURL string `json:"assets_url,omitempty"`
SemanticVersion *semver.Version `json:"-"`
Name string `json:"name,omitempty"` // This is the tag name on /tags queries
TagName string `json:"tag_name,omitempty"`
PreRelease bool `json:"prerelease,omitempty"`
Assets []Asset `json:"assets,omitempty"`
Expand Down
12 changes: 9 additions & 3 deletions service/latest_version/gets.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,17 @@ func (l *Lookup) GetUsePreRelease() bool {
}

// GetURL will ensure `url` is a valid GitHub API URL if `urlType` is 'github'
func GetURL(url string, urlType string) string {
if urlType == "github" {
func (l *Lookup) GetURL() string {
url := l.URL
if l.Type == "github" {
// Convert "owner/repo" to the API path.
if strings.Count(url, "/") == 1 {
url = fmt.Sprintf("https://api.github.com/repos/%s/releases", url)
apiTarget := "releases"
if l.GitHubData.TagFallback() {
apiTarget = "tags"
}
url = fmt.Sprintf("https://api.github.com/repos/%s/%s",
url, apiTarget)
}
}
return url
Expand Down
47 changes: 47 additions & 0 deletions service/latest_version/gets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,50 @@ func TestLookup_GetUsePreRelease(t *testing.T) {
})
}
}

func TestLookup_GetURL(t *testing.T) {
// GIVEN a Lookup
tests := map[string]struct {
urlType bool
url string
tagFallback bool
want string
}{
"type=url": {
urlType: true,
url: "https://release-argus.io",
want: "https://release-argus.io",
},
"type=github": {
url: "release-argus/Argus",
want: "https://api.github.com/repos/release-argus/Argus/releases",
},
"type=github, tagFallback": {
url: "release-argus/Argus",
tagFallback: true,
want: "https://api.github.com/repos/release-argus/Argus/tags",
},
}

for name, tc := range tests {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()

lookup := testLookup(tc.urlType, false)
lookup.URL = tc.url
if !tc.urlType {
lookup.GitHubData.tagFallback = tc.tagFallback
}

// WHEN GetURL is called
got := lookup.GetURL()

// THEN the function returns the correct result
if got != tc.want {
t.Errorf("\nwant: %q\ngot: %q",
tc.want, got)
}
})
}
}
6 changes: 5 additions & 1 deletion service/latest_version/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ func (l *Lookup) filterGitHubReleases(
var err error

// Check that TagName matches URLCommands
if tagName, err = l.URLCommands.Run(releases[i].TagName, *logFrom); err != nil {
tag := releases[i].TagName
if tag == "" {
tag = releases[i].Name
}
if tagName, err = l.URLCommands.Run(tag, *logFrom); err != nil {
continue
}

Expand Down
7 changes: 7 additions & 0 deletions service/latest_version/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ func TestLookup_FilterGitHubReleases(t *testing.T) {
usePreReleases bool
want []string
}{
"use Name if no TagName (/tags vs /releases API)": {
releases: []github_types.Release{
{Name: "0.99.0"},
{Name: "0.3.0"},
{Name: "0.0.1"},
}, want: []string{"0.99.0", "0.3.0", "0.0.1"},
},
"keep pre-releases": {
usePreReleases: true,
releases: []github_types.Release{
Expand Down
8 changes: 7 additions & 1 deletion service/latest_version/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@ func boolPtr(val bool) *bool {
func stringPtr(val string) *string {
return &val
}

// Unsure why Go tests give a different result than the compiled binary
var initialEmptyListETag string

func TestMain(m *testing.M) {
// initialize jLog
jLog = util.NewJLog("DEBUG", false)
jLog.Testing = true
LogInit(jLog)
FindEmptyListETag(os.Getenv("GITHUB_TOKEN"))
initialEmptyListETag = getEmptyListETag()

// run other tests
exitCode := m.Run()
Expand Down Expand Up @@ -76,7 +82,7 @@ func testLookup(urlType bool, allowInvalidCerts bool) *Lookup {
lookup.URLCommands = filter.URLCommandSlice{
{Type: "regex", Regex: stringPtr("v([0-9.]+)")}}
} else {
lookup.GitHubData = &GitHubData{}
lookup.GitHubData = NewGitHubData("", nil)
lookup.URLCommands = filter.URLCommandSlice{
{Type: "regex", Regex: stringPtr("([0-9.]+)")}}
lookup.AccessToken = stringPtr(os.Getenv("GITHUB_TOKEN"))
Expand Down
2 changes: 1 addition & 1 deletion service/latest_version/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (l *Lookup) Init(
options *opt.Options,
) {
if l.Type == "github" {
l.GitHubData = &GitHubData{}
l.GitHubData = NewGitHubData("", nil)
}

l.Defaults = defaults
Expand Down
35 changes: 31 additions & 4 deletions service/latest_version/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package latestver

import (
"bytes"
"crypto/tls"
"fmt"
"io"
Expand Down Expand Up @@ -170,7 +171,7 @@ func (l *Lookup) httpRequest(logFrom *util.LogFrom) (rawBody []byte, err error)
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}

req, err := http.NewRequest(http.MethodGet, GetURL(l.URL, l.Type), nil)
req, err := http.NewRequest(http.MethodGet, l.GetURL(), nil)
if err != nil {
err = fmt.Errorf("failed creating http request for %q: %w",
l.URL, err)
Expand Down Expand Up @@ -211,10 +212,36 @@ func (l *Lookup) httpRequest(logFrom *util.LogFrom) (rawBody []byte, err error)
rawBody, err = io.ReadAll(resp.Body)
jLog.Error(err, *logFrom, err != nil)
if l.Type == "github" && err == nil {
newETag := strings.TrimPrefix(resp.Header.Get("etag"), "W/")
if l.GitHubData.ETag() != newETag {
jLog.Verbose("Potentially found new releases (ETag changed)", *logFrom, true)
// 200 - Resource has changed
if resp.StatusCode == http.StatusOK {
newETag := strings.TrimPrefix(resp.Header.Get("etag"), "W/")
l.GitHubData.SetETag(newETag)
// []byte{91, 93} == []byte("[]") == empty JSON array
if len(rawBody) == 2 && bytes.Equal(rawBody, []byte{91, 93}) {
// Update the default empty list ETag
setEmptyListETag(newETag)
// Flip the fallback flag
l.GitHubData.SetTagFallback()
if l.GitHubData.TagFallback() {
jLog.Verbose(fmt.Sprintf("/releases gave %v, trying /tags", string(rawBody)), *logFrom, true)
rawBody, err = l.httpRequest(logFrom)
}
// Has tags/releases
} else {
jLog.Verbose("Potentially found new releases (new ETag)", *logFrom, true)
}

// 304 - Resource has not changed
} else if resp.StatusCode == http.StatusNotModified {
// Didn't find any releases before and nothing's changed
if !l.GitHubData.hasReleases() {
// Flip the fallback flag
l.GitHubData.SetTagFallback()
if l.GitHubData.TagFallback() {
jLog.Verbose("no tags found on /releases, trying /tags", *logFrom, true)
rawBody, err = l.httpRequest(logFrom)
}
}
}
}
return
Expand Down
Loading

0 comments on commit 1e527f8

Please sign in to comment.