in tools/relnotes/relnotes.go [185:247]
func mdPrintChanges(changes []*generic.Changelist, byCategory bool) {
printChange := func(change *generic.Changelist) {
fmt.Printf("- ")
content := change.Subject
note := releaseNote(change)
if note != "" && note != "yes" && note != "y" {
// The release note contains content.
content = note
}
fmt.Printf("%s", content)
if len(change.AssociatedIssues) > 0 {
fmt.Printf(" (")
for i, issue := range change.AssociatedIssues {
if i == 0 {
fmt.Printf("[Issue %d](https://%s)", issue.Number, issue.Link)
} else {
fmt.Printf(", [%d](https://%s)", issue.Number, issue.Link)
}
}
fmt.Printf(")")
}
fmt.Printf(" <!-- CL %d -->\n", change.Number)
}
// Group CLs by category or by first associated issue number.
if byCategory {
pkgMap := map[string][]*generic.Changelist{}
for _, change := range changes {
pkgMap[change.Category()] = append(pkgMap[change.Category()], change)
}
for _, changes := range pkgMap {
for _, change := range changes {
printChange(change)
}
}
} else {
sort.Slice(changes, func(i, j int) bool {
// Sort first by associated issue, then by CL number.
var iIssue, jIssue int // first associated issues
if len(changes[i].AssociatedIssues) > 0 {
iIssue = changes[i].AssociatedIssues[0].Number
}
if len(changes[j].AssociatedIssues) > 0 {
jIssue = changes[j].AssociatedIssues[0].Number
}
if iIssue != 0 && jIssue != 0 {
return iIssue < jIssue // sort CLs with issues first
}
return iIssue != 0 || changes[i].Number < changes[j].Number
})
currentChange := -1
for i, change := range changes {
if len(change.AssociatedIssues) > 0 && change.AssociatedIssues[0].Number != currentChange {
currentChange = change.AssociatedIssues[0].Number
fmt.Printf("CL(s) for issue %d:\n", currentChange)
} else if len(change.AssociatedIssues) == 0 && (i == 0 || len(changes[i-1].AssociatedIssues) > 0) {
fmt.Printf("CL(s) not associated with any issue:\n")
}
printChange(change)
}
}
}