Skip to content

Commit a4e2b81

Browse files
authored
chore(tool/cmd/migrate): preserve existing libraries during ruby migration (#6970)
Existing library configurations from librarian.yaml are preserved during Ruby library migration rather than being overwritten. The `parseExistingLibraries` helper reads existing configurations from `librarian.yaml` if present, and `mergeLibs` retains settings for previously configured libraries while appending newly discovered libraries. For #6632 Fixes #6968
1 parent 46aeac4 commit a4e2b81

2 files changed

Lines changed: 228 additions & 1 deletion

File tree

tool/cmd/migrate/ruby.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,15 @@ func runRubyMigration(ctx context.Context, repoPath string) error {
8181
},
8282
},
8383
}
84+
existingLibs, err := parseExistingLibraries(repoPath)
85+
if err != nil {
86+
return err
87+
}
8488
libs, err := findRubyLibraries(src.Dir, repoPath)
8589
if err != nil {
8690
return err
8791
}
88-
cfg.Libraries = libs
92+
cfg.Libraries = mergeLibs(existingLibs, libs)
8993
// The directory name in Googleapis is present for migration code to look
9094
// up API details. It shouldn't be persisted.
9195
cfg.Sources.Googleapis.Dir = ""
@@ -96,6 +100,21 @@ func runRubyMigration(ctx context.Context, repoPath string) error {
96100
return nil
97101
}
98102

103+
func parseExistingLibraries(repoPath string) ([]*config.Library, error) {
104+
absConfigPath, err := filepath.Abs(filepath.Join(repoPath, config.LibrarianYAML))
105+
if err != nil {
106+
return nil, fmt.Errorf("getting absolute path to %s: %w", config.LibrarianYAML, err)
107+
}
108+
cfg, err := yaml.Read[config.Config](absConfigPath)
109+
if err != nil {
110+
if errors.Is(err, fs.ErrNotExist) {
111+
return nil, nil
112+
}
113+
return nil, err
114+
}
115+
return cfg.Libraries, nil
116+
}
117+
99118
func findRubyLibraries(googleapisPath, repoPath string) ([]*config.Library, error) {
100119
entries, err := os.ReadDir(repoPath)
101120
if err != nil {
@@ -244,3 +263,25 @@ func parseVersionedBuild(googleapisDir, apiPath string) (*VersionedBuild, error)
244263
}
245264
return vb, nil
246265
}
266+
267+
// mergeLibs merges existing libraries with discovered libraries. Existing libraries' configurations
268+
// are preserved and discovered libraries are appended to the result.
269+
func mergeLibs(existingLibs []*config.Library, libs []*config.Library) []*config.Library {
270+
existingMap := toMap(existingLibs)
271+
res := make([]*config.Library, len(existingLibs))
272+
copy(res, existingLibs)
273+
for _, lib := range libs {
274+
if _, ok := existingMap[lib.Name]; !ok {
275+
res = append(res, lib)
276+
}
277+
}
278+
return res
279+
}
280+
281+
func toMap(libs []*config.Library) map[string]*config.Library {
282+
libraryMap := make(map[string]*config.Library)
283+
for _, lib := range libs {
284+
libraryMap[lib.Name] = lib
285+
}
286+
return libraryMap
287+
}

