Skip to content

Commit ea9faf8

Browse files
vpavicphilwebb
authored andcommitted
Allow build info properties to be excluded
Update Maven and Gradle plugins to allow build info properties to be excluded. Prior to this commit, the `BuildPropertiesWriter` would fail with an NPE if the group, artifact, name or version properties were `null`. This was specifically problematic with the Gradle plugin, since its DSL allows `null` properties which would either be passed to the writer or, in the case of `artifact`, converted into a string value of "unspecified". See gh-27412
1 parent 2034ad4 commit ea9faf8

File tree

13 files changed

+355
-26
lines changed

13 files changed

+355
-26
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/integrating-with-actuator.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ By default, the generated build information is derived from the project:
3030
| Property | Default value
3131

3232
| `build.artifact`
33-
| The base name of the `bootJar` or `bootWar` task, or `unspecified` if no such task
34-
exists
33+
| The base name of the `bootJar` or `bootWar` task
3534

3635
| `build.group`
3736
| The group of the project
@@ -61,6 +60,8 @@ include::../gradle/integrating-with-actuator/build-info-custom-values.gradle[tag
6160
include::../gradle/integrating-with-actuator/build-info-custom-values.gradle.kts[tags=custom-values]
6261
----
6362

63+
NOTE: To omit any of the default properties from the generated build information, set its value to `null`.
64+
6465
The default value for `build.time` is the instant at which the project is being built.
6566
A side-effect of this is that the task will never be up-to-date.
6667
As a result, builds will take longer as more tasks, including the project's tests, will have to be executed.

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,12 +60,9 @@ public BuildInfo() {
6060
public void generateBuildProperties() {
6161
try {
6262
new BuildPropertiesWriter(new File(getDestinationDir(), "build-info.properties"))
63-
.writeBuildProperties(
64-
new ProjectDetails(this.properties.getGroup(),
65-
(this.properties.getArtifact() != null) ? this.properties.getArtifact()
66-
: "unspecified",
67-
this.properties.getVersion(), this.properties.getName(), this.properties.getTime(),
68-
coerceToStringValues(this.properties.getAdditional())));
63+
.writeBuildProperties(new ProjectDetails(this.properties.getGroup(), this.properties.getArtifact(),
64+
this.properties.getVersion(), this.properties.getName(), this.properties.getTime(),
65+
coerceToStringValues(this.properties.getAdditional())));
6966
}
7067
catch (IOException ex) {
7168
throw new TaskExecutionException(this, ex);

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* Integration tests for the {@link BuildInfo} task.
4040
*
4141
* @author Andy Wilkinson
42+
* @author Vedran Pavic
4243
*/
4344
@GradleCompatibility(configurationCache = true)
4445
class BuildInfoIntegrationTests {
@@ -50,8 +51,8 @@ void defaultValues() {
5051
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
5152
Properties buildInfoProperties = buildInfoProperties();
5253
assertThat(buildInfoProperties).containsKey("build.time");
53-
assertThat(buildInfoProperties).containsEntry("build.artifact", "unspecified");
54-
assertThat(buildInfoProperties).containsEntry("build.group", "");
54+
assertThat(buildInfoProperties).doesNotContainKey("build.artifact");
55+
assertThat(buildInfoProperties).doesNotContainKey("build.group");
5556
assertThat(buildInfoProperties).containsEntry("build.name", this.gradleBuild.getProjectDir().getName());
5657
assertThat(buildInfoProperties).containsEntry("build.version", "unspecified");
5758
}
@@ -122,6 +123,26 @@ void reproducibleOutputWithFixedTime() throws IOException, InterruptedException
122123
assertThat(firstHash).isEqualTo(secondHash);
123124
}
124125

126+
@TestTemplate
127+
void removePropertiesUsingNulls() {
128+
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
129+
Properties buildInfoProperties = buildInfoProperties();
130+
assertThat(buildInfoProperties).doesNotContainKey("build.group");
131+
assertThat(buildInfoProperties).doesNotContainKey("build.artifact");
132+
assertThat(buildInfoProperties).doesNotContainKey("build.version");
133+
assertThat(buildInfoProperties).doesNotContainKey("build.name");
134+
}
135+
136+
@TestTemplate
137+
void removePropertiesUsingEmptyStrings() {
138+
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
139+
Properties buildInfoProperties = buildInfoProperties();
140+
assertThat(buildInfoProperties).doesNotContainKey("build.group");
141+
assertThat(buildInfoProperties).doesNotContainKey("build.artifact");
142+
assertThat(buildInfoProperties).doesNotContainKey("build.version");
143+
assertThat(buildInfoProperties).doesNotContainKey("build.name");
144+
}
145+
125146
private Properties buildInfoProperties() {
126147
File file = new File(this.gradleBuild.getProjectDir(), "build/build-info.properties");
127148
assertThat(file).isFile();

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* Tests for {@link BuildInfo}.
3939
*
4040
* @author Andy Wilkinson
41+
* @author Vedran Pavic
4142
*/
4243
@ClassPathExclusions("kotlin-daemon-client-*")
4344
class BuildInfoTests {
@@ -49,8 +50,8 @@ class BuildInfoTests {
4950
void basicExecution() {
5051
Properties properties = buildInfoProperties(createTask(createProject("test")));
5152
assertThat(properties).containsKey("build.time");
52-
assertThat(properties).containsEntry("build.artifact", "unspecified");
53-
assertThat(properties).containsEntry("build.group", "");
53+
assertThat(properties).doesNotContainKey("build.artifact");
54+
assertThat(properties).doesNotContainKey("build.group");
5455
assertThat(properties).containsEntry("build.name", "test");
5556
assertThat(properties).containsEntry("build.version", "unspecified");
5657
}
@@ -62,6 +63,20 @@ void customArtifactIsReflectedInProperties() {
6263
assertThat(buildInfoProperties(task)).containsEntry("build.artifact", "custom");
6364
}
6465

66+
@Test
67+
void artifactCanBeRemovedFromPropertiesUsingNull() {
68+
BuildInfo task = createTask(createProject("test"));
69+
task.getProperties().setArtifact(null);
70+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.artifact");
71+
}
72+
73+
@Test
74+
void artifactCanBeRemovedFromPropertiesUsingEmptyString() {
75+
BuildInfo task = createTask(createProject("test"));
76+
task.getProperties().setArtifact("");
77+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.artifact");
78+
}
79+
6580
@Test
6681
void projectGroupIsReflectedInProperties() {
6782
BuildInfo task = createTask(createProject("test"));
@@ -76,13 +91,41 @@ void customGroupIsReflectedInProperties() {
7691
assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example");
7792
}
7893

94+
@Test
95+
void groupCanBeRemovedFromPropertiesUsingNull() {
96+
BuildInfo task = createTask(createProject("test"));
97+
task.getProperties().setGroup(null);
98+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.group");
99+
}
100+
101+
@Test
102+
void groupCanBeRemovedFromPropertiesUsingEmptyString() {
103+
BuildInfo task = createTask(createProject("test"));
104+
task.getProperties().setGroup("");
105+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.group");
106+
}
107+
79108
@Test
80109
void customNameIsReflectedInProperties() {
81110
BuildInfo task = createTask(createProject("test"));
82111
task.getProperties().setName("Example");
83112
assertThat(buildInfoProperties(task)).containsEntry("build.name", "Example");
84113
}
85114

115+
@Test
116+
void nameCanBeRemovedFromPropertiesUsingNull() {
117+
BuildInfo task = createTask(createProject("test"));
118+
task.getProperties().setName(null);
119+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.name");
120+
}
121+
122+
@Test
123+
void nameCanBeRemovedFromPropertiesUsingEmptyString() {
124+
BuildInfo task = createTask(createProject("test"));
125+
task.getProperties().setName("");
126+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.name");
127+
}
128+
86129
@Test
87130
void projectVersionIsReflectedInProperties() {
88131
BuildInfo task = createTask(createProject("test"));
@@ -97,6 +140,20 @@ void customVersionIsReflectedInProperties() {
97140
assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4");
98141
}
99142

143+
@Test
144+
void versionCanBeRemovedFromPropertiesUsingNull() {
145+
BuildInfo task = createTask(createProject("test"));
146+
task.getProperties().setVersion(null);
147+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.version");
148+
}
149+
150+
@Test
151+
void versionCanBeRemovedFromPropertiesUsingEmptyString() {
152+
BuildInfo task = createTask(createProject("test"));
153+
task.getProperties().setVersion("");
154+
assertThat(buildInfoProperties(task)).doesNotContainKey("build.version");
155+
}
156+
100157
@Test
101158
void timeIsSetInProperties() {
102159
BuildInfo task = createTask(createProject("test"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id 'org.springframework.boot' version '{version}' apply false
3+
}
4+
5+
group = 'foo'
6+
version = '0.1.0'
7+
8+
task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) {
9+
destinationDir project.buildDir
10+
properties {
11+
group = ''
12+
artifact = ''
13+
version = ''
14+
name = ''
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id 'org.springframework.boot' version '{version}' apply false
3+
}
4+
5+
group = 'foo'
6+
version = '0.1.0'
7+
8+
task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) {
9+
destinationDir project.buildDir
10+
properties {
11+
group = null
12+
artifact = null
13+
version = null
14+
name = null
15+
}
16+
}

spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,13 +25,15 @@
2525
import java.util.Properties;
2626

2727
import org.springframework.core.CollectionFactory;
28+
import org.springframework.util.StringUtils;
2829

2930
/**
3031
* A {@code BuildPropertiesWriter} writes the {@code build-info.properties} for
3132
* consumption by the Actuator.
3233
*
3334
* @author Andy Wilkinson
3435
* @author Stephane Nicoll
36+
* @author Vedran Pavic
3537
* @since 1.0.0
3638
*/
3739
public final class BuildPropertiesWriter {
@@ -71,10 +73,18 @@ private void createFileIfNecessary(File file) throws IOException {
7173

7274
protected Properties createBuildInfo(ProjectDetails project) {
7375
Properties properties = CollectionFactory.createSortedProperties(true);
74-
properties.put("build.group", project.getGroup());
75-
properties.put("build.artifact", project.getArtifact());
76-
properties.put("build.name", project.getName());
77-
properties.put("build.version", project.getVersion());
76+
if (StringUtils.hasText(project.getGroup())) {
77+
properties.put("build.group", project.getGroup());
78+
}
79+
if (StringUtils.hasText(project.getArtifact())) {
80+
properties.put("build.artifact", project.getArtifact());
81+
}
82+
if (StringUtils.hasText(project.getName())) {
83+
properties.put("build.name", project.getName());
84+
}
85+
if (StringUtils.hasText(project.getVersion())) {
86+
properties.put("build.version", project.getVersion());
87+
}
7888
if (project.getTime() != null) {
7989
properties.put("build.time", DateTimeFormatter.ISO_INSTANT.format(project.getTime()));
8090
}

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* Integration tests for the Maven plugin's build info support.
3636
*
3737
* @author Andy Wilkinson
38+
* @author Vedran Pavic
3839
*/
3940
@ExtendWith(MavenBuildExtension.class)
4041
class BuildInfoIntegrationTests {
@@ -63,6 +64,14 @@ void generatedBuildInfoUsesCustomBuildTime(MavenBuild mavenBuild) {
6364
.hasBuildTime("2019-07-08T08:00:00Z")));
6465
}
6566

67+
@TestTemplate
68+
void generatedBuildInfoUsesCustomBuildProperties(MavenBuild mavenBuild) {
69+
mavenBuild.project("build-info-custom-build-properties")
70+
.execute(buildInfo((buildInfo) -> assertThat(buildInfo).hasBuildGroup("test-group")
71+
.hasBuildArtifact("test-artifact").hasBuildName("test-name").hasBuildVersion("test-version")
72+
.containsBuildTime()));
73+
}
74+
6675
@TestTemplate
6776
void generatedBuildInfoReproducible(MavenBuild mavenBuild) {
6877
mavenBuild.project("build-info-reproducible")
@@ -89,6 +98,13 @@ void whenBuildTimeIsDisabledIfDoesNotAppearInGeneratedBuildInfo(MavenBuild maven
8998
.doesNotContainBuildTime()));
9099
}
91100

101+
@TestTemplate
102+
void whenBuildPropertiesAreEmptyTheyDoNotAppearInGeneratedBuildInfo(MavenBuild mavenBuild) {
103+
mavenBuild.project("build-info-disable-build-properties").execute(
104+
buildInfo((buildInfo) -> assertThat(buildInfo).doesNotContainBuildGroup().doesNotContainBuildArtifact()
105+
.doesNotContainBuildName().doesNotContainBuildVersion().containsBuildTime()));
106+
}
107+
92108
private ProjectCallback buildInfo(Consumer<AssertProvider<BuildInfoAssert>> buildInfo) {
93109
return buildInfo("target/classes/META-INF/build-info.properties", buildInfo);
94110
}
@@ -130,18 +146,34 @@ BuildInfoAssert hasBuildGroup(String expected) {
130146
return containsEntry("build.group", expected);
131147
}
132148

149+
BuildInfoAssert doesNotContainBuildGroup() {
150+
return doesNotContainKey("build.group");
151+
}
152+
133153
BuildInfoAssert hasBuildArtifact(String expected) {
134154
return containsEntry("build.artifact", expected);
135155
}
136156

157+
BuildInfoAssert doesNotContainBuildArtifact() {
158+
return doesNotContainKey("build.artifact");
159+
}
160+
137161
BuildInfoAssert hasBuildName(String expected) {
138162
return containsEntry("build.name", expected);
139163
}
140164

165+
BuildInfoAssert doesNotContainBuildName() {
166+
return doesNotContainKey("build.name");
167+
}
168+
141169
BuildInfoAssert hasBuildVersion(String expected) {
142170
return containsEntry("build.version", expected);
143171
}
144172

173+
BuildInfoAssert doesNotContainBuildVersion() {
174+
return doesNotContainKey("build.version");
175+
}
176+
145177
BuildInfoAssert containsBuildTime() {
146178
return containsKey("build.time");
147179
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>org.springframework.boot.maven.it</groupId>
6+
<artifactId>build-info-custom-build-properties</artifactId>
7+
<version>0.0.1.BUILD-SNAPSHOT</version>
8+
<name>Generate build info with custom build properties</name>
9+
<properties>
10+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
11+
<maven.compiler.source>@java.version@</maven.compiler.source>
12+
<maven.compiler.target>@java.version@</maven.compiler.target>
13+
</properties>
14+
<build>
15+
<plugins>
16+
<plugin>
17+
<groupId>@project.groupId@</groupId>
18+
<artifactId>@project.artifactId@</artifactId>
19+
<version>@project.version@</version>
20+
<executions>
21+
<execution>
22+
<configuration>
23+
<group>test-group</group>
24+
<artifact>test-artifact</artifact>
25+
<version>test-version</version>
26+
<name>test-name</name>
27+
</configuration>
28+
<goals>
29+
<goal>build-info</goal>
30+
</goals>
31+
</execution>
32+
</executions>
33+
</plugin>
34+
</plugins>
35+
</build>
36+
</project>

0 commit comments

Comments
 (0)