Skip to content

Commit c5cb5b8

Browse files
authored
chore: create generation PR body (#1859)
Implement `formatGenerationPRBody` to create generation pull request body. Updates #1703
1 parent 3521090 commit c5cb5b8

File tree

9 files changed

+712
-59
lines changed

9 files changed

+712
-59
lines changed

internal/conventionalcommits/conventional_commits.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import (
1919
"log/slog"
2020
"regexp"
2121
"strings"
22+
"time"
23+
24+
"github.com/googleapis/librarian/internal/gitrepo"
2225
)
2326

2427
// ConventionalCommit represents a parsed conventional commit message.
@@ -32,6 +35,8 @@ type ConventionalCommit struct {
3235
Description string
3336
// Body is the long-form description of the change.
3437
Body string
38+
// LibraryID is the library ID the commit associated with.
39+
LibraryID string
3540
// Footers contain metadata (e.g,"BREAKING CHANGE", "Reviewed-by").
3641
Footers map[string]string
3742
// IsBreaking indicates if the commit introduces a breaking change.
@@ -40,6 +45,8 @@ type ConventionalCommit struct {
4045
IsNested bool
4146
// SHA is the full commit hash.
4247
SHA string
48+
// When is the timestamp of the commit.
49+
When time.Time
4350
}
4451

4552
const breakingChangeKey = "BREAKING CHANGE"
@@ -209,7 +216,8 @@ func extractCommitParts(message string) []commitPart {
209216
// Malformed override or nested blocks (e.g., with a missing end marker) are
210217
// ignored. Any commit part that is found but fails to parse as a valid
211218
// conventional commit is logged and skipped.
212-
func ParseCommits(message, hashString string) ([]*ConventionalCommit, error) {
219+
func ParseCommits(commit *gitrepo.Commit, libraryID string) ([]*ConventionalCommit, error) {
220+
message := commit.Message
213221
if strings.TrimSpace(message) == "" {
214222
return nil, fmt.Errorf("empty commit message")
215223
}
@@ -218,7 +226,7 @@ func ParseCommits(message, hashString string) ([]*ConventionalCommit, error) {
218226
var commits []*ConventionalCommit
219227

220228
for _, part := range extractCommitParts(message) {
221-
c, err := parseSimpleCommit(part, hashString)
229+
c, err := parseSimpleCommit(part, commit, libraryID)
222230
if err != nil {
223231
slog.Warn("failed to parse commit part", "commit", part.message, "error", err)
224232
continue
@@ -234,7 +242,7 @@ func ParseCommits(message, hashString string) ([]*ConventionalCommit, error) {
234242

235243
// parseSimpleCommit parses a simple commit message and returns a ConventionalCommit.
236244
// A simple commit message is commit that does not include override or nested commits.
237-
func parseSimpleCommit(commitPart commitPart, hashString string) (*ConventionalCommit, error) {
245+
func parseSimpleCommit(commitPart commitPart, commit *gitrepo.Commit, libraryID string) (*ConventionalCommit, error) {
238246
trimmedMessage := strings.TrimSpace(commitPart.message)
239247
if trimmedMessage == "" {
240248
return nil, fmt.Errorf("empty commit message")
@@ -243,7 +251,7 @@ func parseSimpleCommit(commitPart commitPart, hashString string) (*ConventionalC
243251

244252
header, ok := parseHeader(lines[0])
245253
if !ok {
246-
slog.Warn("Invalid conventional commit message", "message", commitPart.message, "hash", hashString)
254+
slog.Warn("Invalid conventional commit message", "message", commitPart.message, "hash", commit.Hash.String())
247255
return nil, nil
248256
}
249257

@@ -256,9 +264,11 @@ func parseSimpleCommit(commitPart commitPart, hashString string) (*ConventionalC
256264
Scope: header.Scope,
257265
Description: header.Description,
258266
Body: strings.TrimSpace(strings.Join(bodyLines, "\n")),
267+
LibraryID: libraryID,
259268
Footers: footers,
260269
IsBreaking: header.IsBreaking || footerIsBreaking,
261270
IsNested: commitPart.isNested,
262-
SHA: hashString,
271+
SHA: commit.Hash.String(),
272+
When: commit.When,
263273
}, nil
264274
}

internal/conventionalcommits/conventional_commits_test.go

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@ package conventionalcommits
1717
import (
1818
"strings"
1919
"testing"
20+
"time"
21+
22+
"github.com/go-git/go-git/v5/plumbing"
23+
"github.com/googleapis/librarian/internal/gitrepo"
2024

2125
"github.com/google/go-cmp/cmp"
2226
)
2327

2428
func TestParseCommits(t *testing.T) {
29+
now := time.Now()
30+
sha := plumbing.NewHash("fake-sha")
2531
tests := []struct {
2632
name string
2733
message string
@@ -36,9 +42,11 @@ func TestParseCommits(t *testing.T) {
3642
{
3743
Type: "feat",
3844
Description: "add new feature",
45+
LibraryID: "example-id",
3946
IsNested: false,
4047
Footers: make(map[string]string),
41-
SHA: "fake-sha",
48+
SHA: sha.String(),
49+
When: now,
4250
},
4351
},
4452
},
@@ -50,9 +58,11 @@ func TestParseCommits(t *testing.T) {
5058
Type: "feat",
5159
Scope: "scope",
5260
Description: "add new feature",
61+
LibraryID: "example-id",
5362
IsNested: false,
5463
Footers: make(map[string]string),
55-
SHA: "fake-sha",
64+
SHA: sha.String(),
65+
When: now,
5666
},
5767
},
5868
},
@@ -63,10 +73,12 @@ func TestParseCommits(t *testing.T) {
6373
{
6474
Type: "feat",
6575
Description: "add new feature",
76+
LibraryID: "example-id",
6677
IsBreaking: true,
6778
IsNested: false,
6879
Footers: make(map[string]string),
69-
SHA: "fake-sha",
80+
SHA: sha.String(),
81+
When: now,
7082
},
7183
},
7284
},
@@ -77,9 +89,11 @@ func TestParseCommits(t *testing.T) {
7789
{
7890
Type: "feat",
7991
Description: "add new feature",
92+
LibraryID: "example-id",
8093
IsNested: false,
8194
Footers: map[string]string{"Co-authored-by": "John Doe <[email protected]>"},
82-
SHA: "fake-sha",
95+
SHA: sha.String(),
96+
When: now,
8397
},
8498
},
8599
},
@@ -90,12 +104,14 @@ func TestParseCommits(t *testing.T) {
90104
{
91105
Type: "feat",
92106
Description: "add new feature",
107+
LibraryID: "example-id",
93108
IsNested: false,
94109
Footers: map[string]string{
95110
"Co-authored-by": "John Doe <[email protected]>",
96111
"Reviewed-by": "Jane Smith <[email protected]>",
97112
},
98-
SHA: "fake-sha",
113+
SHA: sha.String(),
114+
When: now,
99115
},
100116
},
101117
},
@@ -107,13 +123,15 @@ func TestParseCommits(t *testing.T) {
107123
Type: "feat",
108124
Description: "[library-name] add new feature",
109125
Body: "This is the body.\n...",
126+
LibraryID: "example-id",
110127
IsNested: false,
111128
IsBreaking: false,
112129
Footers: map[string]string{
113130
"PiperOrigin-RevId": "piper_cl_number",
114131
"Source-Link": "[googleapis/googleapis@{source_commit_hash}](https://github.com/googleapis/googleapis/commit/{source_commit_hash})",
115132
},
116-
SHA: "fake-sha",
133+
SHA: sha.String(),
134+
When: now,
117135
},
118136
},
119137
},
@@ -125,10 +143,12 @@ func TestParseCommits(t *testing.T) {
125143
Type: "feat",
126144
Description: "add new feature",
127145
Body: "",
146+
LibraryID: "example-id",
128147
IsNested: false,
129148
IsBreaking: true,
130149
Footers: map[string]string{"BREAKING CHANGE": "this is a breaking change"},
131-
SHA: "fake-sha",
150+
SHA: sha.String(),
151+
When: now,
132152
},
133153
},
134154
},
@@ -140,10 +160,12 @@ func TestParseCommits(t *testing.T) {
140160
Type: "feat",
141161
Description: "add new feature",
142162
Body: "Breaking change: this is a breaking change",
163+
LibraryID: "example-id",
143164
IsNested: false,
144165
IsBreaking: false,
145166
Footers: map[string]string{},
146-
SHA: "fake-sha",
167+
SHA: sha.String(),
168+
When: now,
147169
},
148170
},
149171
},
@@ -155,9 +177,11 @@ func TestParseCommits(t *testing.T) {
155177
Type: "feat",
156178
Description: "add new feature",
157179
Body: "This is the body of the commit message.\nIt can span multiple lines.",
180+
LibraryID: "example-id",
158181
IsNested: false,
159182
Footers: map[string]string{"Co-authored-by": "John Doe <[email protected]>"},
160-
SHA: "fake-sha",
183+
SHA: sha.String(),
184+
When: now,
161185
},
162186
},
163187
},
@@ -169,10 +193,12 @@ func TestParseCommits(t *testing.T) {
169193
Type: "feat",
170194
Description: "add new feature",
171195
Body: "This is the body.",
196+
LibraryID: "example-id",
172197
IsNested: false,
173198
IsBreaking: true,
174199
Footers: map[string]string{"BREAKING CHANGE": "this is a breaking change\nthat spans multiple lines."},
175-
SHA: "fake-sha",
200+
SHA: sha.String(),
201+
When: now,
176202
},
177203
},
178204
},
@@ -193,9 +219,11 @@ END_COMMIT_OVERRIDE`,
193219
Scope: "override",
194220
Description: "this is the override message",
195221
Body: "This is the body of the override.",
222+
LibraryID: "example-id",
196223
IsNested: false,
197224
Footers: map[string]string{"Reviewed-by": "Jane Doe"},
198-
SHA: "fake-sha",
225+
SHA: sha.String(),
226+
When: now,
199227
},
200228
},
201229
},
@@ -231,27 +259,33 @@ END_NESTED_COMMIT
231259
Scope: "parser",
232260
Description: "main feature",
233261
Body: "main commit body",
262+
LibraryID: "example-id",
234263
IsNested: false,
235264
Footers: map[string]string{},
236-
SHA: "fake-sha",
265+
SHA: sha.String(),
266+
When: now,
237267
},
238268
{
239269
Type: "fix",
240270
Scope: "sub",
241271
Description: "fix a bug",
242272
Body: "some details for the fix",
273+
LibraryID: "example-id",
243274
IsNested: true,
244275
Footers: map[string]string{},
245-
SHA: "fake-sha",
276+
SHA: sha.String(),
277+
When: now,
246278
},
247279
{
248280
Type: "chore",
249281
Scope: "deps",
250282
Description: "update deps",
251283
Body: "",
284+
LibraryID: "example-id",
252285
IsNested: true,
253286
Footers: map[string]string{},
254-
SHA: "fake-sha",
287+
SHA: sha.String(),
288+
When: now,
255289
},
256290
},
257291
},
@@ -270,9 +304,11 @@ END_NESTED_COMMIT
270304
Scope: "parser",
271305
Description: "main feature",
272306
Body: "main commit body",
307+
LibraryID: "example-id",
273308
IsNested: false,
274309
Footers: map[string]string{},
275-
SHA: "fake-sha",
310+
SHA: sha.String(),
311+
When: now,
276312
},
277313
},
278314
},
@@ -313,17 +349,21 @@ END_COMMIT_OVERRIDE
313349
Type: "feat",
314350
Description: "[abc] nested commit 1",
315351
Body: "body of nested commit 1\n...",
352+
LibraryID: "example-id",
316353
IsNested: true,
317354
Footers: map[string]string{"PiperOrigin-RevId": "123456", "Source-link": "fake-link"},
318-
SHA: "fake-sha",
355+
SHA: sha.String(),
356+
When: now,
319357
},
320358
{
321359
Type: "feat",
322360
Description: "[abc] nested commit 2",
323361
IsNested: true,
324362
Body: "body of nested commit 2\n...",
363+
LibraryID: "example-id",
325364
Footers: map[string]string{"PiperOrigin-RevId": "654321", "Source-link": "fake-link"},
326-
SHA: "fake-sha",
365+
SHA: sha.String(),
366+
When: now,
327367
},
328368
},
329369
},
@@ -347,16 +387,23 @@ END_NESTED_COMMIT`,
347387
Scope: "override",
348388
Description: "this is the override message",
349389
Body: "This is the body of the override.",
390+
LibraryID: "example-id",
350391
IsNested: false,
351392
Footers: map[string]string{"Reviewed-by": "Jane Doe"},
352-
SHA: "fake-sha",
393+
SHA: sha.String(),
394+
When: now,
353395
},
354396
},
355397
},
356398
}
357399
for _, test := range tests {
358400
t.Run(test.name, func(t *testing.T) {
359-
got, err := ParseCommits(test.message, "fake-sha")
401+
commit := &gitrepo.Commit{
402+
Message: test.message,
403+
Hash: plumbing.NewHash("fake-sha"),
404+
When: now,
405+
}
406+
got, err := ParseCommits(commit, "example-id")
360407
if test.wantErr {
361408
if err == nil {
362409
t.Errorf("%s should return error", test.name)

0 commit comments

Comments
 (0)