diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 9b3b4656d..6ac3df75c 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -616,9 +616,19 @@ func (c *Client) Tags(ref string) ([]string, error) { } break - } + return extractVersionsFromRegistryTags(registryTags), nil +} +// extractVersionsFromRegistryTags parses a list of OCI tag strings returns a +// list containing only those that are valid Helm chart version strings. +// The returned list is ordered by semantic version with the newest version +// first. +// Semantic versions are *not* de-duplicated. +// Versions may have a v-prefix and if there are two semantically equal versions +// with and without a v-prefix, both will be returned in their same order that +// they were supplied. +func extractVersionsFromRegistryTags(registryTags []string) []string { var tagVersions []*semver.Version for _, tag := range registryTags { tagVersion, err := tagToVersion(tag) @@ -633,11 +643,9 @@ func (c *Client) Tags(ref string) ([]string, error) { tags := make([]string, len(tagVersions)) for iTv, tv := range tagVersions { - tags[iTv] = tv.String() + tags[iTv] = tv.Original() } - - return tags, nil - + return tags } // tagToVersion parses an OCI tag string and converts it to a semantic version. diff --git a/pkg/registry/client_test.go b/pkg/registry/client_test.go index 405dd568e..064ed1f84 100644 --- a/pkg/registry/client_test.go +++ b/pkg/registry/client_test.go @@ -455,3 +455,64 @@ func TestTagToVersion(t *testing.T) { }) } } + +func TestExtractVersionsFromRegistryTags(t *testing.T) { + type testCase struct { + tags []string + versions []string + } + + tests := map[string]testCase{ + "empty tags": { + tags: []string{}, + versions: []string{}, + }, + "nil tags": { + tags: nil, + versions: []string{}, + }, + "one-empty-string": { + tags: []string{""}, + versions: []string{}, + }, + "non-semver": { + tags: []string{"test-tag"}, + versions: []string{}, + }, + "one-strict": { + tags: []string{"1.2.3"}, + versions: []string{"1.2.3"}, + }, + "one-v-prefix": { + tags: []string{"v1.2.3"}, + versions: []string{"v1.2.3"}, + }, + "duplicate-versions": { + tags: []string{"1.2.3", "1.2.3"}, + versions: []string{"1.2.3", "1.2.3"}, + }, + "duplicate-versions-with-and-without-v-prefix-retain-original-order-1": { + tags: []string{"1.2.3", "v1.2.3"}, + versions: []string{"1.2.3", "v1.2.3"}, + }, + "duplicate-versions-with-and-without-v-prefix-retain-original-order-2": { + tags: []string{"v1.2.3", "1.2.3"}, + versions: []string{"v1.2.3", "1.2.3"}, + }, + "unsorted-are-returned-sorted": { + tags: []string{"1.2.3-alpha.1", "v2.3.4-beta.10", "2.3.4", "v1.2.3", "2.3.4-alpha.3"}, + versions: []string{"2.3.4", "v2.3.4-beta.10", "2.3.4-alpha.3", "v1.2.3", "1.2.3-alpha.1"}, + }, + "mix": { + tags: []string{"v1.2.3", "2.3.4", "test-tag", "v1.2.3"}, + versions: []string{"2.3.4", "v1.2.3", "v1.2.3"}, + }, + } + for title, tc := range tests { + tc := tc + t.Run(title, func(t *testing.T) { + versions := extractVersionsFromRegistryTags(tc.tags) + assert.Equal(t, tc.versions, versions) + }) + } +}