@@ -19,6 +19,7 @@ import (
1919 "errors"
2020 "fmt"
2121 "html/template"
22+ "sort"
2223 "strings"
2324 "time"
2425
@@ -66,25 +67,40 @@ var (
6667 "shortSHA" : shortSHA ,
6768 }).Parse (`Librarian Version: {{.LibrarianVersion}}
6869Language Image: {{.ImageVersion}}
69-
70+ {{ $prInfo := . }}
7071{{- range .NoteSections -}}
71- {{ $noteSection := . }}
7272<details><summary>{{.LibraryID}}: {{.NewVersion}}</summary>
7373
74- ## [{{.NewVersion}}]({{"https://github.com/"}}{{.RepoOwner}}/{{.RepoName}}/compare/{{.PreviousTag}}...{{.NewTag}}) ({{.Date}})
74+ ## [{{.NewVersion}}]({{"https://github.com/"}}{{$prInfo .RepoOwner}}/{{$prInfo .RepoName}}/compare/{{.PreviousTag}}...{{.NewTag}}) ({{$prInfo .Date}})
7575{{ range .CommitSections }}
7676### {{.Heading}}
7777{{ range .Commits }}
78+ {{ if not .IsBulkCommit -}}
7879{{ if .PiperCLNumber -}}
79- * {{.Subject}} (PiperOrigin-RevId: {{.PiperCLNumber}}) ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$noteSection .RepoOwner}}/{{$noteSection .RepoName}}/commit/{{shortSHA .CommitHash}}))
80+ * {{.Subject}} (PiperOrigin-RevId: {{.PiperCLNumber}}) ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$prInfo .RepoOwner}}/{{$prInfo .RepoName}}/commit/{{shortSHA .CommitHash}}))
8081{{- else -}}
81- * {{.Subject}} ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$noteSection.RepoOwner}}/{{$noteSection.RepoName}}/commit/{{shortSHA .CommitHash}}))
82+ * {{.Subject}} ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$prInfo.RepoOwner}}/{{$prInfo.RepoName}}/commit/{{shortSHA .CommitHash}}))
83+ {{- end }}
8284{{- end }}
8385{{ end }}
8486
8587{{- end }}
8688</details>
8789
90+
91+ {{ end }}
92+ {{- if .BulkChanges -}}
93+ <details><summary>Bulk Changes</summary>
94+ {{ range .BulkChanges }}
95+ {{ if .PiperCLNumber -}}
96+ * {{.Type}}: {{.Subject}} (PiperOrigin-RevId: {{.PiperCLNumber}}) ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$prInfo.RepoOwner}}/{{$prInfo.RepoName}}/commit/{{shortSHA .CommitHash}}))
97+ Libraries: {{.LibraryIDs}}
98+ {{- else -}}
99+ * {{.Type}}: {{.Subject}} ([{{shortSHA .CommitHash}}]({{"https://github.com/"}}{{$prInfo.RepoOwner}}/{{$prInfo.RepoName}}/commit/{{shortSHA .CommitHash}}))
100+ Libraries: {{.LibraryIDs}}
101+ {{- end }}
102+ {{- end }}
103+ </details>
88104{{ end }}
89105` ))
90106
@@ -130,20 +146,21 @@ Language Image: {{.ImageVersion}}
130146` ))
131147)
132148
133- type releaseNote struct {
149+ type releasePRBody struct {
134150 LibrarianVersion string
135151 ImageVersion string
152+ RepoOwner string
153+ RepoName string
154+ Date string
136155 NoteSections []* releaseNoteSection
156+ BulkChanges []* config.Commit
137157}
138158
139159type releaseNoteSection struct {
140- RepoOwner string
141- RepoName string
142160 LibraryID string
143161 PreviousTag string
144162 NewTag string
145163 NewVersion string
146- Date string
147164 CommitSections []* commitSection
148165}
149166
@@ -156,19 +173,39 @@ type commitSection struct {
156173func formatReleaseNotes (state * config.LibrarianState , ghRepo * github.Repository ) (string , error ) {
157174 librarianVersion := cli .Version ()
158175 var releaseSections []* releaseNoteSection
176+ // create a map to deduplicate bulk changes based on their commit hash
177+ // and subject
178+ bulkChangesMap := make (map [string ]* config.Commit )
159179 for _ , library := range state .Libraries {
160180 if ! library .ReleaseTriggered {
161181 continue
162182 }
163183
164- section := formatLibraryReleaseNotes (library , ghRepo )
184+ for _ , commit := range library .Changes {
185+ if commit .IsBulkCommit () {
186+ bulkChangesMap [commit .CommitHash + commit .Subject ] = commit
187+ }
188+ }
189+
190+ section := formatLibraryReleaseNotes (library )
165191 releaseSections = append (releaseSections , section )
166192 }
193+ var bulkChanges []* config.Commit
194+ for _ , commit := range bulkChangesMap {
195+ bulkChanges = append (bulkChanges , commit )
196+ }
197+ sort .Slice (bulkChanges , func (i , j int ) bool {
198+ return bulkChanges [i ].CommitHash < bulkChanges [j ].CommitHash
199+ })
167200
168- data := & releaseNote {
201+ data := & releasePRBody {
169202 LibrarianVersion : librarianVersion ,
203+ Date : time .Now ().Format ("2006-01-02" ),
204+ RepoOwner : ghRepo .Owner ,
205+ RepoName : ghRepo .Name ,
170206 ImageVersion : state .Image ,
171207 NoteSections : releaseSections ,
208+ BulkChanges : bulkChanges ,
172209 }
173210
174211 var out bytes.Buffer
@@ -181,7 +218,7 @@ func formatReleaseNotes(state *config.LibrarianState, ghRepo *github.Repository)
181218
182219// formatLibraryReleaseNotes generates release notes in Markdown format for a single library.
183220// It returns the generated release notes and the new version string.
184- func formatLibraryReleaseNotes (library * config.LibraryState , ghRepo * github. Repository ) * releaseNoteSection {
221+ func formatLibraryReleaseNotes (library * config.LibraryState ) * releaseNoteSection {
185222 // The version should already be updated to the next version.
186223 newVersion := library .Version
187224 tagFormat := config .DetermineTagFormat (library .ID , library , nil )
@@ -190,7 +227,9 @@ func formatLibraryReleaseNotes(library *config.LibraryState, ghRepo *github.Repo
190227
191228 commitsByType := make (map [string ][]* config.Commit )
192229 for _ , commit := range library .Changes {
193- commitsByType [commit .Type ] = append (commitsByType [commit .Type ], commit )
230+ if ! commit .IsBulkCommit () {
231+ commitsByType [commit .Type ] = append (commitsByType [commit .Type ], commit )
232+ }
194233 }
195234
196235 var sections []* commitSection
@@ -207,13 +246,10 @@ func formatLibraryReleaseNotes(library *config.LibraryState, ghRepo *github.Repo
207246 }
208247
209248 section := & releaseNoteSection {
210- RepoOwner : ghRepo .Owner ,
211- RepoName : ghRepo .Name ,
212249 LibraryID : library .ID ,
213250 NewVersion : newVersion ,
214251 PreviousTag : previousTag ,
215252 NewTag : newTag ,
216- Date : time .Now ().Format ("2006-01-02" ),
217253 CommitSections : sections ,
218254 }
219255
0 commit comments