From 671356727d7683913f09a7689a47086a02a5915a Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 3 Jul 2024 18:07:04 +0100 Subject: [PATCH 1/9] publish-announcement: sort versions to be decreasing order --- cmd/releasego/publish-announcement.go | 10 +++++++ cmd/releasego/publish-announcement_test.go | 35 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index 9baf990c..9cb96e6f 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -12,6 +12,7 @@ import ( "io" "os" "regexp" + "sort" "strings" "text/template" "time" @@ -56,6 +57,9 @@ type ReleaseInfo struct { func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { ri := new(ReleaseInfo) + // Sort the versions in descending order. + sortVersions(versions) + // Generate human-readable title and URL-friendly slug from the Go versions. ri.Title = generateBlogPostTitle(versions) ri.Slug = generateSlug(ri.Title) @@ -219,6 +223,12 @@ func generateBlogPostTitle(versions []string) string { } } +func sortVersions(versions []string) { + sort.Slice(versions, func(i, j int) bool { + return goversion.New(versions[i]).MajorMinorPatch() > goversion.New(versions[j]).MajorMinorPatch() + }) +} + func generateSlug(input string) string { // Convert to lowercase input = strings.ToLower(input) diff --git a/cmd/releasego/publish-announcement_test.go b/cmd/releasego/publish-announcement_test.go index e21f8e51..530f126f 100644 --- a/cmd/releasego/publish-announcement_test.go +++ b/cmd/releasego/publish-announcement_test.go @@ -25,7 +25,7 @@ func Test_ReleaseInfo_WriteAnnouncement(t *testing.T) { }{ {"real-2024-06-04", NewReleaseInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, true)}, {"only-one-branch", NewReleaseInfo(testTime, []string{"1.22.8-3"}, author, true)}, - {"three-branches", NewReleaseInfo(testTime, []string{"1.23.1-1", "1.22.8-1", "1.21.11-16"}, author, true)}, + {"three-branches", NewReleaseInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -76,3 +76,36 @@ func TestGoReleaseVersionLink(t *testing.T) { t.Errorf("expected the release link to be %q, but got %q", expected, result) } } + +func TestGoReleaseVersionSorting(t *testing.T) { + versions := []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"} + expected := []string{"1.23.1-1", "1.22.8-1", "1.21.11-16"} + + sortVersions(versions) + + if !equal(versions, expected) { + t.Errorf("expected the sorted versions to be %q, but got %q", expected, versions) + } + + versions = []string{"1.22.4-1", "1.21.11-1"} + expected = []string{"1.22.4-1", "1.21.11-1"} + + sortVersions(versions) + + if !equal(versions, expected) { + t.Errorf("expected the sorted versions to be %q, but got %q", expected, versions) + } +} + +// equal is a helper function to compare two slices of strings. +func equal(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} From f0e27b50a1718e7c3544f04217232be9cd021f25 Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 3 Jul 2024 23:04:18 +0100 Subject: [PATCH 2/9] better sorting --- cmd/releasego/publish-announcement.go | 30 +++++++++----- cmd/releasego/publish-announcement_test.go | 47 +++++----------------- goversion/goversion.go | 37 +++++++++++++++++ goversion/goversion_test.go | 33 +++++++++++++++ 4 files changed, 100 insertions(+), 47 deletions(-) diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index 9cb96e6f..022e3cf4 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -54,11 +54,22 @@ type ReleaseInfo struct { } // take all this methods and make it one constructor function for ReleaseInfo -func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { +func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) (*ReleaseInfo, error) { ri := new(ReleaseInfo) - // Sort the versions in descending order. - sortVersions(versions) + // Sort the versions in descending order + var sortErr error + sort.Slice(versions, func(i, j int) bool { + b, err := goversion.New(versions[i]).IsNewerThan(goversion.New(versions[j])) + if err != nil { + sortErr = err + } + return b + }) + + if sortErr != nil { + return nil, fmt.Errorf("error sorting versions: %w", sortErr) + } // Generate human-readable title and URL-friendly slug from the Go versions. ri.Title = generateBlogPostTitle(versions) @@ -88,7 +99,7 @@ func NewReleaseInfo(releaseDate time.Time, versions []string, author string, sec ri.Tags = append(ri.Tags, "security") } - return ri + return ri, nil } func (ri ReleaseInfo) CategoriesString() string { @@ -151,7 +162,10 @@ func publishAnnouncement(p subcmd.ParseFunc) (err error) { } versionsList := strings.Split(releaseVersions, ",") - releaseInfo := NewReleaseInfo(releaseDate, versionsList, author, security) + releaseInfo, err := NewReleaseInfo(releaseDate, versionsList, author, security) + if err != nil { + return fmt.Errorf("error creating release info: %w", err) + } ctx := context.Background() client, err := githubutil.NewClient(ctx, *pat) @@ -223,12 +237,6 @@ func generateBlogPostTitle(versions []string) string { } } -func sortVersions(versions []string) { - sort.Slice(versions, func(i, j int) bool { - return goversion.New(versions[i]).MajorMinorPatch() > goversion.New(versions[j]).MajorMinorPatch() - }) -} - func generateSlug(input string) string { // Convert to lowercase input = strings.ToLower(input) diff --git a/cmd/releasego/publish-announcement_test.go b/cmd/releasego/publish-announcement_test.go index 530f126f..491cbf06 100644 --- a/cmd/releasego/publish-announcement_test.go +++ b/cmd/releasego/publish-announcement_test.go @@ -19,13 +19,21 @@ func Test_ReleaseInfo_WriteAnnouncement(t *testing.T) { author = "Test Author" ) + newInfo := func(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { + ri, err := NewReleaseInfo(releaseDate, versions, author, security) + if err != nil { + t.Fatalf("NewReleaseInfo() error = %v", err) + } + return ri + } + tests := []struct { name string ri *ReleaseInfo }{ - {"real-2024-06-04", NewReleaseInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, true)}, - {"only-one-branch", NewReleaseInfo(testTime, []string{"1.22.8-3"}, author, true)}, - {"three-branches", NewReleaseInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, + {"real-2024-06-04", newInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, true)}, + {"only-one-branch", newInfo(testTime, []string{"1.22.8-3"}, author, true)}, + {"three-branches", newInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -76,36 +84,3 @@ func TestGoReleaseVersionLink(t *testing.T) { t.Errorf("expected the release link to be %q, but got %q", expected, result) } } - -func TestGoReleaseVersionSorting(t *testing.T) { - versions := []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"} - expected := []string{"1.23.1-1", "1.22.8-1", "1.21.11-16"} - - sortVersions(versions) - - if !equal(versions, expected) { - t.Errorf("expected the sorted versions to be %q, but got %q", expected, versions) - } - - versions = []string{"1.22.4-1", "1.21.11-1"} - expected = []string{"1.22.4-1", "1.21.11-1"} - - sortVersions(versions) - - if !equal(versions, expected) { - t.Errorf("expected the sorted versions to be %q, but got %q", expected, versions) - } -} - -// equal is a helper function to compare two slices of strings. -func equal(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/goversion/goversion.go b/goversion/goversion.go index 1d17277b..8bc54625 100644 --- a/goversion/goversion.go +++ b/goversion/goversion.go @@ -156,3 +156,40 @@ func extractPrerelease(part, prerelease *string) { *part = (*part)[:i] } } + +// IsNewerThan compares two versions and returns true if the receiver is newer than the argument +// version. The comparison is done by comparing the major, minor, patch, revision, prerelease and note parts in that +// order. If all parts are equal, false is returned. It may return an error if a part is not an integer. +func (v *GoVersion) IsNewerThan(other *GoVersion) (bool, error) { + cmp := func(a, b string) (bool, error) { + intA, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return false, err + } + intB, err := strconv.ParseInt(b, 10, 64) + if err != nil { + return false, err + } + return intA > intB, nil + } + + if v.Major != other.Major { + return cmp(v.Major, other.Major) + } + if v.Minor != other.Minor { + return cmp(v.Minor, other.Minor) + } + if v.Patch != other.Patch { + return cmp(v.Patch, other.Patch) + } + if v.Revision != other.Revision { + return cmp(v.Revision, other.Revision) + } + if v.Prerelease != other.Prerelease { + return v.Prerelease > other.Prerelease, nil + } + if v.Note != other.Note { + return v.Note > other.Note, nil + } + return false, nil +} diff --git a/goversion/goversion_test.go b/goversion/goversion_test.go index 9e17f166..5c7a82e0 100644 --- a/goversion/goversion_test.go +++ b/goversion/goversion_test.go @@ -4,6 +4,7 @@ package goversion import ( + "fmt" "testing" ) @@ -127,3 +128,35 @@ func TestGoVersion_UpstreamFormatGitTag(t *testing.T) { }) } } + +func TestGoVersion_IsNewerThan(t *testing.T) { + tests := []struct { + version string + other string + want bool + wantErr bool + }{ + {"1.21.3-1", "1.22.2-1", false, false}, + {"1.22.2-1", "1.21.3-1", true, false}, + {"1.22.2-1", "1.22.2-1", false, false}, + {"1.22.2-2", "1.22.2-1", true, false}, + {"1.100.2-1", "1.22.2-2", true, false}, + {"2.1.1-1", "1.1.1-1", true, false}, + {"1.1.2-1", "1.1.1-1", true, false}, + {"1.1.1", "1.2.1-1", false, false}, + {"&.1.1", "1.1.1-1", false, true}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%v_%v", tt.version, tt.other), func(t *testing.T) { + v := New(tt.version) + other := New(tt.other) + got, err := v.IsNewerThan(other) + if (err != nil) != tt.wantErr { + t.Errorf("IsNewerThan() error = %v, wantErr %v", err, tt.wantErr) + } + if got != tt.want { + t.Errorf("IsNewerThan() = %v, want %v", got, tt.want) + } + }) + } +} From 013514e4e7e6d1ec3fbfb1520124da740c292145 Mon Sep 17 00:00:00 2001 From: mertakman Date: Thu, 18 Jul 2024 12:50:51 +0100 Subject: [PATCH 3/9] fix:Remove error returning logic from new release info --- cmd/releasego/publish-announcement.go | 20 ++++---------------- cmd/releasego/publish-announcement_test.go | 16 ++++------------ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index fd628287..8a6b680d 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -55,23 +55,14 @@ type ReleaseInfo struct { } // take all this methods and make it one constructor function for ReleaseInfo -func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) (*ReleaseInfo, error) { +func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { ri := new(ReleaseInfo) // Sort the versions in descending order - var sortErr error sort.Slice(versions, func(i, j int) bool { - b, err := goversion.New(versions[i]).IsNewerThan(goversion.New(versions[j])) - if err != nil { - sortErr = err - } - return b + return true }) - if sortErr != nil { - return nil, fmt.Errorf("error sorting versions: %w", sortErr) - } - // Generate human-readable title and URL-friendly slug from the Go versions. ri.Title = generateBlogPostTitle(versions) ri.Slug = generateSlug(ri.Title) @@ -101,7 +92,7 @@ func NewReleaseInfo(releaseDate time.Time, versions []string, author string, sec ri.SecurityRelease = true } - return ri, nil + return ri } func (ri ReleaseInfo) CategoriesString() string { @@ -164,10 +155,7 @@ func publishAnnouncement(p subcmd.ParseFunc) (err error) { } versionsList := strings.Split(releaseVersions, ",") - releaseInfo, err := NewReleaseInfo(releaseDate, versionsList, author, security) - if err != nil { - return fmt.Errorf("error creating release info: %w", err) - } + releaseInfo := NewReleaseInfo(releaseDate, versionsList, author, security) ctx := context.Background() client, err := githubutil.NewClient(ctx, *pat) diff --git a/cmd/releasego/publish-announcement_test.go b/cmd/releasego/publish-announcement_test.go index afeb7153..06b78b7e 100644 --- a/cmd/releasego/publish-announcement_test.go +++ b/cmd/releasego/publish-announcement_test.go @@ -19,22 +19,14 @@ func Test_ReleaseInfo_WriteAnnouncement(t *testing.T) { author = "Test Author" ) - newInfo := func(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { - ri, err := NewReleaseInfo(releaseDate, versions, author, security) - if err != nil { - t.Fatalf("NewReleaseInfo() error = %v", err) - } - return ri - } - tests := []struct { name string ri *ReleaseInfo }{ - {"2024-06-04-real", newInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, true)}, - {"2024-06-04-nonsecurity", newInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, false)}, - {"only-one-branch", newInfo(testTime, []string{"1.22.8-3"}, author, true)}, - {"three-branches", newInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, + {"2024-06-04-real", NewReleaseInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, true)}, + {"2024-06-04-nonsecurity", NewReleaseInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, false)}, + {"only-one-branch", NewReleaseInfo(testTime, []string{"1.22.8-3"}, author, true)}, + {"three-branches", NewReleaseInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 2c5d45ebc2281fc94b0342db413a417b8c889097 Mon Sep 17 00:00:00 2001 From: mertakman Date: Thu, 18 Jul 2024 17:03:02 +0100 Subject: [PATCH 4/9] fix:use stdlib for sorting --- .../testdata/publish-announcement/three-branches.golden.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/releasego/testdata/publish-announcement/three-branches.golden.md b/cmd/releasego/testdata/publish-announcement/three-branches.golden.md index f7a76ce3..1f515fcc 100644 --- a/cmd/releasego/testdata/publish-announcement/three-branches.golden.md +++ b/cmd/releasego/testdata/publish-announcement/three-branches.golden.md @@ -1,7 +1,7 @@ --- -post_title: Go 1.23.1-1, 1.22.8-1, and 1.21.11-16 Microsoft builds now available +post_title: Go 1.22.8-1, 1.23.1-1, and 1.21.11-16 Microsoft builds now available author1: Test Author -post_slug: go-1-23-1-1--1-22-8-1-and-1-21-11-16-microsoft-builds-now-available +post_slug: go-1-22-8-1--1-23-1-1-and-1-21-11-16-microsoft-builds-now-available categories: Microsoft for Go Developers, Security tags: go, release, security featured_image: From aeacee33d06f9f226f4ce0784aaee7fc02d149b3 Mon Sep 17 00:00:00 2001 From: mertakman Date: Thu, 18 Jul 2024 17:03:12 +0100 Subject: [PATCH 5/9] fix:use stdlib for sorting --- cmd/releasego/publish-announcement.go | 19 +++--- goversion/goversion.go | 50 ++++++++------- goversion/goversion_test.go | 89 +++++++++++++++++++-------- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index 8a6b680d..c61c96d9 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -57,11 +57,13 @@ type ReleaseInfo struct { // take all this methods and make it one constructor function for ReleaseInfo func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { ri := new(ReleaseInfo) + goVersions := make(goversion.GoVersions, 0) + for _, version := range versions { + goVersions = append(goVersions, goversion.New(version)) + } // Sort the versions in descending order - sort.Slice(versions, func(i, j int) bool { - return true - }) + sort.Sort(goVersions) // Generate human-readable title and URL-friendly slug from the Go versions. ri.Title = generateBlogPostTitle(versions) @@ -72,13 +74,12 @@ func NewReleaseInfo(releaseDate time.Time, versions []string, author string, sec ri.Tags = []string{"go", "release"} // Process each Go version, extracting release information and generating links. - for _, version := range versions { - goVersion := goversion.New(version).UpstreamFormatGitTag() + for _, goVersion := range goVersions { ri.Versions = append(ri.Versions, GoVersionData{ - MSGoVersion: "v" + version, - MSGoVersionLink: createMSGoReleaseLinkFromVersion(version), - GoVersion: goVersion, - GoVersionLink: createGoReleaseLinkFromVersion(goVersion), + MSGoVersion: "v" + goVersion.MajorMinorPatchPrereleaseRevision(), + MSGoVersionLink: createMSGoReleaseLinkFromVersion(goVersion.MajorMinorPatchPrereleaseRevision()), + GoVersion: goVersion.UpstreamFormatGitTag(), + GoVersionLink: createGoReleaseLinkFromVersion(goVersion.UpstreamFormatGitTag()), }) } diff --git a/goversion/goversion.go b/goversion/goversion.go index 8bc54625..fc5709ae 100644 --- a/goversion/goversion.go +++ b/goversion/goversion.go @@ -157,39 +157,43 @@ func extractPrerelease(part, prerelease *string) { } } -// IsNewerThan compares two versions and returns true if the receiver is newer than the argument -// version. The comparison is done by comparing the major, minor, patch, revision, prerelease and note parts in that -// order. If all parts are equal, false is returned. It may return an error if a part is not an integer. -func (v *GoVersion) IsNewerThan(other *GoVersion) (bool, error) { - cmp := func(a, b string) (bool, error) { - intA, err := strconv.ParseInt(a, 10, 64) +type GoVersions []*GoVersion + +func (versions GoVersions) Len() int { return len(versions) } +func (versions GoVersions) Swap(i, j int) { versions[i], versions[j] = versions[j], versions[i] } +func (versions GoVersions) Less(i, j int) bool { + less := func(a, b string) bool { + intA, err := strconv.Atoi(a) if err != nil { - return false, err + return false } - intB, err := strconv.ParseInt(b, 10, 64) + + intB, err := strconv.Atoi(b) if err != nil { - return false, err + return false } - return intA > intB, nil + return intA > intB } - if v.Major != other.Major { - return cmp(v.Major, other.Major) + current, next := versions[i], versions[j] + + if current.Major != next.Major { + return less(current.Major, next.Major) } - if v.Minor != other.Minor { - return cmp(v.Minor, other.Minor) + if current.Minor != next.Minor { + return less(current.Minor, next.Minor) } - if v.Patch != other.Patch { - return cmp(v.Patch, other.Patch) + if current.Patch != next.Patch { + return less(current.Patch, next.Patch) } - if v.Revision != other.Revision { - return cmp(v.Revision, other.Revision) + if current.Revision != next.Revision { + return less(current.Revision, next.Revision) } - if v.Prerelease != other.Prerelease { - return v.Prerelease > other.Prerelease, nil + if current.Prerelease != next.Prerelease { + return current.Prerelease < next.Prerelease } - if v.Note != other.Note { - return v.Note > other.Note, nil + if current.Note != next.Note { + return current.Note < next.Note } - return false, nil + return false } diff --git a/goversion/goversion_test.go b/goversion/goversion_test.go index 5c7a82e0..1dccf48f 100644 --- a/goversion/goversion_test.go +++ b/goversion/goversion_test.go @@ -4,7 +4,7 @@ package goversion import ( - "fmt" + "sort" "testing" ) @@ -129,33 +129,74 @@ func TestGoVersion_UpstreamFormatGitTag(t *testing.T) { } } -func TestGoVersion_IsNewerThan(t *testing.T) { +func TestGoVersions_Sort(t *testing.T) { tests := []struct { - version string - other string - want bool - wantErr bool + name string + versions GoVersions + expected GoVersions }{ - {"1.21.3-1", "1.22.2-1", false, false}, - {"1.22.2-1", "1.21.3-1", true, false}, - {"1.22.2-1", "1.22.2-1", false, false}, - {"1.22.2-2", "1.22.2-1", true, false}, - {"1.100.2-1", "1.22.2-2", true, false}, - {"2.1.1-1", "1.1.1-1", true, false}, - {"1.1.2-1", "1.1.1-1", true, false}, - {"1.1.1", "1.2.1-1", false, false}, - {"&.1.1", "1.1.1-1", false, true}, + { + name: "basic version sorting", + versions: GoVersions{ + New("1.2.3"), + New("1.2.1"), + New("1.3.0"), + New("1.2.3-2"), + New("1.2.3-1"), + }, + expected: GoVersions{ + New("1.3.0"), + New("1.2.3-2"), + New("1.2.3"), + New("1.2.3-1"), + New("1.2.1"), + }, + }, + { + name: "version with prerelease and note", + versions: GoVersions{ + New("1.2.3-beta"), + New("1.2.3-rc1"), + New("1.2.3-1-fips"), + New("1.2.3"), + New("1.2.3-2"), + }, + expected: GoVersions{ + New("1.2.3-2"), + New("1.2.3"), + New("1.2.3-beta"), + New("1.2.3-1-fips"), + New("1.2.3-rc1"), + }, + }, + { + name: "sorting with major and minor versions", + versions: GoVersions{ + New("2.0.0"), + New("1.10.0"), + New("1.2.3"), + New("1.2.0"), + New("1.3.0"), + }, + expected: GoVersions{ + New("2.0.0"), + New("1.10.0"), + New("1.3.0"), + New("1.2.3"), + New("1.2.0"), + }, + }, } + for _, tt := range tests { - t.Run(fmt.Sprintf("%v_%v", tt.version, tt.other), func(t *testing.T) { - v := New(tt.version) - other := New(tt.other) - got, err := v.IsNewerThan(other) - if (err != nil) != tt.wantErr { - t.Errorf("IsNewerThan() error = %v, wantErr %v", err, tt.wantErr) - } - if got != tt.want { - t.Errorf("IsNewerThan() = %v, want %v", got, tt.want) + t.Run(tt.name, func(t *testing.T) { + // Sort the versions + sort.Sort(tt.versions) + for i, v := range tt.versions { + if *v != *tt.expected[i] { + t.Errorf(tt.name) + t.Errorf("expected %v at index %d, got %v", tt.expected[i].Original, i, v.Original) + } } }) } From e02f7857f3a5fc7d499864ed0fb71ab05ea83591 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 18 Jul 2024 10:51:52 -0700 Subject: [PATCH 6/9] Apply suggestions from code review --- goversion/goversion.go | 3 +++ goversion/goversion_test.go | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/goversion/goversion.go b/goversion/goversion.go index fc5709ae..81999ec2 100644 --- a/goversion/goversion.go +++ b/goversion/goversion.go @@ -157,6 +157,9 @@ func extractPrerelease(part, prerelease *string) { } } +// GoVersions implements [sort.Interface] and sorts versions in descending order. +// If Major, Minor, Patch, or Revision of any GoVersion in the slice can't be parsed by +// [strconv.Atoi], the result of using this type is undefined. type GoVersions []*GoVersion func (versions GoVersions) Len() int { return len(versions) } diff --git a/goversion/goversion_test.go b/goversion/goversion_test.go index 1dccf48f..8d57951a 100644 --- a/goversion/goversion_test.go +++ b/goversion/goversion_test.go @@ -194,7 +194,6 @@ func TestGoVersions_Sort(t *testing.T) { sort.Sort(tt.versions) for i, v := range tt.versions { if *v != *tt.expected[i] { - t.Errorf(tt.name) t.Errorf("expected %v at index %d, got %v", tt.expected[i].Original, i, v.Original) } } From 26fcb927b9378c3d4ea05c2e6fd52a2db273eb68 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 18 Jul 2024 10:59:58 -0700 Subject: [PATCH 7/9] Move GoVersions to new internal package --- cmd/releasego/publish-announcement.go | 3 +- goversion/goversion.go | 44 --------------- goversion/goversion_test.go | 73 ------------------------ internal/infrasort/goversions.go | 51 +++++++++++++++++ internal/infrasort/goversions_test.go | 80 +++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 118 deletions(-) create mode 100644 internal/infrasort/goversions.go create mode 100644 internal/infrasort/goversions_test.go diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index c61c96d9..6afe51b2 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -20,6 +20,7 @@ import ( "github.com/microsoft/go-infra/githubutil" "github.com/microsoft/go-infra/goversion" + "github.com/microsoft/go-infra/internal/infrasort" "github.com/microsoft/go-infra/subcmd" ) @@ -57,7 +58,7 @@ type ReleaseInfo struct { // take all this methods and make it one constructor function for ReleaseInfo func NewReleaseInfo(releaseDate time.Time, versions []string, author string, security bool) *ReleaseInfo { ri := new(ReleaseInfo) - goVersions := make(goversion.GoVersions, 0) + goVersions := make(infrasort.GoVersions, 0) for _, version := range versions { goVersions = append(goVersions, goversion.New(version)) } diff --git a/goversion/goversion.go b/goversion/goversion.go index 81999ec2..1d17277b 100644 --- a/goversion/goversion.go +++ b/goversion/goversion.go @@ -156,47 +156,3 @@ func extractPrerelease(part, prerelease *string) { *part = (*part)[:i] } } - -// GoVersions implements [sort.Interface] and sorts versions in descending order. -// If Major, Minor, Patch, or Revision of any GoVersion in the slice can't be parsed by -// [strconv.Atoi], the result of using this type is undefined. -type GoVersions []*GoVersion - -func (versions GoVersions) Len() int { return len(versions) } -func (versions GoVersions) Swap(i, j int) { versions[i], versions[j] = versions[j], versions[i] } -func (versions GoVersions) Less(i, j int) bool { - less := func(a, b string) bool { - intA, err := strconv.Atoi(a) - if err != nil { - return false - } - - intB, err := strconv.Atoi(b) - if err != nil { - return false - } - return intA > intB - } - - current, next := versions[i], versions[j] - - if current.Major != next.Major { - return less(current.Major, next.Major) - } - if current.Minor != next.Minor { - return less(current.Minor, next.Minor) - } - if current.Patch != next.Patch { - return less(current.Patch, next.Patch) - } - if current.Revision != next.Revision { - return less(current.Revision, next.Revision) - } - if current.Prerelease != next.Prerelease { - return current.Prerelease < next.Prerelease - } - if current.Note != next.Note { - return current.Note < next.Note - } - return false -} diff --git a/goversion/goversion_test.go b/goversion/goversion_test.go index 8d57951a..9e17f166 100644 --- a/goversion/goversion_test.go +++ b/goversion/goversion_test.go @@ -4,7 +4,6 @@ package goversion import ( - "sort" "testing" ) @@ -128,75 +127,3 @@ func TestGoVersion_UpstreamFormatGitTag(t *testing.T) { }) } } - -func TestGoVersions_Sort(t *testing.T) { - tests := []struct { - name string - versions GoVersions - expected GoVersions - }{ - { - name: "basic version sorting", - versions: GoVersions{ - New("1.2.3"), - New("1.2.1"), - New("1.3.0"), - New("1.2.3-2"), - New("1.2.3-1"), - }, - expected: GoVersions{ - New("1.3.0"), - New("1.2.3-2"), - New("1.2.3"), - New("1.2.3-1"), - New("1.2.1"), - }, - }, - { - name: "version with prerelease and note", - versions: GoVersions{ - New("1.2.3-beta"), - New("1.2.3-rc1"), - New("1.2.3-1-fips"), - New("1.2.3"), - New("1.2.3-2"), - }, - expected: GoVersions{ - New("1.2.3-2"), - New("1.2.3"), - New("1.2.3-beta"), - New("1.2.3-1-fips"), - New("1.2.3-rc1"), - }, - }, - { - name: "sorting with major and minor versions", - versions: GoVersions{ - New("2.0.0"), - New("1.10.0"), - New("1.2.3"), - New("1.2.0"), - New("1.3.0"), - }, - expected: GoVersions{ - New("2.0.0"), - New("1.10.0"), - New("1.3.0"), - New("1.2.3"), - New("1.2.0"), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Sort the versions - sort.Sort(tt.versions) - for i, v := range tt.versions { - if *v != *tt.expected[i] { - t.Errorf("expected %v at index %d, got %v", tt.expected[i].Original, i, v.Original) - } - } - }) - } -} diff --git a/internal/infrasort/goversions.go b/internal/infrasort/goversions.go new file mode 100644 index 00000000..47c84344 --- /dev/null +++ b/internal/infrasort/goversions.go @@ -0,0 +1,51 @@ +package infrasort + +import ( + "strconv" + + "github.com/microsoft/go-infra/goversion" +) + +// GoVersions implements [sort.Interface] and sorts versions in descending order. +// If Major, Minor, Patch, or Revision of any GoVersion in the slice can't be parsed by +// [strconv.Atoi], the result of using this type is undefined. +type GoVersions []*goversion.GoVersion + +func (versions GoVersions) Len() int { return len(versions) } +func (versions GoVersions) Swap(i, j int) { versions[i], versions[j] = versions[j], versions[i] } +func (versions GoVersions) Less(i, j int) bool { + less := func(a, b string) bool { + intA, err := strconv.Atoi(a) + if err != nil { + return false + } + + intB, err := strconv.Atoi(b) + if err != nil { + return false + } + return intA > intB + } + + current, next := versions[i], versions[j] + + if current.Major != next.Major { + return less(current.Major, next.Major) + } + if current.Minor != next.Minor { + return less(current.Minor, next.Minor) + } + if current.Patch != next.Patch { + return less(current.Patch, next.Patch) + } + if current.Revision != next.Revision { + return less(current.Revision, next.Revision) + } + if current.Prerelease != next.Prerelease { + return current.Prerelease < next.Prerelease + } + if current.Note != next.Note { + return current.Note < next.Note + } + return false +} diff --git a/internal/infrasort/goversions_test.go b/internal/infrasort/goversions_test.go new file mode 100644 index 00000000..396ae2ba --- /dev/null +++ b/internal/infrasort/goversions_test.go @@ -0,0 +1,80 @@ +package infrasort + +import ( + "sort" + "testing" + + "github.com/microsoft/go-infra/goversion" +) + +func TestGoVersions_Sort(t *testing.T) { + tests := []struct { + name string + versions GoVersions + expected GoVersions + }{ + { + name: "basic version sorting", + versions: GoVersions{ + goversion.New("1.2.3"), + goversion.New("1.2.1"), + goversion.New("1.3.0"), + goversion.New("1.2.3-2"), + goversion.New("1.2.3-1"), + }, + expected: GoVersions{ + goversion.New("1.3.0"), + goversion.New("1.2.3-2"), + goversion.New("1.2.3"), + goversion.New("1.2.3-1"), + goversion.New("1.2.1"), + }, + }, + { + name: "version with prerelease and note", + versions: GoVersions{ + goversion.New("1.2.3-beta"), + goversion.New("1.2.3-rc1"), + goversion.New("1.2.3-1-fips"), + goversion.New("1.2.3"), + goversion.New("1.2.3-2"), + }, + expected: GoVersions{ + goversion.New("1.2.3-2"), + goversion.New("1.2.3"), + goversion.New("1.2.3-beta"), + goversion.New("1.2.3-1-fips"), + goversion.New("1.2.3-rc1"), + }, + }, + { + name: "sorting with major and minor versions", + versions: GoVersions{ + goversion.New("2.0.0"), + goversion.New("1.10.0"), + goversion.New("1.2.3"), + goversion.New("1.2.0"), + goversion.New("1.3.0"), + }, + expected: GoVersions{ + goversion.New("2.0.0"), + goversion.New("1.10.0"), + goversion.New("1.3.0"), + goversion.New("1.2.3"), + goversion.New("1.2.0"), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Sort the versions + sort.Sort(tt.versions) + for i, v := range tt.versions { + if *v != *tt.expected[i] { + t.Errorf("expected %v at index %d, got %v", tt.expected[i].Original, i, v.Original) + } + } + }) + } +} From caae827dc7aa80afc679e243d5184832ee915a86 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 18 Jul 2024 11:08:32 -0700 Subject: [PATCH 8/9] Fix blog post title (use sorted data) --- cmd/releasego/publish-announcement.go | 10 ++++++++-- .../publish-announcement/three-branches.golden.md | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/releasego/publish-announcement.go b/cmd/releasego/publish-announcement.go index 6afe51b2..ffe6544c 100644 --- a/cmd/releasego/publish-announcement.go +++ b/cmd/releasego/publish-announcement.go @@ -66,6 +66,12 @@ func NewReleaseInfo(releaseDate time.Time, versions []string, author string, sec // Sort the versions in descending order sort.Sort(goVersions) + // Recreate versions slice with sorted info. + versions = versions[:0] + for _, goVersion := range goVersions { + versions = append(versions, goVersion.Full()) + } + // Generate human-readable title and URL-friendly slug from the Go versions. ri.Title = generateBlogPostTitle(versions) ri.Slug = generateSlug(ri.Title) @@ -77,8 +83,8 @@ func NewReleaseInfo(releaseDate time.Time, versions []string, author string, sec // Process each Go version, extracting release information and generating links. for _, goVersion := range goVersions { ri.Versions = append(ri.Versions, GoVersionData{ - MSGoVersion: "v" + goVersion.MajorMinorPatchPrereleaseRevision(), - MSGoVersionLink: createMSGoReleaseLinkFromVersion(goVersion.MajorMinorPatchPrereleaseRevision()), + MSGoVersion: "v" + goVersion.Full(), + MSGoVersionLink: createMSGoReleaseLinkFromVersion(goVersion.Full()), GoVersion: goVersion.UpstreamFormatGitTag(), GoVersionLink: createGoReleaseLinkFromVersion(goVersion.UpstreamFormatGitTag()), }) diff --git a/cmd/releasego/testdata/publish-announcement/three-branches.golden.md b/cmd/releasego/testdata/publish-announcement/three-branches.golden.md index 1f515fcc..f7a76ce3 100644 --- a/cmd/releasego/testdata/publish-announcement/three-branches.golden.md +++ b/cmd/releasego/testdata/publish-announcement/three-branches.golden.md @@ -1,7 +1,7 @@ --- -post_title: Go 1.22.8-1, 1.23.1-1, and 1.21.11-16 Microsoft builds now available +post_title: Go 1.23.1-1, 1.22.8-1, and 1.21.11-16 Microsoft builds now available author1: Test Author -post_slug: go-1-22-8-1--1-23-1-1-and-1-21-11-16-microsoft-builds-now-available +post_slug: go-1-23-1-1--1-22-8-1-and-1-21-11-16-microsoft-builds-now-available categories: Microsoft for Go Developers, Security tags: go, release, security featured_image: From 1e4a0b2566e66da2f671024c8e979cc820c6e7a6 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 18 Jul 2024 11:11:08 -0700 Subject: [PATCH 9/9] Add test that includes a Note --- cmd/releasego/publish-announcement_test.go | 1 + .../2024-06-04-note.golden.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 cmd/releasego/testdata/publish-announcement/2024-06-04-note.golden.md diff --git a/cmd/releasego/publish-announcement_test.go b/cmd/releasego/publish-announcement_test.go index 06b78b7e..a71bd09b 100644 --- a/cmd/releasego/publish-announcement_test.go +++ b/cmd/releasego/publish-announcement_test.go @@ -27,6 +27,7 @@ func Test_ReleaseInfo_WriteAnnouncement(t *testing.T) { {"2024-06-04-nonsecurity", NewReleaseInfo(testTime, []string{"1.22.4-1", "1.21.11-1"}, author, false)}, {"only-one-branch", NewReleaseInfo(testTime, []string{"1.22.8-3"}, author, true)}, {"three-branches", NewReleaseInfo(testTime, []string{"1.22.8-1", "1.23.1-1", "1.21.11-16"}, author, true)}, + {"2024-06-04-note", NewReleaseInfo(testTime, []string{"1.22.4-1-fips", "1.22.4-1", "1.21.11-1", "1.21.11-1-fips"}, author, false)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/releasego/testdata/publish-announcement/2024-06-04-note.golden.md b/cmd/releasego/testdata/publish-announcement/2024-06-04-note.golden.md new file mode 100644 index 00000000..b7b9f5cb --- /dev/null +++ b/cmd/releasego/testdata/publish-announcement/2024-06-04-note.golden.md @@ -0,0 +1,19 @@ +--- +post_title: Go 1.22.4-1, 1.22.4-1-fips, 1.21.11-1, and 1.21.11-1-fips Microsoft builds now available +author1: Test Author +post_slug: go-1-22-4-1--1-22-4-1-fips-1-21-11-1-and-1-21-11-1-fips-microsoft-builds-now-available +categories: Microsoft for Go Developers +tags: go, release +featured_image: +summary: The Microsoft builds of the Go security patches released today, are now available for download. +--- + +A new set of Microsoft Go builds is now [available for download](https://github.com/microsoft/go#download-and-install). +For more information about this release and the changes included, see the table below: + +| Microsoft Release | Upstream Tag | +|-------------------|--------------| +| [v1.22.4-1](https://github.com/microsoft/go/releases/tag/v1.22.4-1) | go1.22.4 [release notes](https://go.dev/doc/devel/release#go1.22.4) | +| [v1.22.4-1-fips](https://github.com/microsoft/go/releases/tag/v1.22.4-1-fips) | go1.22.4 [release notes](https://go.dev/doc/devel/release#go1.22.4) | +| [v1.21.11-1](https://github.com/microsoft/go/releases/tag/v1.21.11-1) | go1.21.11 [release notes](https://go.dev/doc/devel/release#go1.21.11) | +| [v1.21.11-1-fips](https://github.com/microsoft/go/releases/tag/v1.21.11-1-fips) | go1.21.11 [release notes](https://go.dev/doc/devel/release#go1.21.11) |