Skip to content

Commit 3f0fc61

Browse files
authored
extract spring-boot-dependencies data for type safety (#31480)
1 parent 3b81e67 commit 3f0fc61

File tree

26 files changed

+884
-672
lines changed

26 files changed

+884
-672
lines changed

.blueprint/cli/commands.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ const defaultCommands = {
3838
desc: 'Generate a matrix for GitHub Actions',
3939
blueprint: '@jhipster/jhipster-dev',
4040
},
41-
'update-vscode': {
42-
desc: 'Update generator-jhipster vscode files',
43-
blueprint: '@jhipster/jhipster-dev',
44-
},
4541
'update-generators': {
4642
desc: 'Update generator-jhipster generators files',
4743
blueprint: '@jhipster/jhipster-dev',
4844
},
45+
'update-spring-boot': {
46+
desc: 'Update Spring Boot metadata files',
47+
blueprint: '@jhipster/jhipster-dev',
48+
},
49+
'update-vscode': {
50+
desc: 'Update generator-jhipster vscode files',
51+
blueprint: '@jhipster/jhipster-dev',
52+
},
4953
};
5054

5155
export default defaultCommands;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Copyright 2013-2025 the original author or authors from the JHipster project.
3+
*
4+
* This file is part of the JHipster project, see https://www.jhipster.tech/
5+
* for more information.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* https://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
import BaseApplicationGenerator from '../../generators/base-core/index.ts';
20+
import { parseMavenPom } from '../../generators/maven/support/dependabot-maven.ts';
21+
import { getPackageRoot } from '../../lib/index.ts';
22+
23+
export default class UpdateSpringBootGenerator extends BaseApplicationGenerator {
24+
springBootJson!: {
25+
versions: Record<string, string>;
26+
properties: Record<string, string>;
27+
modules: Record<string, string>;
28+
};
29+
30+
async beforeQueue() {
31+
await this.composeWithJHipster('bootstrap');
32+
}
33+
34+
get [BaseApplicationGenerator.PREPARING]() {
35+
return this.asAnyTaskGroup({
36+
parsePom() {
37+
this.destinationRoot(getPackageRoot());
38+
const springDependenciesPom = this.readTemplate(
39+
this.fetchFromInstalledJHipster('spring-boot/resources/spring-boot-dependencies.pom'),
40+
) as string;
41+
const pom = parseMavenPom(springDependenciesPom);
42+
this.springBootJson = {
43+
versions: {
44+
[pom.project.artifactId]: pom.project.version,
45+
...Object.fromEntries(
46+
Object.entries(pom.project.properties!)
47+
.filter(([property]) => property.endsWith('.version'))
48+
.map(([property, value]) => [property.slice(0, -8), value]),
49+
),
50+
},
51+
properties: pom.project.properties ?? {},
52+
modules: Object.fromEntries(
53+
pom.project.dependencyManagement?.dependencies.dependency
54+
.filter(dep => dep.groupId === 'org.springframework.boot')
55+
.map(dep => [dep.artifactId, '']) ?? [],
56+
),
57+
};
58+
},
59+
});
60+
}
61+
62+
get [BaseApplicationGenerator.WRITING]() {
63+
return this.asAnyTaskGroup({
64+
async writing() {
65+
this.writeDestinationJSON('generators/spring-boot/resources/spring-boot-dependencies.json', this.springBootJson);
66+
},
67+
});
68+
}
69+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Copyright 2013-2025 the original author or authors from the JHipster project.
3+
*
4+
* This file is part of the JHipster project, see https://www.jhipster.tech/
5+
* for more information.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* https://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
export { default } from './generator.ts';

.github/workflows/update-spring-boot-dependencies.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,17 @@ jobs:
5151
with:
5252
ref: ${{ inputs.destBranch }}
5353
fetch-depth: 0
54-
- uses: jhipster/actions/setup-git@v1
54+
- uses: jhipster/actions/setup-runner@v1
55+
with:
56+
node-version: 24
57+
binary-dir: ${{ github.workspace }}/generator-jhipster/bin
5558
- name: Download spring-boot-dependencies bom
5659
run: |
5760
wget -O spring-boot-dependencies.pom ${{ inputs.repository }}/org/springframework/boot/spring-boot-dependencies/${{ inputs.springBootVersion }}/spring-boot-dependencies-${{ inputs.springBootVersion }}.pom
5861
git add .
5962
working-directory: generators/spring-boot/resources
63+
- name: Parse POM
64+
run: bin/jhipster.cjs update-spring-boot-dependencies --force
6065
- name: Create Pull Request
6166
uses: peter-evans/create-pull-request@v7
6267
with:

generators/base-workspaces/internal/docker-dependencies.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import { mutateData } from '../../../lib/utils/object.ts';
2020
import type BaseCoreGenerator from '../../base-core/generator.ts';
2121
import { getDockerfileContainers } from '../../docker/utils.ts';
22+
import springBootDependencies from '../../spring-boot/resources/spring-boot-dependencies.ts';
2223

2324
const ELASTICSEARCH_IMAGE = 'docker.elastic.co/elasticsearch/elasticsearch';
2425

@@ -28,11 +29,7 @@ export function loadDockerDependenciesTask<const G extends BaseCoreGenerator>(
2829
) {
2930
context.dockerContainers ??= {};
3031
const dockerfile = this.readTemplate(this.fetchFromInstalledJHipster('server/resources/Dockerfile')) as string;
31-
const springDependenciesPom = this.readTemplate(
32-
this.fetchFromInstalledJHipster('spring-boot/resources/spring-boot-dependencies.pom'),
33-
) as string;
34-
const result = springDependenciesPom.match(/elasticsearch-client.version>(?<version>.+)<\/elasticsearch-client.version>/);
35-
const elasticsearchClientVersion = result!.groups!.version;
32+
const elasticsearchClientVersion = springBootDependencies.properties['elasticsearch-client.version'];
3633
mutateData(
3734
context.dockerContainers,
3835
this.prepareDependencies(

generators/maven/support/dependabot-maven.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,20 @@
1818
*/
1919
import { XMLParser } from 'fast-xml-parser';
2020

21+
import type { JavaArtifact } from '../../java-simple-application/types.ts';
2122
import { defaultXmlParserOptions } from '../internal/xml-store.ts';
2223

2324
export type MavenPom = {
2425
project: {
2526
artifactId: string;
2627
version: string;
2728
properties?: Record<string, string>;
29+
dependencies?: JavaArtifact[];
30+
dependencyManagement?: {
31+
dependencies: {
32+
dependency: JavaArtifact[];
33+
};
34+
};
2835
};
2936
};
3037

generators/spring-boot/generator.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import type { Source as CommonSource } from '../common/types.ts';
3939
import type { Entity as CypressEntity } from '../cypress/types.ts';
4040
import { ADD_SPRING_MILESTONE_REPOSITORY } from '../generator-constants.ts';
4141
import { addJavaImport, generateKeyStore, javaBeanCase } from '../java/support/index.ts';
42-
import { getPomVersionProperties, parseMavenPom } from '../maven/support/index.ts';
42+
import type { JavaArtifactType } from '../java-simple-application/types.ts';
4343
import {
4444
getJavaValueGeneratorForType,
4545
getSpecificationBuildForType,
@@ -52,12 +52,14 @@ import cleanupTask from './cleanup.ts';
5252
import { writeFiles as writeEntityFiles } from './entity-files.ts';
5353
import { serverFiles } from './files.ts';
5454
import { askForOptionalItems, askForServerSideOpts, askForServerTestOpts } from './prompts.ts';
55+
import springBootDependencies from './resources/spring-boot-dependencies.ts';
5556
import type {
5657
Application as SpringBootApplication,
5758
Config as SpringBootConfig,
5859
Entity as SpringBootEntity,
5960
Options as SpringBootOptions,
6061
Source as SpringBootSource,
62+
SpringBootModule,
6163
} from './types.ts';
6264

6365
const { CAFFEINE, EHCACHE, HAZELCAST, INFINISPAN, MEMCACHED, REDIS } = cacheTypes;
@@ -274,11 +276,9 @@ export default class SpringBootGenerator extends SpringBootApplicationGenerator
274276
'spring-boot-dependencies': "'SPRING-BOOT-VERSION'",
275277
};
276278
} else {
277-
const pomFile = this.readJHipsterResource('spring-boot-dependencies.pom')!;
278-
const pom = parseMavenPom(pomFile);
279-
application.springBootDependencies = this.prepareDependencies(getPomVersionProperties(pom), 'java');
279+
application.springBootDependencies = this.prepareDependencies(springBootDependencies.versions, 'java');
280280
application.javaDependencies!['spring-boot'] = application.springBootDependencies['spring-boot-dependencies'];
281-
Object.assign(application.javaManagedProperties!, pom.project.properties);
281+
Object.assign(application.javaManagedProperties!, springBootDependencies.properties);
282282
application.javaDependencies!.liquibase = application.javaManagedProperties!['liquibase.version']!;
283283
}
284284
},
@@ -420,6 +420,26 @@ ${classProperties
420420
);
421421
};
422422
},
423+
needles({ source }) {
424+
const getScopeForModule = (moduleName: SpringBootModule): JavaArtifactType['scope'] => {
425+
if (moduleName === 'spring-boot-properties-migrator') return 'runtime';
426+
if (moduleName === 'spring-boot-configuration-processor') return 'annotationProcessor';
427+
return moduleName.endsWith('-test') ? 'test' : undefined;
428+
};
429+
source.addSpringBootModule = (...moduleNames) =>
430+
source.addJavaDependencies?.(
431+
moduleNames
432+
.filter(module => typeof module === 'string' || module.condition)
433+
.map(module => (typeof module === 'string' ? module : module.module))
434+
.map(name => ({
435+
groupId: 'org.springframework.boot',
436+
artifactId: name,
437+
scope: getScopeForModule(name),
438+
})),
439+
);
440+
441+
source.overrideProperty = props => source.addJavaProperty!(props);
442+
},
423443
});
424444
}
425445

