From f9935776f111d3cada25f6aa19e1e60f0abcce72 Mon Sep 17 00:00:00 2001 From: Kent Date: Tue, 25 Apr 2023 13:45:40 -0400 Subject: [PATCH] ensure deterministic results for SortBySemVer() Signed-off-by: Kent --- pkg/tag/semver.go | 31 +++++++++++++++++++++++++++++++ pkg/tag/tag.go | 2 +- pkg/tag/tag_test.go | 7 ++++--- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 pkg/tag/semver.go diff --git a/pkg/tag/semver.go b/pkg/tag/semver.go new file mode 100644 index 00000000..71ea3cee --- /dev/null +++ b/pkg/tag/semver.go @@ -0,0 +1,31 @@ +package tag + +import "github.com/Masterminds/semver" + +// semverCollection is a replacement for semver.Collection that breaks version +// comparison ties through a lexical comparison of the original version strings. +// Using this, instead of semver.Collection, when sorting will yield +// deterministic results that semver.Collection will not yield. +type semverCollection []*semver.Version + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (s semverCollection) Len() int { + return len(s) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (s semverCollection) Less(i, j int) bool { + comp := s[i].Compare(s[j]) + if comp != 0 { + return comp < 0 + } + return s[i].Original() < s[j].Original() +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (s semverCollection) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} diff --git a/pkg/tag/tag.go b/pkg/tag/tag.go index 5d16024e..36d083d3 100644 --- a/pkg/tag/tag.go +++ b/pkg/tag/tag.go @@ -165,7 +165,7 @@ func (il ImageTagList) SortBySemVer() SortableImageTagList { } svl = append(svl, svi) } - sort.Sort(semver.Collection(svl)) + sort.Sort(semverCollection(svl)) for _, svi := range svl { sil = append(sil, NewImageTag(svi.Original(), *il.items[svi.Original()].TagDate, il.items[svi.Original()].TagDigest)) } diff --git a/pkg/tag/tag_test.go b/pkg/tag/tag_test.go index fb5bc02c..23beb6d3 100644 --- a/pkg/tag/tag_test.go +++ b/pkg/tag/tag_test.go @@ -106,7 +106,7 @@ func Test_SortableImageTagList(t *testing.T) { }) t.Run("Sort by semver", func(t *testing.T) { - names := []string{"v2.0.2", "v1.0", "v1.0.1", "v2.0.3", "v2.0"} + names := []string{"v2.0.2", "v1.0", "v2.0.0", "v1.0.1", "v2.0.3", "v2.0"} il := NewImageTagList() for _, name := range names { tag := NewImageTag(name, time.Now(), "") @@ -117,8 +117,9 @@ func Test_SortableImageTagList(t *testing.T) { assert.Equal(t, "v1.0", sil[0].TagName) assert.Equal(t, "v1.0.1", sil[1].TagName) assert.Equal(t, "v2.0", sil[2].TagName) - assert.Equal(t, "v2.0.2", sil[3].TagName) - assert.Equal(t, "v2.0.3", sil[4].TagName) + assert.Equal(t, "v2.0.0", sil[3].TagName) + assert.Equal(t, "v2.0.2", sil[4].TagName) + assert.Equal(t, "v2.0.3", sil[5].TagName) }) t.Run("Sort by date", func(t *testing.T) {