Skip to content

Commit 677a182

Browse files
authored
Merge pull request #8817 from g-gaston/release-notes-concurrency
🌱 Make release notes generation concurrent
2 parents aa93b82 + c472b28 commit 677a182

File tree

1 file changed

+105
-44
lines changed

1 file changed

+105
-44
lines changed

hack/tools/release/notes.go

Lines changed: 105 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"regexp"
3232
"sort"
3333
"strings"
34+
"sync"
3435
"time"
3536
)
3637

@@ -64,8 +65,9 @@ var (
6465

6566
fromTag = flag.String("from", "", "The tag or commit to start from.")
6667

67-
since = flag.String("since", "", "Include commits starting from and including this date. Accepts format: YYYY-MM-DD")
68-
until = flag.String("until", "", "Include commits up to and including this date. Accepts format: YYYY-MM-DD")
68+
since = flag.String("since", "", "Include commits starting from and including this date. Accepts format: YYYY-MM-DD")
69+
until = flag.String("until", "", "Include commits up to and including this date. Accepts format: YYYY-MM-DD")
70+
numWorkers = flag.Int("workers", 10, "Number of concurrent routines to process PR entries. If running into GitHub rate limiting, use 1.")
6971

7072
tagRegex = regexp.MustCompile(`^\[release-[\w-\.]*\]`)
7173
)
@@ -129,7 +131,7 @@ func getAreaLabel(merge string) (string, error) {
129131

130132
out, err := cmd.CombinedOutput()
131133
if err != nil {
132-
return "", err
134+
return "", fmt.Errorf("%s: %v", string(out), err)
133135
}
134136

135137
pr := &githubPullRequest{}
@@ -223,53 +225,49 @@ func run() int {
223225
}
224226
}
225227