tool/cmd/migrate/ruby_test.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,189 @@ func TestParseVersionedBuild(t *testing.T) {
295295
})
296296
}
297297
}
298+
299+
func TestMergeLibs(t *testing.T) {
300+
for _, test := range []struct {
301+
name string
302+
existingLibs []*config.Library
303+
libs []*config.Library
304+
want []*config.Library
305+
}{
306+
{
307+
name: "preserve existing library configuration",
308+
existingLibs: []*config.Library{
309+
{
310+
Name: "google-cloud-secret_manager-v1",
311+
Version: "1.2.0",
312+
APIs: []*config.API{
313+
{Path: "google/cloud/secretmanager/v1"},
314+
},
315+
},
316+
},
317+
libs: []*config.Library{
318+
{
319+
Name: "google-cloud-secret_manager-v1",
320+
Version: "0.1.0",
321+
APIs: []*config.API{
322+
{Path: "google/cloud/secretmanager/v1"},
323+
},
324+
},
325+
},
326+
want: []*config.Library{
327+
{
328+
Name: "google-cloud-secret_manager-v1",
329+
Version: "1.2.0",
330+
APIs: []*config.API{
331+
{Path: "google/cloud/secretmanager/v1"},
332+
},
333+
},
334+
},
335+
},
336+
{
337+
name: "append new discovered libraries",
338+
existingLibs: []*config.Library{
339+
{
340+
Name: "google-cloud-secret_manager-v1",
341+
Version: "1.2.0",
342+
},
343+
},
344+
libs: []*config.Library{
345+
{
346+
Name: "google-cloud-secret_manager-v1",
347+
Version: "0.1.0",
348+
},
349+
{
350+
Name: "google-cloud-compute-v1",
351+
Version: "0.1.0",
352+
},
353+
},
354+
want: []*config.Library{
355+
{
356+
Name: "google-cloud-secret_manager-v1",
357+
Version: "1.2.0",
358+
},
359+
{
360+
Name: "google-cloud-compute-v1",
361+
Version: "0.1.0",
362+
},
363+
},
364+
},
365+
{
366+
name: "nil existing libraries returns discovered libraries",
367+
libs: []*config.Library{
368+
{
369+
Name: "google-cloud-compute-v1",
370+
Version: "0.1.0",
371+
},
372+
},
373+
want: []*config.Library{
374+
{
375+
Name: "google-cloud-compute-v1",
376+
Version: "0.1.0",
377+
},
378+
},
379+
},
380+
{
381+
name: "preserve existing libraries not in discovered list",
382+
existingLibs: []*config.Library{
383+
{
384+
Name: "google-cloud-secret_manager-v1",
385+
Version: "1.2.0",
386+
},
387+
{
388+
Name: "google-cloud-recaptcha_enterprise-v1",
389+
Version: "1.0.0",
390+
},
391+
},
392+
libs: []*config.Library{
393+
{
394+
Name: "google-cloud-secret_manager-v1",
395+
Version: "0.1.0",
396+
},
397+
},
398+
want: []*config.Library{
399+
{
400+
Name: "google-cloud-secret_manager-v1",
401+
Version: "1.2.0",
402+
},
403+
{
404+
Name: "google-cloud-recaptcha_enterprise-v1",
405+
Version: "1.0.0",
406+
},
407+
},
408+
},
409+
} {
410+
t.Run(test.name, func(t *testing.T) {
411+
got := mergeLibs(test.existingLibs, test.libs)
412+
if diff := cmp.Diff(test.want, got); diff != "" {
413+
t.Errorf("mismatch (-want +got):\n%s", diff)
414+
}
415+
})
416+
}
417+
}
418+
419+
func TestParseExistingLibraries(t *testing.T) {
420+
for _, test := range []struct {
421+
name string
422+
setup func(t *testing.T, dir string)
423+
want []*config.Library
424+
}{
425+
{
426+
name: "valid librarian.yaml with libraries",
427+
setup: func(t *testing.T, dir string) {
428+
cfg := &config.Config{
429+
Libraries: []*config.Library{
430+
{
431+
Name: "google-cloud-secret_manager-v1",
432+
Version: "1.2.0",
433+
APIs: []*config.API{
434+
{Path: "google/cloud/secretmanager/v1"},
435+
},
436+
},
437+
},
438+
}
439+
if err := yaml.Write(filepath.Join(dir, config.LibrarianYAML), cfg); err != nil {
440+
t.Fatal(err)
441+
}
442+
},
443+
want: []*config.Library{
444+
{
445+
Name: "google-cloud-secret_manager-v1",
446+
Version: "1.2.0",
447+
APIs: []*config.API{
448+
{Path: "google/cloud/secretmanager/v1"},
449+
},
450+
},
451+
},
452+
},
453+
{
454+
name: "librarian.yaml does not exist",
455+
setup: func(t *testing.T, dir string) {},
456+
want: nil,
457+
},
458+
{
459+
name: "librarian.yaml without libraries",
460+
setup: func(t *testing.T, dir string) {
461+
cfg := &config.Config{
462+
Language: config.LanguageRuby,
463+
}
464+
if err := yaml.Write(filepath.Join(dir, config.LibrarianYAML), cfg); err != nil {
465+
t.Fatal(err)
466+
}
467+
},
468+
want: nil,
469+
},
470+
} {
471+
t.Run(test.name, func(t *testing.T) {
472+
dir := t.TempDir()
473+
test.setup(t, dir)
474+
got, err := parseExistingLibraries(dir)
475+
if err != nil {
476+
t.Fatal(err)
477+
}
478+
if diff := cmp.Diff(test.want, got); diff != "" {
479+
t.Errorf("mismatch (-want +got):\n%s", diff)
480+
}
481+
})
482+
}
483+
}

0 commit comments

Comments
 (0)