Skip to content

Commit 8cd3491

Browse files
authored
feat(yoshi-java-monorepo): update library version in librarian.yaml (#2750)
1 parent b4686c4 commit 8cd3491

4 files changed

Lines changed: 244 additions & 0 deletions

File tree

src/strategies/java-yoshi-mono-repo.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import {ConventionalCommit} from '../commit';
3131
import {Java, JavaBuildUpdatesOption} from './java';
3232
import {JavaUpdate} from '../updaters/java/java-update';
33+
import {LibrarianYamlUpdater} from '../updaters/java/librarian-yaml';
3334
import {filterCommits} from '../util/filter-commits';
3435

3536
export class JavaYoshiMonoRepo extends Java {
@@ -135,6 +136,11 @@ export class JavaYoshiMonoRepo extends Java {
135136
this.targetBranch,
136137
this.path
137138
);
139+
const librarianFilesSearch = this.github.findFilesByFilenameAndRef(
140+
'librarian.yaml',
141+
this.targetBranch,
142+
this.path
143+
);
138144

139145
const pomFiles = await pomFilesSearch;
140146
pomFiles.forEach(path => {
@@ -201,6 +207,18 @@ export class JavaYoshiMonoRepo extends Java {
201207
});
202208
});
203209

210+
const librarianFiles = await librarianFilesSearch;
211+
librarianFiles.forEach(path => {
212+
updates.push({
213+
path: this.addPath(path),
214+
createIfMissing: false,
215+
updater: new LibrarianYamlUpdater({
216+
version,
217+
versionsMap,
218+
}),
219+
});
220+
});
221+
204222
this.extraFiles.forEach(extraFile => {
205223
if (typeof extraFile === 'object') {
206224
return;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {DefaultUpdater} from '../default';
16+
import * as yaml from 'yaml';
17+
import {logger as defaultLogger, Logger} from '../../util/logger';
18+
19+
export interface JavaModule {
20+
distribution_name_override: string;
21+
[key: string]: any;
22+
}
23+
24+
export interface LibrarianLibrary {
25+
name: string;
26+
version: string;
27+
java: JavaModule;
28+
[key: string]: any;
29+
}
30+
31+
export interface LibrarianYamlSchema {
32+
libraries: LibrarianLibrary[];
33+
[key: string]: any;
34+
}
35+
36+
/**
37+
* Updates a librarian.yaml file.
38+
*/
39+
export class LibrarianYamlUpdater extends DefaultUpdater {
40+
specialArtifacts: ReadonlyMap<string, string> = new Map([
41+
['google-cloud-java', 'google-cloud-java'],
42+
]);
43+
/**
44+
* Given initial file contents, return updated contents.
45+
* @param {string} content The initial content
46+
* @returns {string} The updated content
47+
*/
48+
updateContent(content: string, logger: Logger = defaultLogger): string {
49+
if (!this.versionsMap) {
50+
logger.warn('missing versions map');
51+
return content;
52+
}
53+
54+
// Use yaml package to make sure librarian.yaml is not reformatted because
55+
// we use different tool to format librarian.yaml.
56+
const doc = yaml.parseDocument(content);
57+
if (!doc || doc.errors.length > 0) {
58+
logger.warn('Invalid yaml, cannot be parsed');
59+
return content;
60+
}
61+
62+
const libraries = doc.get('libraries');
63+
if (!libraries || !yaml.isSeq(libraries)) {
64+
return content;
65+
}
66+
67+
let modified = false;
68+
for (const library of libraries.items) {
69+
if (!yaml.isMap(library)) continue;
70+
71+
const artifactID = this.findArtifactID(
72+
library.toJSON() as LibrarianLibrary
73+
);
74+
if (this.versionsMap.has(artifactID)) {
75+
const newVersion = this.versionsMap.get(artifactID);
76+
if (newVersion && library.get('version') !== newVersion.toString()) {
77+
library.set('version', newVersion.toString());
78+
modified = true;
79+
}
80+
}
81+
}
82+
83+
if (modified) {
84+
return doc.toString({lineWidth: 0});
85+
}
86+
return content;
87+
}
88+
89+
findArtifactID(library: LibrarianLibrary): string {
90+
const artifact = this.specialArtifacts.get(library.name);
91+
if (artifact) {
92+
return artifact;
93+
}
94+
if (library.java && library.java.distribution_name_override) {
95+
return library.java.distribution_name_override.split(':')[1];
96+
}
97+
return `google-cloud-${library.name}`;
98+
}
99+
}

test/strategies/java-yoshi-mono-repo.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {TagName} from '../../src/util/tag-name';
2727
import {Version} from '../../src/version';
2828
import {Changelog} from '../../src/updaters/changelog';
2929
import {JavaUpdate} from '../../src/updaters/java/java-update';
30+
import {LibrarianYamlUpdater} from '../../src/updaters/java/librarian-yaml';
3031
import {VersionsManifest} from '../../src/updaters/java/versions-manifest';
3132
import {CompositeUpdater} from '../../src/updaters/composite';
3233

@@ -261,6 +262,9 @@ describe('JavaYoshiMonoRepo', () => {
261262
findFilesStub
262263
.withArgs('Version.java', 'main', '.')
263264
.resolves(['path1/Version.java']);
265+
findFilesStub
266+
.withArgs('librarian.yaml', 'main', '.')
267+
.resolves(['path1/librarian.yaml']);
264268
const getFileContentsStub = sandbox.stub(
265269
github,
266270
'getFileContentsOnBranch'
@@ -289,6 +293,7 @@ describe('JavaYoshiMonoRepo', () => {
289293
assertHasUpdate(updates, 'path1/README.md', JavaUpdate);
290294
assertHasUpdate(updates, 'path2/README.md', JavaUpdate);
291295
assertHasUpdate(updates, 'path1/Version.java', JavaUpdate);
296+
assertHasUpdate(updates, 'path1/librarian.yaml', LibrarianYamlUpdater);
292297
});
293298

294299
it('finds and updates extra files', async () => {
@@ -340,6 +345,9 @@ describe('JavaYoshiMonoRepo', () => {
340345
findFilesStub
341346
.withArgs('Version.java', 'main', '.')
342347
.resolves(['path1/Version.java']);
348+
findFilesStub
349+
.withArgs('librarian.yaml', 'main', '.')
350+
.resolves(['path1/librarian.yaml']);
343351
const getFileContentsStub = sandbox.stub(
344352
github,
345353
'getFileContentsOnBranch'
@@ -370,6 +378,7 @@ describe('JavaYoshiMonoRepo', () => {
370378
assertHasUpdate(updates, 'path1/README.md', JavaUpdate);
371379
assertHasUpdate(updates, 'path2/README.md', JavaUpdate);
372380
assertHasUpdate(updates, 'path1/Version.java', JavaUpdate);
381+
assertHasUpdate(updates, 'path1/librarian.yaml', LibrarianYamlUpdater);
373382
});
374383

375384
it('updates changelog.json', async () => {

test/updaters/librarian-yaml.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {describe, it} from 'mocha';
16+
import {expect} from 'chai';
17+
import {LibrarianYamlUpdater} from '../../src/updaters/java/librarian-yaml';
18+
import {Version} from '../../src/version';
19+
20+
const oldContent = `language: java
21+
sources:
22+
googleapis:
23+
commit: cd090841ab172574e740c214c99df00aef9c0dee
24+
sha256: 08e4b7744dc23b6e3320a3f1d05db9f40853aaf1089d06bfb8d79044b7a66f21
25+
default:
26+
java:
27+
libraries_bom_version: 26.79.0
28+
libraries:
29+
- name: google-cloud-java
30+
version: 1.84.0
31+
skip_generate: true
32+
- name: shopping-css
33+
version: 0.58.0
34+
apis:
35+
- path: google/shopping/css/v1
36+
java:
37+
api_description_override: The CSS API is used to manage your CSS and control your CSS Products portfolio
38+
non_cloud_api: true
39+
distribution_name_override: com.google.shopping:google-shopping-css
40+
name_pretty_override: CSS API
41+
java_apis:
42+
- additional_protos:
43+
- google/cloud/common_resources.proto
44+
path: google/shopping/css/v1
45+
product_documentation_override: https://developers.google.com/comparison-shopping-services/api
46+
- name: secretmanager
47+
version: 2.1.0
48+
java:
49+
api_description_override: allows you to encrypt, store, manage, and audit infrastructure and application-level secrets.
50+
`;
51+
52+
const updatedContent = `language: java
53+
sources:
54+
googleapis:
55+
commit: cd090841ab172574e740c214c99df00aef9c0dee
56+
sha256: 08e4b7744dc23b6e3320a3f1d05db9f40853aaf1089d06bfb8d79044b7a66f21
57+
default:
58+
java:
59+
libraries_bom_version: 26.79.0
60+
libraries:
61+
- name: google-cloud-java
62+
version: 1.85.0
63+
skip_generate: true
64+
- name: shopping-css
65+
version: 0.59.0
66+
apis:
67+
- path: google/shopping/css/v1
68+
java:
69+
api_description_override: The CSS API is used to manage your CSS and control your CSS Products portfolio
70+
non_cloud_api: true
71+
distribution_name_override: com.google.shopping:google-shopping-css
72+
name_pretty_override: CSS API
73+
java_apis:
74+
- additional_protos:
75+
- google/cloud/common_resources.proto
76+
path: google/shopping/css/v1
77+
product_documentation_override: https://developers.google.com/comparison-shopping-services/api
78+
- name: secretmanager
79+
version: 2.2.0
80+
java:
81+
api_description_override: allows you to encrypt, store, manage, and audit infrastructure and application-level secrets.
82+
`;
83+
84+
describe('LibrarianYamlUpdater', () => {
85+
it('updates librarian.yaml version based on versionsMap', () => {
86+
const versionsMap = new Map<string, Version>();
87+
versionsMap.set('google-shopping-css', Version.parse('0.59.0'));
88+
versionsMap.set('google-cloud-secretmanager', Version.parse('2.2.0'));
89+
versionsMap.set('google-cloud-java', Version.parse('1.85.0'));
90+
91+
const updater = new LibrarianYamlUpdater({
92+
version: Version.parse('1.0.0'), // Unused
93+
versionsMap,
94+
});
95+
const newContent = updater.updateContent(oldContent);
96+
// Compare the content to verify librarian.yaml is not reformatted.
97+
expect(newContent).to.eq(updatedContent);
98+
});
99+
100+
it('returns original content if versionsMap is missing', () => {
101+
const updater = new LibrarianYamlUpdater({
102+
version: Version.parse('1.0.0'),
103+
});
104+
const newContent = updater.updateContent(oldContent);
105+
expect(newContent).to.equal(oldContent);
106+
});
107+
108+
it('returns original content if no libraries match versionsMap', () => {
109+
const versionsMap = new Map<string, Version>();
110+
versionsMap.set('non-existent', Version.parse('1.0.0'));
111+
const updater = new LibrarianYamlUpdater({
112+
version: Version.parse('1.0.0'),
113+
versionsMap,
114+
});
115+
const newContent = updater.updateContent(oldContent);
116+
expect(newContent).to.equal(oldContent);
117+
});
118+
});

0 commit comments

Comments
 (0)