diff --git a/tool/actions-gh-release/release.go b/tool/actions-gh-release/release.go index b468ab414d..c072f37c01 100644 --- a/tool/actions-gh-release/release.go +++ b/tool/actions-gh-release/release.go @@ -67,6 +67,7 @@ type ReleaseCommitMatcherConfig struct { ParentOfMergeCommit bool `json:"parentOfMergeCommit,omitempty"` Prefixes []string `json:"prefixes,omitempty"` Contains []string `json:"contains,omitempty"` + Labels []string `json:"labels,omitempty"` } func (c ReleaseCommitMatcherConfig) Empty() bool { @@ -92,6 +93,17 @@ func (c ReleaseCommitMatcherConfig) Match(commit Commit, mergeCommit *Commit) bo return false } +func (c ReleaseCommitMatcherConfig) MatchLabels(labels []*github.Label) bool { + for _, cl := range c.Labels { + for _, l := range labels { + if l.GetName() == cl { + return true + } + } + } + return false +} + func (c *ReleaseConfig) Validate() error { if c.Tag == "" { return fmt.Errorf("tag must be specified") @@ -279,6 +291,14 @@ func buildReleaseCommits(ctx context.Context, ghClient *githubClient, commits [] return pr, nil } + // Whether commitCategory is decided by PR's labels or not. + useLabels := false + for _, ctg := range cfg.CommitCategories { + if len(ctg.Labels) > 0 { + useLabels = true + break + } + } out := make([]ReleaseCommit, 0, len(commits)) for _, commit := range commits { @@ -298,17 +318,22 @@ func buildReleaseCommits(ctx context.Context, ghClient *githubClient, commits [] CategoryName: determineCommitCategory(commit, mergeCommits[commit.Hash], cfg.CommitCategories), } - if gen.UsePullRequestMetadata { + if gen.UsePullRequestMetadata || useLabels { pr, err := getPullRequest(commit) if err != nil { // only error logging, ignore error log.Printf("Failed to get pull request: %v\n", err) } - if pr != nil { + + if pr != nil && gen.UsePullRequestMetadata { c.PullRequestNumber = pr.GetNumber() c.PullRequestOwner = pr.GetUser().GetLogin() c.ReleaseNote = extractReleaseNote(pr.GetTitle(), pr.GetBody(), gen.UseReleaseNoteBlock) } + + if pr != nil && useLabels { + c.CategoryName = determineCommitCategoryOfPR(pr, cfg.CommitCategories) + } } out = append(out, c) @@ -343,6 +368,15 @@ func determineCommitCategory(commit Commit, mergeCommit *Commit, categories []Re return "" } +func determineCommitCategoryOfPR(pr *github.PullRequest, categories []ReleaseCommitCategoryConfig) string { + for _, c := range categories { + if c.ReleaseCommitMatcherConfig.MatchLabels(pr.Labels) { + return c.ID + } + } + return "" +} + func renderReleaseNote(p ReleaseProposal, cfg ReleaseConfig) []byte { var b strings.Builder b.WriteString(fmt.Sprintf("## Release %s with changes since %s\n\n", p.Tag, p.PreTag)) diff --git a/tool/actions-gh-release/release_test.go b/tool/actions-gh-release/release_test.go index 3170931bde..f66e691cab 100644 --- a/tool/actions-gh-release/release_test.go +++ b/tool/actions-gh-release/release_test.go @@ -19,6 +19,7 @@ import ( "fmt" "testing" + "github.com/google/go-github/v39/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -306,6 +307,79 @@ func TestBuildReleaseCommits(t *testing.T) { } } +func TestBuildReleaseCommitsForCategoryLabel(t *testing.T) { + t.Parallel() + + cfgs := []ReleaseCommitCategoryConfig{ + { + ID: "breaking-change", + Title: "Breaking Changes", + ReleaseCommitMatcherConfig: ReleaseCommitMatcherConfig{ + Labels: []string{"change-category/breaking-change"}, + }, + }, + { + ID: "notable-change", + Title: "Notable Changes", + ReleaseCommitMatcherConfig: ReleaseCommitMatcherConfig{ + Labels: []string{"change-category/notable-change"}, + }, + }, + { + ID: "internal-change", + Title: "Internal Changes", + ReleaseCommitMatcherConfig: ReleaseCommitMatcherConfig{}, + }, + } + + testcases := []struct { + title string + labels []*github.Label + categories []ReleaseCommitCategoryConfig + expectedCategory string + }{ + { + title: "no categories config provided", + labels: []*github.Label{}, + categories: []ReleaseCommitCategoryConfig{}, + expectedCategory: "", + }, + { + title: "breaking-change", + labels: []*github.Label{{Name: github.String("change-category/breaking-change")}}, + categories: cfgs, + expectedCategory: "breaking-change", + }, + { + title: "not match any category", + labels: []*github.Label{{Name: github.String("foo")}}, + categories: cfgs, + expectedCategory: "", + }, + { + title: "matching multiple labels results in the first one defined in config", + labels: []*github.Label{ + {Name: github.String("change-category/notable-change")}, + {Name: github.String("change-category/breaking-change")}, + }, + categories: cfgs, + expectedCategory: "breaking-change", + }, + } + + for _, tc := range testcases { + t.Run(tc.title, func(t *testing.T) { + t.Parallel() + + pr := &github.PullRequest{ + Labels: tc.labels, + } + got := determineCommitCategoryOfPR(pr, tc.categories) + assert.Equal(t, tc.expectedCategory, got) + }) + } +} + func TestRenderReleaseNote(t *testing.T) { testcases := []struct { name string