Skip to content

Commit 12fef2b

Browse files
authored
chore: refactor generation result (#2530)
Updates #2378
1 parent f7a6555 commit 12fef2b

File tree

5 files changed

+47
-22
lines changed

5 files changed

+47
-22
lines changed

internal/conventionalcommits/conventional_commits.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package conventionalcommits
1616

1717
import (
1818
"encoding/json"
19+
"errors"
1920
"fmt"
2021
"log/slog"
2122
"regexp"
@@ -41,13 +42,16 @@ var (
4142
// literal. The key is followed by ":" and then the value.
4243
// e.g., "Reviewed-by: G. Gemini" or "BREAKING CHANGE: an API was changed".
4344
footerRegex = regexp.MustCompile(`^([A-Za-z-]+|` + breakingChangeKey + `):(.*)`)
44-
sourceLinkRegex = regexp.MustCompile(`^\[googleapis\/googleapis@(?P<shortSHA>.*)\]\(https:\/\/github\.com\/googleapis\/googleapis\/commit\/(?P<sha>.*)\)$`)
45+
sourceLinkRegex = regexp.MustCompile(`^\[googleapis/googleapis@(?P<shortSHA>.*)]\(https://github\.com/googleapis/googleapis/commit/(?P<sha>.*)\)$`)
4546
// libraryIDRegex extracts the libraryID from the commit message in a generation PR.
4647
// For a generation PR, each commit is expected to have the libraryID in brackets
4748
// ('[]').
48-
libraryIDRegex = regexp.MustCompile(`\[([^\]]+)\]`)
49+
libraryIDRegex = regexp.MustCompile(`\[([^]]+)]`)
4950
)
5051

52+
// ErrEmptyCommitMessage returns when the commit message is empty.
53+
var ErrEmptyCommitMessage = errors.New("empty commit message")
54+
5155
// ConventionalCommit represents a parsed conventional commit message.
5256
// See https://www.conventionalcommits.org/en/v1.0.0/ for details.
5357
type ConventionalCommit struct {
@@ -129,7 +133,7 @@ func (c *ConventionalCommit) MarshalJSON() ([]byte, error) {
129133
func ParseCommits(commit *gitrepo.Commit, libraryID string) ([]*ConventionalCommit, error) {
130134
message := commit.Message
131135
if strings.TrimSpace(message) == "" {
132-
return nil, fmt.Errorf("empty commit message")
136+
return nil, ErrEmptyCommitMessage
133137
}
134138
message = extractCommitMessageOverride(message)
135139

internal/gitrepo/gitrepo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ func (r *LocalRepository) GetCommit(commitHash string) (*Commit, error) {
299299

300300
// GetLatestCommit returns the latest commit of the given path in the repository.
301301
func (r *LocalRepository) GetLatestCommit(path string) (*Commit, error) {
302+
slog.Info("Retrieving the latest commit", "path", path)
302303
opt := &git.LogOptions{
303304
Order: git.LogOrderCommitterTime,
304305
FileName: &path,

internal/librarian/command.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type commitInfo struct {
8888
sourceRepo gitrepo.Repository
8989
state *config.LibrarianState
9090
workRoot string
91+
apiOnboarding bool
9192
failedGenerations int
9293
}
9394

internal/librarian/generate.go

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ type generateRunner struct {
5050
workRoot string
5151
}
5252

53+
// generationStatus represents the result of a single library generation.
54+
type generationStatus struct {
55+
// oldCommit is the SHA of the previously generated version of the library.
56+
oldCommit string
57+
// apiOnboarding is true if an API is being configured and generated for the
58+
// first time.
59+
apiOnboarding bool
60+
}
61+
5362
func newGenerateRunner(cfg *config.Config) (*generateRunner, error) {
5463
runner, err := newCommandRunner(cfg)
5564
if err != nil {
@@ -88,18 +97,20 @@ func (r *generateRunner) run(ctx context.Context) error {
8897
// use this map to keep the mapping from library id to commit sha before the
8998
// generation since we need these commits to create pull request body.
9099
idToCommits := make(map[string]string)
100+
apiOnboarding := false
91101
var failedLibraries []string
92102
failedGenerations := 0
93103
if r.api != "" || r.library != "" {
94104
libraryID := r.library
95105
if libraryID == "" {
96106
libraryID = findLibraryIDByAPIPath(r.state, r.api)
97107
}
98-
oldCommit, err := r.generateSingleLibrary(ctx, libraryID, outputDir)
108+
status, err := r.generateSingleLibrary(ctx, libraryID, outputDir)
99109
if err != nil {
100110
return err
101111
}
102-
idToCommits[libraryID] = oldCommit
112+
idToCommits[libraryID] = status.oldCommit
113+
apiOnboarding = status.apiOnboarding
103114
} else {
104115
succeededGenerations := 0
105116
blockedGenerations := 0
@@ -112,15 +123,15 @@ func (r *generateRunner) run(ctx context.Context) error {
112123
continue
113124
}
114125
}
115-
oldCommit, err := r.generateSingleLibrary(ctx, library.ID, outputDir)
126+
status, err := r.generateSingleLibrary(ctx, library.ID, outputDir)
116127
if err != nil {
117128
slog.Error("failed to generate library", "id", library.ID, "err", err)
118129
failedLibraries = append(failedLibraries, library.ID)
119130
failedGenerations++
120131
} else {
121132
// Only add the mapping if library generation is successful so that
122133
// failed library will not appear in generation PR body.
123-
idToCommits[library.ID] = oldCommit
134+
idToCommits[library.ID] = status.oldCommit
124135
succeededGenerations++
125136
}
126137
}
@@ -154,6 +165,7 @@ func (r *generateRunner) run(ctx context.Context) error {
154165
sourceRepo: r.sourceRepo,
155166
state: r.state,
156167
workRoot: r.workRoot,
168+
apiOnboarding: apiOnboarding,
157169
failedGenerations: failedGenerations,
158170
}
159171

@@ -170,58 +182,65 @@ func (r *generateRunner) run(ctx context.Context) error {
170182
//
171183
// 3. Build the library.
172184
//
173-
// 4. Update the last generated commit.
174-
//
175-
// Returns the last generated commit before the generation and error, if any.
176-
func (r *generateRunner) generateSingleLibrary(ctx context.Context, libraryID, outputDir string) (string, error) {
185+
// 4. Update the last generated commit or initial piper id if the library needs configure.
186+
func (r *generateRunner) generateSingleLibrary(ctx context.Context, libraryID, outputDir string) (*generationStatus, error) {
177187
safeLibraryDirectory := getSafeDirectoryName(libraryID)
188+
apiOnboarding := false
178189
if r.needsConfigure() {
179190
slog.Info("library not configured, start initial configuration", "library", r.library)
180191
configureOutputDir := filepath.Join(outputDir, safeLibraryDirectory, "configure")
181192
if err := os.MkdirAll(configureOutputDir, 0755); err != nil {
182-
return "", err
193+
return nil, err
183194
}
184195
configuredLibraryID, err := r.runConfigureCommand(ctx, configureOutputDir)
185196
if err != nil {
186-
return "", err
197+
return nil, err
187198
}
199+
200+
apiOnboarding = true
188201
libraryID = configuredLibraryID
189202
}
190203

191204
// At this point, we should have a library in the state.
192205
libraryState := findLibraryByID(r.state, libraryID)
193206
if libraryState == nil {
194-
return "", fmt.Errorf("library %q not configured yet, generation stopped", libraryID)
207+
return nil, fmt.Errorf("library %q not configured yet, generation stopped", libraryID)
195208
}
196209
lastGenCommit := libraryState.LastGeneratedCommit
197210

198211
if len(libraryState.APIs) == 0 {
199212
slog.Info("library has no APIs; skipping generation", "library", libraryID)
200-
return "", nil
213+
return &generationStatus{
214+
oldCommit: "",
215+
apiOnboarding: apiOnboarding,
216+
}, nil
201217
}
202218

203219
// For each library, create a separate output directory. This avoids
204220
// libraries interfering with each other, and makes it easier to see what
205221
// was generated for each library when debugging.
206222
libraryOutputDir := filepath.Join(outputDir, safeLibraryDirectory)
207223
if err := os.MkdirAll(libraryOutputDir, 0755); err != nil {
208-
return "", err
224+
return nil, err
209225
}
210226

211227
generatedLibraryID, err := r.runGenerateCommand(ctx, libraryID, libraryOutputDir)
212228
if err != nil {
213-
return "", err
229+
return nil, err
214230
}
215231

216232
if err := r.runBuildCommand(ctx, generatedLibraryID); err != nil {
217-
return "", err
233+
return nil, err
218234
}
219235

220236
if err := r.updateLastGeneratedCommitState(generatedLibraryID); err != nil {
221-
return "", err
237+
return nil, err
222238
}
223239

224-
return lastGenCommit, nil
240+
return &generationStatus{
241+
oldCommit: lastGenCommit,
242+
apiOnboarding: apiOnboarding,
243+
}, nil
225244
}
226245

227246
func (r *generateRunner) needsConfigure() bool {
@@ -451,7 +470,7 @@ func setAllAPIStatus(state *config.LibrarianState, status string) {
451470

452471
// getSafeDirectoryName returns a directory name which doesn't contain slashes
453472
// based on a library ID. This avoids cases where a library ID contains
454-
// slashes but we want generateSingleLibrary to create a directory which
473+
// slashes, but we want generateSingleLibrary to create a directory which
455474
// is not a subdirectory of some other directory. For example, if there
456475
// are library IDs of "pubsub" and "pubsub/v2" we don't want to create
457476
// "output/pubsub/v2" and then "output/pubsub" later. This function does

internal/librarian/generate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ func TestGenerateScenarios(t *testing.T) {
638638
wantConfigureCalls int
639639
}{
640640
{
641-
name: "generate single library including initial configuration",
641+
name: "generate_single_library_including_initial_configuration",
642642
api: "some/api",
643643
library: "some-library",
644644
state: &config.LibrarianState{

0 commit comments

Comments
 (0)