diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 0388c9b0..3a78a899 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -35,6 +35,7 @@ jobs: install-mode: goinstall args: --timeout 10m --verbose --issues-exit-code=0 only-new-issues: true + install-mode: goinstall code-scan: name: Code Scan @@ -67,9 +68,11 @@ jobs: permissions: security-events: write steps: - # We only need to checkout as govuln does the go setup... + # We only need to checkout as govuln does the go setup using the go.mod file. - name: Checkout code uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + with: + persist-credentials: false - id: govulncheck uses: golang/govulncheck-action@v1 diff --git a/docs/installation.md b/docs/installation.md index 42b91e81..c4c3c184 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -75,6 +75,10 @@ However, by passing the following flag,`-a, --test-all-containers` version-check `use-metadata.version-checker.io` is not required when this is set. All other options, apart from URL overrides, are ignored when this is set. +- `use-github-release.version-checker.io/my-container: "true"`: opt-in to sourcing the latest version for `ghcr.io` images from the backing GitHub repository's Releases instead of GHCR package tags. + - **Note:** Release-backed lookups do not include image SHA/digest information, so version-checker cannot detect rebuilt images at the same version. If `use-sha.version-checker.io/my-container: "true"` is also set (or the image tag is `latest` or omitted), version-checker will fall back to using GHCR package tags. + - This is useful for projects that publish version signals in GitHub Releases but do not keep GHCR tags up to date. + - `override-url.version-checker.io/my-container: docker.io/bitnami/etcd`: is used to change the URL for where to lookup where the latest image version is. In this example, the current version of `my-container` will be compared diff --git a/go.mod b/go.mod index 1b548e35..93584dd0 100644 --- a/go.mod +++ b/go.mod @@ -113,9 +113,9 @@ require ( github.com/google/go-querystring v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.6 // indirect + github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.9.2 // indirect diff --git a/go.sum b/go.sum index 1083609b..358e722d 100644 --- a/go.sum +++ b/go.sum @@ -221,17 +221,12 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A= github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -314,7 +309,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= diff --git a/known-configurations.md b/known-configurations.md index 7da1e4dd..741be1ec 100644 --- a/known-configurations.md +++ b/known-configurations.md @@ -42,3 +42,14 @@ Velero contains an image `1220` which is always the latest. | Annotation | |-| | `match-regex.version-checker.io/velero: 'v(\d+)\.(\d+)\.(\d+)'` | + +### n8n: ghcr.io/n8n-io/n8n + +n8n publishes the versions to compare against via GitHub Releases rather than +using GHCR package tags with enough specificity. + +| Annotation | +|-| +| `use-github-release.version-checker.io/n8n: "true"` | + +> Note: This annotation only affects semver/latest checks. If `use-sha.version-checker.io/n8n: "true"` is set or the tag is omitted/`latest`, version-checker will fall back to GHCR package tags, as GitHub Releases do not provide SHA information. diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go index 57769fca..33bfedae 100644 --- a/pkg/api/annotations.go +++ b/pkg/api/annotations.go @@ -22,6 +22,10 @@ const ( // set. All other options are ignored when this is set. MatchRegexAnnotationKey = "match-regex.version-checker.io" + // UseGitHubReleaseAnnotationKey will use GitHub releases as the source for + // latest version checks against GHCR-backed images. + UseGitHubReleaseAnnotationKey = "use-github-release.version-checker.io" + // UseMetaDataAnnotationKey is defined as a tag containing anything after the // patch digit. // e.g. v1.0.1-gke.3 v1.0.1-alpha.0, v1.2.3.4... diff --git a/pkg/api/options.go b/pkg/api/options.go index 6b66d162..209fc84b 100644 --- a/pkg/api/options.go +++ b/pkg/api/options.go @@ -19,6 +19,8 @@ type Options struct { UseSHA bool `json:"use-sha,omitempty"` // Resolve SHA to a TAG ResolveSHAToTags bool `json:"resolve-sha-to-tags,omitempty"` + // Use GitHub releases as the source for latest GHCR versions. + UseGitHubRelease bool `json:"use-github-release,omitempty"` // UseMetaData defines whether tags with '-alpha', '-debian.0' etc. is // permissible. diff --git a/pkg/client/client.go b/pkg/client/client.go index 219802d6..c271fbf0 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -22,13 +22,15 @@ import ( // Used for testing/mocking purposes type ClientHandler interface { - Tags(ctx context.Context, imageURL string) ([]api.ImageTag, error) + Tags(ctx context.Context, imageURL string, opts *api.Options) ([]api.ImageTag, error) } // Client is a container image registry client to list tags of given image // URLs. type Client struct { fallbackClient api.ImageClient + ghcrClient *ghcr.Client + ghcrHostname string log *logrus.Entry clients []api.ImageClient @@ -99,8 +101,11 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) if err != nil { return nil, fmt.Errorf("failed to create fallback client: %w", err) } + ghcrClient := ghcr.New(opts.GHCR) c := &Client{ + ghcrClient: ghcrClient, + ghcrHostname: opts.GHCR.Hostname, // Append all the clients in order of which we want to check against clients: append( selfhostedClients, @@ -108,7 +113,7 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) ecr.New(opts.ECR), dockerClient, gcr.New(opts.GCR), - ghcr.New(opts.GHCR), + ghcrClient, quay.New(opts.Quay, log), ), fallbackClient: fallbackClient, @@ -123,15 +128,34 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) } // Tags returns the full list of image tags available, for a given image URL. -func (c *Client) Tags(ctx context.Context, imageURL string) ([]api.ImageTag, error) { +func (c *Client) Tags(ctx context.Context, imageURL string, opts *api.Options) ([]api.ImageTag, error) { client, host, path := c.fromImageURL(imageURL) c.log.Debugf("using client %q for image URL %q", client.Name(), imageURL) repo, image := client.RepoImageFromPath(path) + if opts != nil && opts.UseGitHubRelease && !opts.UseSHA { + if ghcrClient, ok := client.(*ghcr.Client); ok { + return ghcrClient.ReleaseTags(ctx, repo, image) + } + + if c.ghcrClient != nil && isGHCRHost(host, c.ghcrHostname) { + repo, image := c.ghcrClient.RepoImageFromPath(path) + return c.ghcrClient.ReleaseTags(ctx, repo, image) + } + } + return client.Tags(ctx, host, repo, image) } +func isGHCRHost(host, configuredHostname string) bool { + if configuredHostname != "" && configuredHostname == host { + return true + } + + return ghcr.HostReg.MatchString(host) +} + // fromImageURL will return the appropriate registry client for a given // image URL, and the host + path to search. func (c *Client) fromImageURL(imageURL string) (api.ImageClient, string, string) { diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 61a6081b..4b2d6166 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -2,11 +2,14 @@ package client import ( "context" + "net/http" "testing" + "time" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/jarcoal/httpmock" "github.com/jetstack/version-checker/pkg/api" "github.com/jetstack/version-checker/pkg/client/acr" "github.com/jetstack/version-checker/pkg/client/docker" @@ -188,3 +191,59 @@ func TestFromImageURL(t *testing.T) { }) } } + +func TestTagsUsesGitHubReleasesForGHCR(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "https://api.github.com/repos/test-user-owner/test-repo/releases", + func(req *http.Request) (*http.Response, error) { + return httpmock.NewStringResponse(200, `[ + { + "tag_name": "v1.2.3", + "published_at": "2023-07-08T12:34:56Z" + } + ]`), nil + }) + + handler, err := New(context.TODO(), logrus.NewEntry(logrus.New()), Options{ + GHCR: ghcr.Options{ + Token: "test-token", + }, + }) + assert.NoError(t, err) + + tags, err := handler.Tags(context.Background(), "ghcr.io/test-user-owner/test-repo", &api.Options{ + UseGitHubRelease: true, + }) + assert.NoError(t, err) + assert.Equal(t, []api.ImageTag{ + {Tag: "v1.2.3", Timestamp: time.Date(2023, time.July, 8, 12, 34, 56, 0, time.UTC)}, + }, tags) +} + +func TestTagsUsesGitHubReleasesForGHCRWithoutToken(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterResponder("GET", "https://api.github.com/repos/test-user-owner/test-repo/releases", + func(req *http.Request) (*http.Response, error) { + return httpmock.NewStringResponse(200, `[ + { + "tag_name": "v2.3.4", + "published_at": "2024-01-01T10:20:30Z" + } + ]`), nil + }) + + handler, err := New(context.TODO(), logrus.NewEntry(logrus.New()), Options{}) + assert.NoError(t, err) + + tags, err := handler.Tags(context.Background(), "ghcr.io/test-user-owner/test-repo", &api.Options{ + UseGitHubRelease: true, + }) + assert.NoError(t, err) + assert.Equal(t, []api.ImageTag{ + {Tag: "v2.3.4", Timestamp: time.Date(2024, time.January, 1, 10, 20, 30, 0, time.UTC)}, + }, tags) +} diff --git a/pkg/client/ghcr/ghcr.go b/pkg/client/ghcr/ghcr.go index 80a2e5e2..da98549d 100644 --- a/pkg/client/ghcr/ghcr.go +++ b/pkg/client/ghcr/ghcr.go @@ -6,6 +6,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/jetstack/version-checker/pkg/api" @@ -85,6 +86,32 @@ func (c *Client) Tags(ctx context.Context, _, owner, repo string) ([]api.ImageTa return tags, nil } +func (c *Client) ReleaseTags(ctx context.Context, owner, pkg string) ([]api.ImageTag, error) { + repo := releaseRepoFromPackage(pkg) + if repo == "" { + return nil, fmt.Errorf("unable to determine GitHub repository from package %q", pkg) + } + + opts := &github.ListOptions{PerPage: 100} + var tags []api.ImageTag + for { + releases, resp, err := c.client.Repositories.ListReleases(ctx, owner, repo, opts) + if err != nil { + return nil, fmt.Errorf("getting releases: %w", err) + } + + tags = append(tags, extractReleaseTags(releases)...) + + if resp.NextPage == 0 { + break + } + + opts.Page = resp.NextPage + } + + return tags, nil +} + func (c *Client) determineGetAllVersionsFunc(ctx context.Context, owner, repo string) (func(ctx context.Context, owner, pkgType, repo string, opts *github.PackageListOptions) ([]*github.PackageVersion, *github.Response, error), string, error) { getAllVersions := c.client.Organizations.PackageGetAllVersions ownerType, err := c.ownerType(ctx, owner) @@ -162,3 +189,35 @@ func (c *Client) ownerType(ctx context.Context, owner string) (string, error) { return ownerType, nil } + +func releaseRepoFromPackage(pkg string) string { + repo, _, _ := strings.Cut(pkg, "/") + return repo +} + +func extractReleaseTags(releases []*github.RepositoryRelease) []api.ImageTag { + tags := make([]api.ImageTag, 0, len(releases)) + for _, release := range releases { + if release.GetDraft() || release.GetTagName() == "" { + continue + } + + tags = append(tags, api.ImageTag{ + Tag: release.GetTagName(), + Timestamp: releaseTimestamp(release), + }) + } + + return tags +} + +func releaseTimestamp(release *github.RepositoryRelease) time.Time { + switch { + case release.PublishedAt != nil: + return release.PublishedAt.Time + case release.CreatedAt != nil: + return release.CreatedAt.Time + default: + return time.Time{} + } +} diff --git a/pkg/client/ghcr/ghcr_test.go b/pkg/client/ghcr/ghcr_test.go index ff9568f3..fe0553a4 100644 --- a/pkg/client/ghcr/ghcr_test.go +++ b/pkg/client/ghcr/ghcr_test.go @@ -4,9 +4,11 @@ import ( "context" "net/http" "testing" + "time" "github.com/google/go-github/v70/github" "github.com/jarcoal/httpmock" + "github.com/jetstack/version-checker/pkg/api" "github.com/stretchr/testify/assert" ) @@ -60,6 +62,27 @@ func registerTagResponders() { }) } +func registerReleaseResponders() { + httpmock.RegisterResponder("GET", "https://api.github.com/repos/test-user-owner/test-repo/releases", + func(req *http.Request) (*http.Response, error) { + return httpmock.NewStringResponse(200, `[ + { + "tag_name": "v1.0.0", + "published_at": "2023-07-08T12:34:56Z" + }, + { + "tag_name": "v1.1.0", + "created_at": "2023-08-08T12:34:56Z" + }, + { + "tag_name": "v9.9.9", + "draft": true, + "published_at": "2023-09-08T12:34:56Z" + } + ]`), nil + }) +} + func TestClient_Tags(t *testing.T) { setup() defer teardown() @@ -162,3 +185,23 @@ func TestClient_Tags(t *testing.T) { assert.ElementsMatch(t, []string{"tag1", "tag2"}, []string{tags[0].Tag, tags[1].Tag}) }) } + +func TestClient_ReleaseTags(t *testing.T) { + setup() + defer teardown() + + ctx := context.Background() + + httpmock.Reset() + registerReleaseResponders() + + client := New(Options{}) + client.client = github.NewClient(nil) + + tags, err := client.ReleaseTags(ctx, "test-user-owner", "test-repo/subpath") + assert.NoError(t, err) + assert.Equal(t, []api.ImageTag{ + {Tag: "v1.0.0", Timestamp: time.Date(2023, time.July, 8, 12, 34, 56, 0, time.UTC)}, + {Tag: "v1.1.0", Timestamp: time.Date(2023, time.August, 8, 12, 34, 56, 0, time.UTC)}, + }, tags) +} diff --git a/pkg/controller/checker/checker.go b/pkg/controller/checker/checker.go index 387d2ad1..886a9a76 100644 --- a/pkg/controller/checker/checker.go +++ b/pkg/controller/checker/checker.go @@ -145,10 +145,23 @@ func (c *Checker) isLatestSemver(ctx context.Context, imageURL, currentSHA strin isLatest = true } + // GitHub release tags can be semver-only and may not include digest data. + // Only apply SHA mismatch logic when the latest image has comparable SHA(s). + hasComparableSHA := latestImage.SHA != "" + if !hasComparableSHA { + for _, child := range latestImage.Children { + if child.SHA != "" { + hasComparableSHA = true + break + } + } + } + // If using the same image version, // but the SHA has been updated upstream, // mark not latest if currentImage.Equal(latestImageV) && + hasComparableSHA && !latestImage.MatchesSHA(currentSHA) { isLatest = false if latestImage.SHA != "" { diff --git a/pkg/controller/checker/checker_test.go b/pkg/controller/checker/checker_test.go index b5d1f598..1b83778a 100644 --- a/pkg/controller/checker/checker_test.go +++ b/pkg/controller/checker/checker_test.go @@ -502,6 +502,18 @@ func TestIsLatestSemver(t *testing.T) { }, expIsLatest: true, }, + "if current semver is equal, and latest has no SHA data, then true": { + imageURL: "ghcr.io/test/repo", + currentSHA: "sha256:current", + currentImage: semver.Parse("v1.2.4"), + searchResp: &api.ImageTag{ + Tag: "v1.2.4", + }, + expLatestImage: &api.ImageTag{ + Tag: "v1.2.4", + }, + expIsLatest: true, + }, "if current semver is more, then true": { imageURL: "docker.io", currentSHA: "123", diff --git a/pkg/controller/options/options.go b/pkg/controller/options/options.go index 20ae0798..379aa033 100644 --- a/pkg/controller/options/options.go +++ b/pkg/controller/options/options.go @@ -38,6 +38,7 @@ func (b *Builder) Options(name string) (*api.Options, error) { b.handleSHAOption, b.handleSHAToTagOption, b.handleMetadataOption, + b.handleGitHubReleaseOption, b.handleRegexOption, b.handlePinMajorOption, b.handlePinMinorOption, @@ -88,6 +89,14 @@ func (b *Builder) handleMetadataOption(name string, opts *api.Options, setNonSha return nil } +func (b *Builder) handleGitHubReleaseOption(name string, opts *api.Options, setNonSha *bool, errs *[]string) error { + if useGitHubRelease, ok := b.ans[b.index(name, api.UseGitHubReleaseAnnotationKey)]; ok && useGitHubRelease == "true" { + *setNonSha = true + opts.UseGitHubRelease = true + } + return nil +} + func (b *Builder) handleRegexOption(name string, opts *api.Options, setNonSha *bool, errs *[]string) error { if matchRegex, ok := b.ans[b.index(name, api.MatchRegexAnnotationKey)]; ok { *setNonSha = true diff --git a/pkg/controller/options/options_test.go b/pkg/controller/options/options_test.go index d5d1aa21..8d353e34 100644 --- a/pkg/controller/options/options_test.go +++ b/pkg/controller/options/options_test.go @@ -75,6 +75,15 @@ func TestBuild(t *testing.T) { expOptions: nil, expErr: `cannot define "use-sha.version-checker.io/test-name" with any semver options`, }, + "cannot use sha with github releases": { + containerName: "test-name", + annotations: map[string]string{ + api.UseGitHubReleaseAnnotationKey + "/test-name": "true", + api.UseSHAAnnotationKey + "/test-name": "true", + }, + expOptions: nil, + expErr: `cannot define "use-sha.version-checker.io/test-name" with any semver options`, + }, "output options for pins and add metadata": { containerName: "test-name", annotations: map[string]string{ @@ -114,6 +123,16 @@ func TestBuild(t *testing.T) { }, expErr: "", }, + "output options for github releases": { + containerName: "test-name", + annotations: map[string]string{ + api.UseGitHubReleaseAnnotationKey + "/test-name": "true", + }, + expOptions: &api.Options{ + UseGitHubRelease: true, + }, + expErr: "", + }, "output options for resolve sha": { containerName: "test-name", annotations: map[string]string{ diff --git a/pkg/version/version.go b/pkg/version/version.go index b0fb5ebd..15ea21ea 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -40,7 +40,7 @@ func New(log *logrus.Entry, client client.ClientHandler, cacheTimeout time.Durat // LatestTagFromImage will return the latest tag given an imageURL, according // to the given options. func (v *Version) LatestTagFromImage(ctx context.Context, imageURL string, opts *api.Options) (*api.ImageTag, error) { - tagsI, err := v.imageCache.Get(ctx, imageURL, imageURL, nil) + tagsI, err := v.imageCache.Get(ctx, imageCacheIndex(imageURL, opts), imageURL, opts) if err != nil { return nil, err } @@ -94,9 +94,9 @@ func (v *Version) ResolveSHAToTag(ctx context.Context, imageURL string, imageSHA } // Fetch returns the given image tags for a given image URL. -func (v *Version) Fetch(ctx context.Context, imageURL string, _ *api.Options) (interface{}, error) { +func (v *Version) Fetch(ctx context.Context, imageURL string, opts *api.Options) (interface{}, error) { // fetch tags from image URL - tags, err := v.client.Tags(ctx, imageURL) + tags, err := v.client.Tags(ctx, imageURL, opts) if err != nil { return nil, fmt.Errorf("failed to get tags from remote registry for %q: %s", imageURL, err) @@ -111,3 +111,11 @@ func (v *Version) Fetch(ctx context.Context, imageURL string, _ *api.Options) (i return tags, nil } + +func imageCacheIndex(imageURL string, opts *api.Options) string { + if opts != nil && opts.UseGitHubRelease && !opts.UseSHA { + return "github-release:" + imageURL + } + + return imageURL +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go index f10e88cc..83ee4a39 100644 --- a/pkg/version/version_test.go +++ b/pkg/version/version_test.go @@ -368,7 +368,7 @@ func TestFetch(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockClient := &MockClient{} - mockClient.On("Tags", mock.Anything, tt.imageURL).Return(tt.clientTags, tt.clientError) + mockClient.On("Tags", mock.Anything, tt.imageURL, (*api.Options)(nil)).Return(tt.clientTags, tt.clientError) v := &Version{ log: logrus.NewEntry(logrus.New()), @@ -455,7 +455,7 @@ func TestLatestTagFromImage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockClient := &MockClient{} - mockClient.On("Tags", mock.Anything, tt.imageURL).Return(tt.clientTags, tt.clientError) + mockClient.On("Tags", mock.Anything, tt.imageURL, tt.options).Return(tt.clientTags, tt.clientError) log := logrus.NewEntry(logrus.New()) v := &Version{ @@ -529,7 +529,7 @@ type MockClient struct { mock.Mock } -func (m *MockClient) Tags(ctx context.Context, img string) ([]api.ImageTag, error) { - args := m.Called(ctx, img) +func (m *MockClient) Tags(ctx context.Context, img string, opts *api.Options) ([]api.ImageTag, error) { + args := m.Called(ctx, img, opts) return args.Get(0).([]api.ImageTag), args.Error(1) }