Skip to content

Commit 3336fa5

Browse files
lqiu96JoeWang1127
andauthored
fix(librarian): Filter for commits associated to a library (#2295)
Fixes: #2254 Changes: - When parsing convention commits, the library association takes into consideration the the commit description and tries to associate it with the libraryID in the brackets (e.g. [LIBRARY-ID]). It is possible that the brackets don't exist and in those cases, it will fall back to the libraryID passed in (this will be based on the library's source root and finds if the file modified is inside those source roots). - Release Init will only update the `Changes` field in the library state with the commits that are relevant. For example, if a nested commit has 6 nested commits for video and 4 for eventarc, then the changes will be split accordingly. --------- Signed-off-by: Lawrence Qiu <[email protected]> Co-authored-by: Joe Wang <[email protected]>
1 parent 654b8f3 commit 3336fa5

File tree

6 files changed

+236
-63
lines changed

6 files changed

+236
-63
lines changed

e2e_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -489,23 +489,23 @@ This pull request is generated with proto changes between
489489
490490
BEGIN_COMMIT_OVERRIDE
491491
BEGIN_NESTED_COMMIT
492-
feat: [texttospeech] Support promptable voices by specifying a model name and a prompt
493-
feat: [texttospeech] Add enum value M4A to enum AudioEncoding
494-
docs: [texttospeech] A comment for method 'StreamingSynthesize' in service 'TextToSpeech' is changed
495-
docs: [texttospeech] A comment for enum value 'AUDIO_ENCODING_UNSPECIFIED' in enum 'AudioEncoding' is changed
496-
docs: [texttospeech] A comment for enum value 'OGG_OPUS' in enum 'AudioEncoding' is changed
497-
docs: [texttospeech] A comment for enum value 'PCM' in enum 'AudioEncoding' is changed
498-
docs: [texttospeech] A comment for field 'low_latency_journey_synthesis' in message '.google.cloud.texttospeech.v1beta1.AdvancedVoiceOptions' is changed
499-
docs: [texttospeech] A comment for enum value 'PHONETIC_ENCODING_IPA' in enum 'PhoneticEncoding' is changed
500-
docs: [texttospeech] A comment for enum value 'PHONETIC_ENCODING_X_SAMPA' in enum 'PhoneticEncoding' is changed
501-
docs: [texttospeech] A comment for field 'phrase' in message '.google.cloud.texttospeech.v1beta1.CustomPronunciationParams' is changed
502-
docs: [texttospeech] A comment for field 'pronunciations' in message '.google.cloud.texttospeech.v1beta1.CustomPronunciations' is changed
503-
docs: [texttospeech] A comment for message 'MultiSpeakerMarkup' is changed
504-
docs: [texttospeech] A comment for field 'custom_pronunciations' in message '.google.cloud.texttospeech.v1beta1.SynthesisInput' is changed
505-
docs: [texttospeech] A comment for field 'voice_clone' in message '.google.cloud.texttospeech.v1beta1.VoiceSelectionParams' is changed
506-
docs: [texttospeech] A comment for field 'speaking_rate' in message '.google.cloud.texttospeech.v1beta1.AudioConfig' is changed
507-
docs: [texttospeech] A comment for field 'audio_encoding' in message '.google.cloud.texttospeech.v1beta1.StreamingAudioConfig' is changed
508-
docs: [texttospeech] A comment for field 'text' in message '.google.cloud.texttospeech.v1beta1.StreamingSynthesisInput' is changed
492+
feat: [go-google-cloud-pubsub-v1] Support promptable voices by specifying a model name and a prompt
493+
feat: [go-google-cloud-pubsub-v1] Add enum value M4A to enum AudioEncoding
494+
docs: [go-google-cloud-pubsub-v1] A comment for method 'StreamingSynthesize' in service 'TextToSpeech' is changed
495+
docs: [go-google-cloud-pubsub-v1] A comment for enum value 'AUDIO_ENCODING_UNSPECIFIED' in enum 'AudioEncoding' is changed
496+
docs: [go-google-cloud-pubsub-v1] A comment for enum value 'OGG_OPUS' in enum 'AudioEncoding' is changed
497+
docs: [go-google-cloud-pubsub-v1] A comment for enum value 'PCM' in enum 'AudioEncoding' is changed
498+
docs: [go-google-cloud-pubsub-v1] A comment for field 'low_latency_journey_synthesis' in message '.google.cloud.texttospeech.v1beta1.AdvancedVoiceOptions' is changed
499+
docs: [go-google-cloud-pubsub-v1] A comment for enum value 'PHONETIC_ENCODING_IPA' in enum 'PhoneticEncoding' is changed
500+
docs: [go-google-cloud-pubsub-v1] A comment for enum value 'PHONETIC_ENCODING_X_SAMPA' in enum 'PhoneticEncoding' is changed
501+
docs: [go-google-cloud-pubsub-v1] A comment for field 'phrase' in message '.google.cloud.texttospeech.v1beta1.CustomPronunciationParams' is changed
502+
docs: [go-google-cloud-pubsub-v1] A comment for field 'pronunciations' in message '.google.cloud.texttospeech.v1beta1.CustomPronunciations' is changed
503+
docs: [go-google-cloud-pubsub-v1] A comment for message 'MultiSpeakerMarkup' is changed
504+
docs: [go-google-cloud-pubsub-v1] A comment for field 'custom_pronunciations' in message '.google.cloud.texttospeech.v1beta1.SynthesisInput' is changed
505+
docs: [go-google-cloud-pubsub-v1] A comment for field 'voice_clone' in message '.google.cloud.texttospeech.v1beta1.VoiceSelectionParams' is changed
506+
docs: [go-google-cloud-pubsub-v1] A comment for field 'speaking_rate' in message '.google.cloud.texttospeech.v1beta1.AudioConfig' is changed
507+
docs: [go-google-cloud-pubsub-v1] A comment for field 'audio_encoding' in message '.google.cloud.texttospeech.v1beta1.StreamingAudioConfig' is changed
508+
docs: [go-google-cloud-pubsub-v1] A comment for field 'text' in message '.google.cloud.texttospeech.v1beta1.StreamingSynthesisInput' is changed
509509
510510
PiperOrigin-RevId: 799242210
511511

internal/conventionalcommits/conventional_commits.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ var (
4242
// e.g., "Reviewed-by: G. Gemini" or "BREAKING CHANGE: an API was changed".
4343
footerRegex = regexp.MustCompile(`^([A-Za-z-]+|` + breakingChangeKey + `):\s(.*)`)
4444
sourceLinkRegex = regexp.MustCompile(`^\[googleapis\/googleapis@(?P<shortSHA>.*)\]\(https:\/\/github\.com\/googleapis\/googleapis\/commit\/(?P<sha>.*)\)$`)
45+
// libraryIDRegex extracts the libraryID from the commit message in a generation PR.
46+
// For a generation PR, each commit is expected to have the libraryID in brackets
47+
// ('[]').
48+
libraryIDRegex = regexp.MustCompile(`\[([^\]]+)\]`)
4549
)
4650

4751
// ConventionalCommit represents a parsed conventional commit message.
@@ -80,6 +84,17 @@ type parsedHeader struct {
8084
IsBreaking bool
8185
}
8286

87+
// extractLibraryID pulls the text between '[]' as the commit's libraryID
88+
// A non generation PR may not have the associated libraryID between [] and
89+
// will return an empty libraryID.
90+
func (header *parsedHeader) extractLibraryID() string {
91+
matches := libraryIDRegex.FindStringSubmatch(header.Description)
92+
if len(matches) == 0 {
93+
return ""
94+
}
95+
return matches[1]
96+
}
97+
8398
// commitPart holds the raw string of a commit message and whether it's nested.
8499
type commitPart struct {
85100
message string
@@ -212,6 +227,13 @@ func parseSimpleCommit(commitPart commitPart, commit *gitrepo.Commit, libraryID
212227
}
213228

214229
subjects = append(subjects, []string{})
230+
// If there is an association for the commit (i.e. the commit has '[LIBRARY_ID]' in the
231+
// description), then use that libraryID. Otherwise, use the libraryID passed as the default.
232+
headerLibraryID := header.extractLibraryID()
233+
if headerLibraryID != "" {
234+
libraryID = headerLibraryID
235+
}
236+
215237
commits = append(commits, &ConventionalCommit{
216238
Type: header.Type,
217239
Scope: header.Scope,

internal/conventionalcommits/conventional_commits_test.go

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestParseCommits(t *testing.T) {
3535
wantErrPhrase string
3636
}{
3737
{
38-
name: "simple feat",
38+
name: "simple_commit_with_no_library_association",
3939
message: "feat: add new feature",
4040
want: []*ConventionalCommit{
4141
{
@@ -51,7 +51,7 @@ func TestParseCommits(t *testing.T) {
5151
},
5252
},
5353
{
54-
name: "feat with scope",
54+
name: "simple_commit_with_scope",
5555
message: "feat(scope): add new feature",
5656
want: []*ConventionalCommit{
5757
{
@@ -68,7 +68,7 @@ func TestParseCommits(t *testing.T) {
6868
},
6969
},
7070
{
71-
name: "feat with breaking change",
71+
name: "simple_commit_with_breaking_change",
7272
message: "feat!: add new feature",
7373
want: []*ConventionalCommit{
7474
{
@@ -85,7 +85,7 @@ func TestParseCommits(t *testing.T) {
8585
},
8686
},
8787
{
88-
name: "feat with single footer",
88+
name: "commit_with_single_footer",
8989
message: "feat: add new feature\n\nCo-authored-by: John Doe <[email protected]>",
9090
want: []*ConventionalCommit{
9191
{
@@ -101,7 +101,7 @@ func TestParseCommits(t *testing.T) {
101101
},
102102
},
103103
{
104-
name: "feat with multiple footers",
104+
name: "commit_with_multiple_footers",
105105
message: "feat: add new feature\n\nCo-authored-by: John Doe <[email protected]>\nReviewed-by: Jane Smith <[email protected]>",
106106
want: []*ConventionalCommit{
107107
{
@@ -120,14 +120,14 @@ func TestParseCommits(t *testing.T) {
120120
},
121121
},
122122
{
123-
name: "feat with multiple footers for generated changes",
123+
name: "commit_with_multiple_footers_for_generated_changes",
124124
message: "feat: [library-name] add new feature\nThis is the body.\n...\n\nPiperOrigin-RevId: piper_cl_number\n\nSource-Link: [googleapis/googleapis@{source_commit_hash}](https://github.com/googleapis/googleapis/commit/abcdefg1234567)",
125125
want: []*ConventionalCommit{
126126
{
127127
Type: "feat",
128128
Subject: "[library-name] add new feature",
129129
Body: "This is the body.\n...",
130-
LibraryID: "example-id",
130+
LibraryID: "library-name",
131131
IsNested: false,
132132
IsBreaking: false,
133133
Footers: map[string]string{
@@ -141,7 +141,7 @@ func TestParseCommits(t *testing.T) {
141141
},
142142
},
143143
{
144-
name: "feat with breaking change footer",
144+
name: "commit_with_breaking_change_footer",
145145
message: "feat: add new feature\n\nBREAKING CHANGE: this is a breaking change",
146146
want: []*ConventionalCommit{
147147
{
@@ -159,7 +159,7 @@ func TestParseCommits(t *testing.T) {
159159
},
160160
},
161161
{
162-
name: "feat with wrong breaking change footer",
162+
name: "commit_with_wrong_breaking_change_footer",
163163
message: "feat: add new feature\n\nBreaking change: this is a breaking change",
164164
want: []*ConventionalCommit{
165165
{
@@ -177,7 +177,7 @@ func TestParseCommits(t *testing.T) {
177177
},
178178
},
179179
{
180-
name: "feat with body and footers",
180+
name: "commit_with_body_and_footers",
181181
message: "feat: add new feature\n\nThis is the body of the commit message.\nIt can span multiple lines.\n\nCo-authored-by: John Doe <[email protected]>",
182182
want: []*ConventionalCommit{
183183
{
@@ -194,7 +194,7 @@ func TestParseCommits(t *testing.T) {
194194
},
195195
},
196196
{
197-
name: "feat with multi-line footer",
197+
name: "commit_with_multi-line_footer",
198198
message: "feat: add new feature\n\nThis is the body.\n\nBREAKING CHANGE: this is a breaking change\nthat spans multiple lines.",
199199
want: []*ConventionalCommit{
200200
{
@@ -212,7 +212,7 @@ func TestParseCommits(t *testing.T) {
212212
},
213213
},
214214
{
215-
name: "commit override",
215+
name: "commit_override",
216216
message: `feat: original message
217217
218218
BEGIN_COMMIT_OVERRIDE
@@ -238,13 +238,13 @@ END_COMMIT_OVERRIDE`,
238238
},
239239
},
240240
{
241-
name: "invalid conventional commit",
241+
name: "invalid_conventional_commit",
242242
message: "this is not a conventional commit",
243243
wantErr: false,
244244
want: nil,
245245
},
246246
{
247-
name: "empty commit message",
247+
name: "empty_commit_message",
248248
message: "",
249249
wantErr: true,
250250
wantErrPhrase: "empty commit",
@@ -303,7 +303,7 @@ END_NESTED_COMMIT
303303
},
304304
},
305305
{
306-
name: "commit with empty nested commit",
306+
name: "commit_with_empty_nested_commit",
307307
message: `feat(parser): main feature
308308
main commit body
309309
@@ -326,7 +326,7 @@ END_NESTED_COMMIT
326326
},
327327
},
328328
{
329-
name: "commit override with nested commits",
329+
name: "commit_override_with_nested_commits",
330330
message: `feat: API regeneration main commit
331331
332332
This pull request is generated with proto changes between
@@ -361,10 +361,10 @@ END_COMMIT_OVERRIDE
361361
Type: "feat",
362362
Subject: "[abc] nested commit 1",
363363
Body: "body of nested commit 1\n...",
364-
LibraryID: "example-id",
364+
LibraryID: "abc",
365365
IsNested: true,
366366
Footers: map[string]string{"PiperOrigin-RevId": "123456", "Source-Link": "fake-link"},
367-
SHA: sha.String(),
367+
SHA: sha.String(), // For each nested commit, the SHA should be the same (points to language repo's commit hash)
368368
CommitHash: sha.String(),
369369
When: now,
370370
},
@@ -373,16 +373,16 @@ END_COMMIT_OVERRIDE
373373
Subject: "[abc] nested commit 2",
374374
IsNested: true,
375375
Body: "body of nested commit 2\n...",
376-
LibraryID: "example-id",
376+
LibraryID: "abc",
377377
Footers: map[string]string{"PiperOrigin-RevId": "654321", "Source-Link": "fake-link"},
378-
SHA: sha.String(),
378+
SHA: sha.String(), // For each nested commit, the SHA should be the same (points to language repo's commit hash)
379379
CommitHash: sha.String(),
380380
When: now,
381381
},
382382
},
383383
},
384384
{
385-
name: "nest commit outside of override ignored",
385+
name: "nest_commit_outside_of_override_ignored",
386386
message: `feat: original message
387387
388388
BEGIN_NESTED_COMMIT
@@ -411,7 +411,7 @@ END_NESTED_COMMIT`,
411411
},
412412
},
413413
{
414-
name: "parse multiple lines message inside nested commit, one line header",
414+
name: "parse_multiple_lines_message_inside_nested_commit,_one_line_header",
415415
message: `
416416
chore: Update generation configuration at Tue Aug 26 02:31:23 UTC 2025 (#11734)
417417
@@ -436,7 +436,7 @@ END_COMMIT_OVERRIDE`,
436436
{
437437
Type: "feat",
438438
Subject: "[texttospeech] Support promptable voices by specifying a model name and a prompt",
439-
LibraryID: "example-id",
439+
LibraryID: "texttospeech",
440440
IsNested: true,
441441
Footers: map[string]string{
442442
"PiperOrigin-RevId": "799242210",
@@ -449,7 +449,7 @@ END_COMMIT_OVERRIDE`,
449449
{
450450
Type: "feat",
451451
Subject: "[texttospeech] Add enum value M4A to enum AudioEncoding",
452-
LibraryID: "example-id",
452+
LibraryID: "texttospeech",
453453
IsNested: true,
454454
Footers: map[string]string{
455455
"PiperOrigin-RevId": "799242210",
@@ -462,7 +462,7 @@ END_COMMIT_OVERRIDE`,
462462
{
463463
Type: "docs",
464464
Subject: "[texttospeech] A comment for method 'StreamingSynthesize' in service 'TextToSpeech' is changed",
465-
LibraryID: "example-id",
465+
LibraryID: "texttospeech",
466466
IsNested: true,
467467
Footers: map[string]string{
468468
"PiperOrigin-RevId": "799242210",
@@ -499,7 +499,7 @@ END_COMMIT_OVERRIDE`,
499499
{
500500
Type: "feat",
501501
Subject: "[texttospeech] Support promptable voices by specifying a model name and a prompt",
502-
LibraryID: "example-id",
502+
LibraryID: "texttospeech",
503503
IsNested: true,
504504
Footers: map[string]string{},
505505
SHA: sha.String(),
@@ -509,7 +509,7 @@ END_COMMIT_OVERRIDE`,
509509
{
510510
Type: "feat",
511511
Subject: "[texttospeech] Add enum value M4A to enum AudioEncoding",
512-
LibraryID: "example-id",
512+
LibraryID: "texttospeech",
513513
IsNested: true,
514514
Footers: map[string]string{},
515515
SHA: sha.String(),
@@ -519,7 +519,54 @@ END_COMMIT_OVERRIDE`,
519519
{
520520
Type: "docs",
521521
Subject: "[texttospeech] A comment for method 'StreamingSynthesize' in service 'TextToSpeech' is changed",
522-
LibraryID: "example-id",
522+
LibraryID: "texttospeech",
523+
IsNested: true,
524+
Footers: map[string]string{},
525+
SHA: sha.String(),
526+
CommitHash: sha.String(),
527+
When: now,
528+
},
529+
},
530+
},
531+
{
532+
name: "generation_commit_has_incorrect_libraryID_passed_in",
533+
message: `
534+
chore: librarian generate pull request: 20250919T072957Z (#14501)
535+
536+
This pull request is generated with proto changes between
537+
538+
[googleapis/googleapis@f8776fe](googleapis/googleapis@f8776fe)
539+
(exclusive) and
540+
541+
[googleapis/googleapis@36533b0](googleapis/googleapis@36533b0)
542+
(inclusive).
543+
544+
BEGIN_COMMIT_OVERRIDE
545+
BEGIN_NESTED_COMMIT
546+
docs: [google-cloud-video-live-stream] Update requirements of resource ID fields to be more clear
547+
548+
END_NESTED_COMMIT
549+
550+
BEGIN_NESTED_COMMIT
551+
feat: [google-cloud-eventarc] add new fields to Eventarc resources
552+
553+
END_NESTED_COMMIT
554+
END_COMMIT_OVERRIDE`,
555+
want: []*ConventionalCommit{
556+
{
557+
Type: "docs",
558+
Subject: "[google-cloud-video-live-stream] Update requirements of resource ID fields to be more clear",
559+
LibraryID: "google-cloud-video-live-stream",
560+
IsNested: true,
561+
Footers: map[string]string{},
562+
SHA: sha.String(),
563+
CommitHash: sha.String(),
564+
When: now,
565+
},
566+
{
567+
Type: "feat",
568+
Subject: "[google-cloud-eventarc] add new fields to Eventarc resources",
569+
LibraryID: "google-cloud-eventarc",
523570
IsNested: true,
524571
Footers: map[string]string{},
525572
SHA: sha.String(),

internal/librarian/release_init.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,22 @@ func (r *initRunner) processLibrary(library *config.LibraryState) error {
217217
if err != nil {
218218
return fmt.Errorf("failed to fetch conventional commits for library, %s: %w", library.ID, err)
219219
}
220+
// Filter specifically for commits relevant to a library
221+
commits = filterCommitsByLibraryID(commits, library.ID)
220222
return r.updateLibrary(library, commits)
221223
}
222224

225+
// filterCommitsByLibraryID keeps the conventional commits that match a libaryID.
226+
func filterCommitsByLibraryID(commits []*conventionalcommits.ConventionalCommit, libraryID string) []*conventionalcommits.ConventionalCommit {
227+
var filteredCommits []*conventionalcommits.ConventionalCommit
228+
for _, commit := range commits {
229+
if commit.LibraryID == libraryID {
230+
filteredCommits = append(filteredCommits, commit)
231+
}
232+
}
233+
return filteredCommits
234+
}
235+
223236
// updateLibrary updates the library's state with the new release information:
224237
//
225238
// 1. Determines the library version's next version.

0 commit comments

Comments
 (0)