diff --git a/internal/librarian/add.go b/internal/librarian/add.go index 4c2e5d995d9..b09f7aa372a 100644 --- a/internal/librarian/add.go +++ b/internal/librarian/add.go @@ -100,8 +100,8 @@ func runAdd(ctx context.Context, cfg *config.Config, api string) error { if err != nil { return err } - if cfg.Language == config.LanguageGo || cfg.Language == config.LanguagePython { - if hasBulkReleasePleaseConfigs(".") { + if cfg.Language == config.LanguageGo || cfg.Language == config.LanguagePython || cfg.Language == config.LanguageNodejs { + if hasBulkReleasePleaseConfigs(".", cfg) { if err := syncToReleasePlease(".", cfg, name); err != nil { return err } diff --git a/internal/librarian/release_please.go b/internal/librarian/release_please.go index e01f76b9bf4..a921f1fafc5 100644 --- a/internal/librarian/release_please.go +++ b/internal/librarian/release_please.go @@ -31,15 +31,33 @@ import ( const ( bulkManifestFile = ".release-please-bulk-manifest.json" bulkConfigFile = "release-please-bulk-config.json" + defaultManifestFile = ".release-please-manifest.json" + defaultConfigFile = "release-please-config.json" defaultReleasePleaseVersion = "0.0.0" ) -func hasBulkReleasePleaseConfigs(dir string) bool { - _, errM := os.Stat(filepath.Join(dir, bulkManifestFile)) - _, errC := os.Stat(filepath.Join(dir, bulkConfigFile)) +func hasBulkReleasePleaseConfigs(dir string, cfg *config.Config) bool { + manifestFile, configFile := releasePleaseFiles(cfg) + _, errM := os.Stat(filepath.Join(dir, manifestFile)) + _, errC := os.Stat(filepath.Join(dir, configFile)) return !errors.Is(errM, fs.ErrNotExist) && !errors.Is(errC, fs.ErrNotExist) } +// releasePleaseFiles returns the file names for the Release Please manifest file +// and config file in this order, depending on the SDK language. +func releasePleaseFiles(cfg *config.Config) (string, string) { + // google-cloud-node uses the default Release Please files to add a new library. + // google-cloud-python and google-cloud-go use the "-bulk-" files. + manifestFile := bulkManifestFile + configFile := bulkConfigFile + if cfg.Language == config.LanguageNodejs { + // google-cloud-node uses the default files + manifestFile = defaultManifestFile + configFile = defaultConfigFile + } + return manifestFile, configFile +} + // syncToReleasePlease updates the release-please configuration files with the // onboarded library's package name, initial version, and language-specific // extra files to track for release version bumps. @@ -49,19 +67,20 @@ func syncToReleasePlease(dir string, cfg *config.Config, name string) error { return err } - manifestPath := filepath.Join(dir, bulkManifestFile) + manifestFile, configFile := releasePleaseFiles(cfg) + manifestPath := filepath.Join(dir, manifestFile) manifest, err := readJSONFile[map[string]string](manifestPath) if err != nil { - return fmt.Errorf("failed to read bulk manifest file: %w", err) + return fmt.Errorf("failed to read manifest file: %w", err) } if manifest == nil { manifest = make(map[string]string) } - configPath := filepath.Join(dir, bulkConfigFile) + configPath := filepath.Join(dir, configFile) bulkConfig, err := readJSONFile[map[string]any](configPath) if err != nil { - return fmt.Errorf("failed to read bulk config file: %w", err) + return fmt.Errorf("failed to read config file: %w", err) } if bulkConfig == nil { bulkConfig = make(map[string]any) @@ -69,7 +88,8 @@ func syncToReleasePlease(dir string, cfg *config.Config, name string) error { packagesRaw, pkgsExist := bulkConfig["packages"] packages, isMap := packagesRaw.(map[string]any) if pkgsExist && !isMap { - return fmt.Errorf("'packages' in bulk config is not an object: %v", packagesRaw) + return fmt.Errorf("'packages' in %s is not an object: %v", + configPath, packagesRaw) } if !isMap || packages == nil { packages = make(map[string]any) @@ -78,12 +98,22 @@ func syncToReleasePlease(dir string, cfg *config.Config, name string) error { var extraFiles []any pkgPath := lib.Name - if cfg.Language == config.LanguagePython { + switch cfg.Language { + case config.LanguagePython: pkgPath = python.ReleasePleasePkgPrefix + lib.Name extraFiles = python.ReleasePleaseExtraFiles(lib) + case config.LanguageNodejs: + pkgPath = "packages/" + lib.Name } - if err := syncPackageToReleasePlease(manifest, packages, pkgPath, lib.Version, lib.Name, extraFiles); err != nil { + component := lib.Name + if cfg.Language == config.LanguageNodejs { + // google-cloud-node does not need to override + // component value in package. + component = "" + } + + if err := syncPackageToReleasePlease(manifest, packages, pkgPath, lib.Version, component, extraFiles); err != nil { return err } @@ -140,7 +170,11 @@ func syncPackageToReleasePlease(manifest map[string]string, packages map[string] packages[pkgPath] = pkgCfg } - pkgCfg["component"] = component + if component != "" { + // Python and Go set component names for packages in the config file. + // NodeJS does not do this and passes an empty string in the argument. + pkgCfg["component"] = component + } if len(extraFiles) > 0 { var existing []any diff --git a/internal/librarian/release_please_test.go b/internal/librarian/release_please_test.go index 4d999855770..f9529ab9ef2 100644 --- a/internal/librarian/release_please_test.go +++ b/internal/librarian/release_please_test.go @@ -27,30 +27,63 @@ import ( func TestHasBulkReleasePleaseConfigs(t *testing.T) { for _, test := range []struct { name string + language string createConfig bool createManifest bool want bool }{ { - name: "both missing", + name: "both missing (Go)", + language: config.LanguageGo, createConfig: false, createManifest: false, want: false, }, { - name: "config missing", + name: "config missing (Go)", + language: config.LanguageGo, createConfig: false, createManifest: true, want: false, }, { - name: "manifest missing", + name: "manifest missing (Go)", + language: config.LanguageGo, createConfig: true, createManifest: false, want: false, }, { - name: "both exist", + name: "both exist (Go)", + language: config.LanguageGo, + createConfig: true, + createManifest: true, + want: true, + }, + { + name: "both missing (Nodejs)", + language: config.LanguageNodejs, + createConfig: false, + createManifest: false, + want: false, + }, + { + name: "config missing (Nodejs)", + language: config.LanguageNodejs, + createConfig: false, + createManifest: true, + want: false, + }, + { + name: "manifest missing (Nodejs)", + language: config.LanguageNodejs, + createConfig: true, + createManifest: false, + want: false, + }, + { + name: "both exist (Nodejs)", + language: config.LanguageNodejs, createConfig: true, createManifest: true, want: true, @@ -58,19 +91,24 @@ func TestHasBulkReleasePleaseConfigs(t *testing.T) { } { t.Run(test.name, func(t *testing.T) { tmp := t.TempDir() + manifestFile, configFile := releasePleaseFiles( + &config.Config{ + Language: test.language, + }, + ) if test.createConfig { - if err := os.WriteFile(filepath.Join(tmp, "release-please-bulk-config.json"), []byte("{}"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(tmp, configFile), []byte("{}"), 0644); err != nil { t.Fatal(err) } } if test.createManifest { - if err := os.WriteFile(filepath.Join(tmp, ".release-please-bulk-manifest.json"), []byte("{}"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(tmp, manifestFile), []byte("{}"), 0644); err != nil { t.Fatal(err) } } - got := hasBulkReleasePleaseConfigs(tmp) + got := hasBulkReleasePleaseConfigs(tmp, &config.Config{Language: test.language}) if got != test.want { - t.Errorf("hasBulkReleasePleaseConfigs(%s) = %t, want %t", tmp, got, test.want) + t.Errorf("hasBulkReleasePleaseConfigs(%s, %s) = %t, want %t", tmp, test.language, got, test.want) } }) } @@ -101,6 +139,21 @@ func TestSyncToReleasePlease(t *testing.T) { wantManifest: `{"secretmanager":"1.0.0"}`, wantConfig: `{"packages":{"secretmanager":{"component":"secretmanager"}}}`, }, + { + name: "new nodejs library", + language: config.LanguageNodejs, + initialManifest: `{}`, + initialConfig: `{"packages": {}}`, + library: &config.Library{ + Name: "google-cloud-secretmanager", + Version: "1.0.0", + APIs: []*config.API{ + {Path: "google/cloud/secretmanager/v1"}, + }, + }, + wantManifest: `{"packages/google-cloud-secretmanager":"1.0.0"}`, + wantConfig: `{"packages":{"packages/google-cloud-secretmanager":{}}}`, + }, { name: "new python library", @@ -258,8 +311,13 @@ func TestSyncToReleasePlease(t *testing.T) { } { t.Run(test.name, func(t *testing.T) { tmp := t.TempDir() - manifestPath := filepath.Join(tmp, ".release-please-bulk-manifest.json") - configPath := filepath.Join(tmp, "release-please-bulk-config.json") + manifestFile, configFile := releasePleaseFiles( + &config.Config{ + Language: test.language, + }, + ) + manifestPath := filepath.Join(tmp, manifestFile) + configPath := filepath.Join(tmp, configFile) if err := os.WriteFile(manifestPath, []byte(test.initialManifest), 0644); err != nil { t.Fatal(err) }