@@ -31,6 +31,7 @@ import (
31
31
"regexp"
32
32
"sort"
33
33
"strings"
34
+ "sync"
34
35
"time"
35
36
)
36
37
64
65
65
66
fromTag = flag .String ("from" , "" , "The tag or commit to start from." )
66
67
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." )
69
71
70
72
tagRegex = regexp .MustCompile (`^\[release-[\w-\.]*\]` )
71
73
)
@@ -129,7 +131,7 @@ func getAreaLabel(merge string) (string, error) {
129
131
130
132
out , err := cmd .CombinedOutput ()
131
133
if err != nil {
132
- return "" , err
134
+ return "" , fmt . Errorf ( "%s: %v" , string ( out ), err )
133
135
}
134
136
135
137
pr := & githubPullRequest {}
@@ -223,53 +225,49 @@ func run() int {
223
225
}
224
226
}
225
227
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
249
239
}
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 )
260
260
}
261
261
262
- body = strings .TrimSpace (body )
263
- if body == "" {
262
+ if result .prEntry .title == "" {
264
263
continue
265
264
}
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 )
271
270
}
272
- merges [key ] = append (merges [key ], formatMerge (body , prNumber ))
273
271
}
274
272
275
273
// TODO Turn this into a link (requires knowing the project name + organization)
@@ -346,3 +344,66 @@ func commandExists(cmd string) bool {
346
344
_ , err := exec .LookPath (cmd )
347
345
return err == nil
348
346
}
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