Skip to content

Commit c3c71af

Browse files
authored
feat: generate_blocked field in per-library configuration (#2221)
For #897. This change implements the "generate" command. When library config has generate_blocked field set to true, the generate command skips the library processing. If the library is explicitly specified in the argument via "--library", then this Librarian CLI processes the library.
1 parent 5d05481 commit c3c71af

File tree

4 files changed

+108
-4
lines changed

4 files changed

+108
-4
lines changed

doc/config-schema.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Each object in the `libraries` list represents a single library and has the foll
2929
|-------------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------|
3030
| `id` | string | A unique identifier for the library, in a language-specific format. It should not be empty and only contains alphanumeric characters, slashes, periods, underscores, and hyphens. | Yes | Must be a valid library ID. |
3131
| `next_version` | string | The next released version of the library. Ignored unless it would increase the release version. | No | Must be a valid semantic version, "v" prefix is optional. |
32+
| `generate_blocked` | bool | Set this to `true` to skip the generation of this library. It's `false` by default. | No | |
3233

3334
## Example
3435

@@ -54,4 +55,5 @@ global_files_allowlist:
5455
libraries:
5556
- id: "example-library"
5657
next_version: "2.3.4"
58+
generate_blocked: false
5759
```

internal/config/librarian_config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ type LibrarianConfig struct {
3232

3333
// LibraryConfig defines configuration for a single library, identified by its ID.
3434
type LibraryConfig struct {
35-
LibraryID string `yaml:"id"`
36-
NextVersion string `yaml:"next_version"`
35+
LibraryID string `yaml:"id"`
36+
NextVersion string `yaml:"next_version"`
37+
GenerateBlocked bool `yaml:"generate_blocked"`
3738
}
3839

3940
// GlobalFile defines the global files in language repositories.

internal/librarian/generate.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type generateRunner struct {
4646
repo gitrepo.Repository
4747
sourceRepo gitrepo.Repository
4848
state *config.LibrarianState
49+
librarianConfig *config.LibrarianConfig
4950
workRoot string
5051
}
5152

@@ -69,6 +70,7 @@ func newGenerateRunner(cfg *config.Config) (*generateRunner, error) {
6970
repo: runner.repo,
7071
sourceRepo: runner.sourceRepo,
7172
state: runner.state,
73+
librarianConfig: runner.librarianConfig,
7274
workRoot: runner.workRoot,
7375
}, nil
7476
}
@@ -101,7 +103,16 @@ func (r *generateRunner) run(ctx context.Context) error {
101103
} else {
102104
succeededGenerations := 0
103105
failedGenerations := 0
106+
blockedGenerations := 0
104107
for _, library := range r.state.Libraries {
108+
if r.librarianConfig != nil {
109+
libConfig := r.librarianConfig.LibraryConfigFor(library.ID)
110+
if libConfig != nil && libConfig.GenerateBlocked {
111+
slog.Info("library has generate_blocked, skipping", "id", library.ID)
112+
blockedGenerations++
113+
continue
114+
}
115+
}
105116
oldCommit, err := r.generateSingleLibrary(ctx, library.ID, outputDir)
106117
if err != nil {
107118
slog.Error("failed to generate library", "id", library.ID, "err", err)
@@ -119,9 +130,11 @@ func (r *generateRunner) run(ctx context.Context) error {
119130
"generation statistics",
120131
"all", len(r.state.Libraries),
121132
"successes", succeededGenerations,
133+
"blocked", blockedGenerations,
122134
"failures", failedGenerations)
123-
if failedGenerations > 0 && failedGenerations == len(r.state.Libraries) {
124-
return fmt.Errorf("all %d libraries failed to generate", failedGenerations)
135+
if failedGenerations > 0 && failedGenerations+blockedGenerations == len(r.state.Libraries) {
136+
return fmt.Errorf("all %d libraries failed to generate (blocked: %d)",
137+
failedGenerations, blockedGenerations)
125138
}
126139
}
127140

internal/librarian/generate_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ func TestGenerateScenarios(t *testing.T) {
551551
api string
552552
library string
553553
state *config.LibrarianState
554+
librarianConfig *config.LibrarianConfig
554555
container *mockContainerClient
555556
ghClient GitHubClient
556557
build bool
@@ -830,6 +831,92 @@ func TestGenerateScenarios(t *testing.T) {
830831
wantGenerateCalls: 2,
831832
wantBuildCalls: 1,
832833
},
834+
{
835+
name: "generate skips blocked libraries",
836+
state: &config.LibrarianState{
837+
Image: "gcr.io/test/image:v1.2.3",
838+
Libraries: []*config.LibraryState{
839+
{
840+
ID: "google.cloud.texttospeech.v1",
841+
APIs: []*config.API{{Path: "google/cloud/texttospeech/v1"}},
842+
},
843+
{
844+
ID: "google.cloud.vision.v1",
845+
APIs: []*config.API{{Path: "google/cloud/vision/v1"}},
846+
},
847+
},
848+
},
849+
librarianConfig: &config.LibrarianConfig{
850+
Libraries: []*config.LibraryConfig{
851+
{LibraryID: "google.cloud.texttospeech.v1"},
852+
{LibraryID: "google.cloud.vision.v1", GenerateBlocked: true},
853+
},
854+
},
855+
container: &mockContainerClient{
856+
wantLibraryGen: true,
857+
},
858+
ghClient: &mockGitHubClient{},
859+
build: true,
860+
wantGenerateCalls: 1,
861+
wantBuildCalls: 1,
862+
},
863+
{
864+
name: "generate runs blocked libraries if explicitly requested",
865+
library: "google.cloud.vision.v1",
866+
state: &config.LibrarianState{
867+
Image: "gcr.io/test/image:v1.2.3",
868+
Libraries: []*config.LibraryState{
869+
{
870+
ID: "google.cloud.texttospeech.v1",
871+
APIs: []*config.API{{Path: "google/cloud/texttospeech/v1"}},
872+
},
873+
{
874+
ID: "google.cloud.vision.v1",
875+
APIs: []*config.API{{Path: "google/cloud/vision/v1"}},
876+
},
877+
},
878+
},
879+
librarianConfig: &config.LibrarianConfig{
880+
Libraries: []*config.LibraryConfig{
881+
{LibraryID: "google.cloud.texttospecech.v1"},
882+
{LibraryID: "google.cloud.vision.v1", GenerateBlocked: true},
883+
},
884+
},
885+
container: &mockContainerClient{
886+
wantLibraryGen: true,
887+
},
888+
ghClient: &mockGitHubClient{},
889+
build: true,
890+
wantGenerateCalls: 1,
891+
wantBuildCalls: 1,
892+
},
893+
{
894+
name: "generate skips a blocked library and the rest fail. should report error",
895+
state: &config.LibrarianState{
896+
Image: "gcr.io/test/image:v1.2.3",
897+
Libraries: []*config.LibraryState{
898+
{
899+
ID: "google.cloud.texttospeech.v1",
900+
APIs: []*config.API{{Path: "google/cloud/texttospeech/v1"}},
901+
},
902+
{
903+
ID: "google.cloud.vision.v1",
904+
APIs: []*config.API{{Path: "google/cloud/vision/v1"}},
905+
},
906+
},
907+
},
908+
librarianConfig: &config.LibrarianConfig{
909+
Libraries: []*config.LibraryConfig{
910+
{LibraryID: "google.cloud.texttospeech.v1"},
911+
{LibraryID: "google.cloud.vision.v1", GenerateBlocked: true},
912+
},
913+
},
914+
container: &mockContainerClient{generateErr: errors.New("generate error")},
915+
ghClient: &mockGitHubClient{},
916+
build: true,
917+
wantErr: true,
918+
wantErrMsg: "all 1 libraries failed to generate (blocked: 1)",
919+
},
833920
{
834921
name: "generate all, all fail should report error",
835922
state: &config.LibrarianState{
@@ -885,6 +972,7 @@ func TestGenerateScenarios(t *testing.T) {
885972
repo: repo,
886973
sourceRepo: newTestGitRepo(t),
887974
state: test.state,
975+
librarianConfig: test.librarianConfig,
888976
containerClient: test.container,
889977
ghClient: test.ghClient,
890978
workRoot: t.TempDir(),

0 commit comments

Comments
 (0)