226-
for _, c := range commits {
227-
body := trimTitle(c.body)
228-
var key, prNumber, fork string
229-
prefix, err := getAreaLabel(c.merge)
230-
if err != nil {
231-
fmt.Println(err)
232-
os.Exit(1)
233-
}
234-
switch {
235-
case strings.HasPrefix(body, ":sparkles:"), strings.HasPrefix(body, "✨"):
236-
key = features
237-
body = strings.TrimPrefix(body, ":sparkles:")
238-
body = strings.TrimPrefix(body, "✨")
239-
case strings.HasPrefix(body, ":bug:"), strings.HasPrefix(body, "🐛"):
240-
key = bugs
241-
body = strings.TrimPrefix(body, ":bug:")
242-
body = strings.TrimPrefix(body, "🐛")
243-
case strings.HasPrefix(body, ":book:"), strings.HasPrefix(body, "📖"):
244-
key = documentation
245-
body = strings.TrimPrefix(body, ":book:")
246-
body = strings.TrimPrefix(body, "📖")
247-
if strings.Contains(body, "CAEP") || strings.Contains(body, "proposal") {
248-
key = proposals
228+
results := make(chan releaseNoteEntryResult)
229+
commitCh := make(chan *commit)
230+
var wg sync.WaitGroup
231+
232+
wg.Add(*numWorkers)
233+
for i := 0; i < *numWorkers; i++ {
234+
go func() {
235+
for commit := range commitCh {
236+
processed := releaseNoteEntryResult{}
237+
processed.prEntry, processed.err = generateReleaseNoteEntry(commit)
238+
results <- processed
249239
}
250-
case strings.HasPrefix(body, ":seedling:"), strings.HasPrefix(body, "🌱"):
251-
key = other
252-
body = strings.TrimPrefix(body, ":seedling:")
253-
body = strings.TrimPrefix(body, "🌱")
254-
case strings.HasPrefix(body, ":warning:"), strings.HasPrefix(body, "⚠️"):
255-
key = warning
256-
body = strings.TrimPrefix(body, ":warning:")
257-
body = strings.TrimPrefix(body, "⚠️")
258-
default:
259-
key = unknown
240+
wg.Done()
241+
}()
242+
}
243+
244+
go func() {
245+
for _, c := range commits {
246+
commitCh <- c
247+
}
248+
close(commitCh)
249+
}()
250+
251+
go func() {
252+
wg.Wait()
253+
close(results)
254+
}()
255+
256+
for result := range results {
257+
if result.err != nil {
258+
fmt.Println(result.err)
259+
os.Exit(0)
260260
}
261261

262-
body = strings.TrimSpace(body)
263-
if body == "" {
262+
if result.prEntry.title == "" {
264263
continue
265264
}
266-
body = fmt.Sprintf("- %s: %s", prefix, body)
267-
_, _ = fmt.Sscanf(c.merge, "Merge pull request %s from %s", &prNumber, &fork)
268-
if key == documentation {
269-
merges[key] = append(merges[key], prNumber)
270-
continue
265+
266+
if result.prEntry.section == documentation {
267+
merges[result.prEntry.section] = append(merges[result.prEntry.section], result.prEntry.prNumber)
268+
} else {
269+
merges[result.prEntry.section] = append(merges[result.prEntry.section], result.prEntry.title)
271270
}
272-
merges[key] = append(merges[key], formatMerge(body, prNumber))
273271
}
274272

275273
// TODO Turn this into a link (requires knowing the project name + organization)
@@ -346,3 +344,66 @@ func commandExists(cmd string) bool {
346344
_, err := exec.LookPath(cmd)
347345
return err == nil
348346
}
347+
348+
// releaseNoteEntryResult is the result of processing a PR to create a release note item.
349+
// Used to aggregate the line item and error when processing concurrently.
350+
type releaseNoteEntryResult struct {
351+
prEntry *releaseNoteEntry
352+
err error
353+
}
354+
355+
// releaseNoteEntry represents a line item in the release notes.
356+
type releaseNoteEntry struct {
357+
title string
358+
section string
359+
prNumber string
360+
}
361+
362+
// generateReleaseNoteEntry processes a commit into a PR line item for the release notes.
363+
func generateReleaseNoteEntry(c *commit) (*releaseNoteEntry, error) {
364+
entry := &releaseNoteEntry{}
365+
entry.title = trimTitle(c.body)
366+
var fork string
367+
prefix, err := getAreaLabel(c.merge)
368+
if err != nil {
369+
return nil, err
370+
}
371+
372+
switch {
373+
case strings.HasPrefix(entry.title, ":sparkles:"), strings.HasPrefix(entry.title, "✨"):
374+
entry.section = features
375+
entry.title = strings.TrimPrefix(entry.title, ":sparkles:")
376+
entry.title = strings.TrimPrefix(entry.title, "✨")
377+
case strings.HasPrefix(entry.title, ":bug:"), strings.HasPrefix(entry.title, "🐛"):
378+
entry.section = bugs
379+
entry.title = strings.TrimPrefix(entry.title, ":bug:")
380+
entry.title = strings.TrimPrefix(entry.title, "🐛")
381+
case strings.HasPrefix(entry.title, ":book:"), strings.HasPrefix(entry.title, "📖"):
382+
entry.section = documentation
383+
entry.title = strings.TrimPrefix(entry.title, ":book:")
384+
entry.title = strings.TrimPrefix(entry.title, "📖")
385+
if strings.Contains(entry.title, "CAEP") || strings.Contains(entry.title, "proposal") {
386+
entry.section = proposals
387+
}
388+
case strings.HasPrefix(entry.title, ":seedling:"), strings.HasPrefix(entry.title, "🌱"):
389+
entry.section = other
390+
entry.title = strings.TrimPrefix(entry.title, ":seedling:")
391+
entry.title = strings.TrimPrefix(entry.title, "🌱")
392+
case strings.HasPrefix(entry.title, ":warning:"), strings.HasPrefix(entry.title, "⚠️"):
393+
entry.section = warning
394+
entry.title = strings.TrimPrefix(entry.title, ":warning:")
395+
entry.title = strings.TrimPrefix(entry.title, "⚠️")
396+
default:
397+
entry.section = unknown
398+
}
399+
400+
entry.title = strings.TrimSpace(entry.title)
401+
if entry.title == "" {
402+
return entry, nil
403+
}
404+
entry.title = fmt.Sprintf("- %s: %s", prefix, entry.title)
405+
_, _ = fmt.Sscanf(c.merge, "Merge pull request %s from %s", &entry.prNumber, &fork)
406+
entry.title = formatMerge(entry.title, entry.prNumber)
407+
408+
return entry, nil
409+
}

0 commit comments

Comments
 (0)