@@ -616,6 +636,26 @@ ${classProperties
616636

617637
get postWriting() {
618638
return this.asPostWritingTaskGroup({
639+
baseDependencies({ application, source }) {
640+
source.addSpringBootModule!(
641+
'spring-boot-configuration-processor',
642+
'spring-boot-loader-tools',
643+
'spring-boot-starter',
644+
'spring-boot-starter-actuator',
645+
'spring-boot-starter-aop',
646+
'spring-boot-starter-mail',
647+
'spring-boot-starter-test',
648+
'spring-boot-starter-thymeleaf',
649+
'spring-boot-starter-tomcat',
650+
'spring-boot-starter-validation',
651+
`spring-boot-starter-web${application.reactive ? 'flux' : ''}`,
652+
'spring-boot-test',
653+
{
654+
condition: application.authenticationTypeSession,
655+
module: 'spring-boot-starter-security',
656+
},
657+
);
658+
},
619659
addJHipsterBomDependencies({ application, source }) {
620660
const {
621661
applicationTypeGateway,
@@ -695,11 +735,7 @@ ${classProperties
695735
};
696736
source.addMavenPluginRepository?.(springRepository);
697737
source.addMavenRepository?.(springRepository);
698-
source.addMavenDependency?.({
699-
groupId: 'org.springframework.boot',
700-
artifactId: 'spring-boot-properties-migrator',
701-
scope: 'runtime',
702-
});
738+
source.addSpringBootModule?.('spring-boot-properties-migrator');
703739
}
704740
if (application.jhipsterDependenciesVersion?.endsWith('-SNAPSHOT')) {
705741
source.addMavenRepository?.({

generators/spring-boot/generators/jwt/__snapshots__/generator.spec.ts.snap

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,11 @@
22

33
exports[`generator - spring-boot:jwt reactive(false)-skipUserManagement(false) should call source snapshot 1`] = `
44
{
5-
"addJavaDefinition": [
6-
{
7-
"dependencies": [
8-
{
9-
"artifactId": "spring-boot-starter-security",
10-
"groupId": "org.springframework.boot",
11-
},
12-
{
13-
"artifactId": "spring-boot-starter-oauth2-resource-server",
14-
"groupId": "org.springframework.boot",
15-
},
16-
],
17-
},
5+
"addSpringBootModule": [
6+
[
7+
"spring-boot-starter-security",
8+
"spring-boot-starter-oauth2-resource-server",
9+
],
1810
],
1911
}
2012
`;
@@ -59,19 +51,11 @@ exports[`generator - spring-boot:jwt reactive(false)-skipUserManagement(false) s
5951

6052
exports[`generator - spring-boot:jwt reactive(false)-skipUserManagement(true) should call source snapshot 1`] = `
6153
{
62-
"addJavaDefinition": [
63-
{
64-
"dependencies": [
65-
{
66-
"artifactId": "spring-boot-starter-security",
67-
"groupId": "org.springframework.boot",
68-
},
69-
{
70-
"artifactId": "spring-boot-starter-oauth2-resource-server",
71-
"groupId": "org.springframework.boot",
72-
},
73-
],
74-
},
54+
"addSpringBootModule": [
55+
[
56+
"spring-boot-starter-security",
57+
"spring-boot-starter-oauth2-resource-server",
58+
],
7559
],
7660
}
7761
`;
@@ -119,19 +103,11 @@ exports[`generator - spring-boot:jwt reactive(false)-skipUserManagement(true) sh
119103

120104
exports[`generator - spring-boot:jwt reactive(true)-skipUserManagement(false) should call source snapshot 1`] = `
121105
{
122-
"addJavaDefinition": [
123-
{
124-
"dependencies": [
125-
{
126-
"artifactId": "spring-boot-starter-security",
127-
"groupId": "org.springframework.boot",
128-
},
129-
{
130-
"artifactId": "spring-boot-starter-oauth2-resource-server",
131-
"groupId": "org.springframework.boot",
132-
},
133-
],
134-
},
106+
"addSpringBootModule": [
107+
[
108+
"spring-boot-starter-security",
109+
"spring-boot-starter-oauth2-resource-server",
110+
],
135111
],
136112
}
137113
`;
@@ -176,19 +152,11 @@ exports[`generator - spring-boot:jwt reactive(true)-skipUserManagement(false) sh
176152

177153
exports[`generator - spring-boot:jwt reactive(true)-skipUserManagement(true) should call source snapshot 1`] = `
178154
{
179-
"addJavaDefinition": [
180-
{
181-
"dependencies": [
182-
{
183-
"artifactId": "spring-boot-starter-security",
184-
"groupId": "org.springframework.boot",
185-
},
186-
{
187-
"artifactId": "spring-boot-starter-oauth2-resource-server",
188-
"groupId": "org.springframework.boot",
189-
},
190-
],
191-
},
155+
"addSpringBootModule": [
156+
[
157+
"spring-boot-starter-security",
158+
"spring-boot-starter-oauth2-resource-server",
159+
],
192160
],
193161
}
194162
`;

generators/spring-boot/generators/jwt/generator.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,7 @@ export default class JwtGenerator extends SpringBootApplicationGenerator {
9595
get postWriting() {
9696
return this.asPostWritingTaskGroup({
9797
dependencies({ source }) {
98-
source.addJavaDefinition!({
99-
dependencies: [
100-
{ groupId: 'org.springframework.boot', artifactId: 'spring-boot-starter-security' },
101-
{ groupId: 'org.springframework.boot', artifactId: 'spring-boot-starter-oauth2-resource-server' },
102-
],
103-
});
98+
source.addSpringBootModule?.('spring-boot-starter-security', 'spring-boot-starter-oauth2-resource-server');
10499
},
105100
});
106101
}

0 commit comments

Comments
 (0)