From ea9faf8690ab1863bde8caa0c7f345f811d894f4 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Fri, 16 Jul 2021 21:57:57 +0200 Subject: [PATCH 1/2] 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 --- .../asciidoc/integrating-with-actuator.adoc | 5 +- .../gradle/tasks/buildinfo/BuildInfo.java | 11 +-- .../buildinfo/BuildInfoIntegrationTests.java | 25 ++++++- .../tasks/buildinfo/BuildInfoTests.java | 61 ++++++++++++++- ...s-removePropertiesUsingEmptyStrings.gradle | 16 ++++ ...ionTests-removePropertiesUsingNulls.gradle | 16 ++++ .../loader/tools/BuildPropertiesWriter.java | 20 +++-- .../boot/maven/BuildInfoIntegrationTests.java | 32 ++++++++ .../pom.xml | 36 +++++++++ .../main/java/org/test/SampleApplication.java | 24 ++++++ .../pom.xml | 36 +++++++++ .../main/java/org/test/SampleApplication.java | 24 ++++++ .../boot/maven/BuildInfoMojo.java | 75 +++++++++++++++++-- 13 files changed, 355 insertions(+), 26 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingEmptyStrings.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingNulls.gradle create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/integrating-with-actuator.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/integrating-with-actuator.adoc index 928448551e..535e7948e0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/integrating-with-actuator.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/integrating-with-actuator.adoc @@ -30,8 +30,7 @@ By default, the generated build information is derived from the project: | Property | Default value | `build.artifact` -| The base name of the `bootJar` or `bootWar` task, or `unspecified` if no such task - exists +| The base name of the `bootJar` or `bootWar` task | `build.group` | The group of the project @@ -61,6 +60,8 @@ include::../gradle/integrating-with-actuator/build-info-custom-values.gradle[tag include::../gradle/integrating-with-actuator/build-info-custom-values.gradle.kts[tags=custom-values] ---- +NOTE: To omit any of the default properties from the generated build information, set its value to `null`. + The default value for `build.time` is the instant at which the project is being built. A side-effect of this is that the task will never be up-to-date. As a result, builds will take longer as more tasks, including the project's tests, will have to be executed. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java index 2246248790..b5cd12aa3b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,12 +60,9 @@ public class BuildInfo extends ConventionTask { public void generateBuildProperties() { try { new BuildPropertiesWriter(new File(getDestinationDir(), "build-info.properties")) - .writeBuildProperties( - new ProjectDetails(this.properties.getGroup(), - (this.properties.getArtifact() != null) ? this.properties.getArtifact() - : "unspecified", - this.properties.getVersion(), this.properties.getName(), this.properties.getTime(), - coerceToStringValues(this.properties.getAdditional()))); + .writeBuildProperties(new ProjectDetails(this.properties.getGroup(), this.properties.getArtifact(), + this.properties.getVersion(), this.properties.getName(), this.properties.getTime(), + coerceToStringValues(this.properties.getAdditional()))); } catch (IOException ex) { throw new TaskExecutionException(this, ex); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java index bc9e7a4fe7..a792c552e2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.java @@ -39,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Integration tests for the {@link BuildInfo} task. * * @author Andy Wilkinson + * @author Vedran Pavic */ @GradleCompatibility(configurationCache = true) class BuildInfoIntegrationTests { @@ -50,8 +51,8 @@ class BuildInfoIntegrationTests { assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); Properties buildInfoProperties = buildInfoProperties(); assertThat(buildInfoProperties).containsKey("build.time"); - assertThat(buildInfoProperties).containsEntry("build.artifact", "unspecified"); - assertThat(buildInfoProperties).containsEntry("build.group", ""); + assertThat(buildInfoProperties).doesNotContainKey("build.artifact"); + assertThat(buildInfoProperties).doesNotContainKey("build.group"); assertThat(buildInfoProperties).containsEntry("build.name", this.gradleBuild.getProjectDir().getName()); assertThat(buildInfoProperties).containsEntry("build.version", "unspecified"); } @@ -122,6 +123,26 @@ class BuildInfoIntegrationTests { assertThat(firstHash).isEqualTo(secondHash); } + @TestTemplate + void removePropertiesUsingNulls() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).doesNotContainKey("build.group"); + assertThat(buildInfoProperties).doesNotContainKey("build.artifact"); + assertThat(buildInfoProperties).doesNotContainKey("build.version"); + assertThat(buildInfoProperties).doesNotContainKey("build.name"); + } + + @TestTemplate + void removePropertiesUsingEmptyStrings() { + assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + Properties buildInfoProperties = buildInfoProperties(); + assertThat(buildInfoProperties).doesNotContainKey("build.group"); + assertThat(buildInfoProperties).doesNotContainKey("build.artifact"); + assertThat(buildInfoProperties).doesNotContainKey("build.version"); + assertThat(buildInfoProperties).doesNotContainKey("build.name"); + } + private Properties buildInfoProperties() { File file = new File(this.gradleBuild.getProjectDir(), "build/build-info.properties"); assertThat(file).isFile(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java index 61d2629dfd..441ebd4d24 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoTests.java @@ -38,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link BuildInfo}. * * @author Andy Wilkinson + * @author Vedran Pavic */ @ClassPathExclusions("kotlin-daemon-client-*") class BuildInfoTests { @@ -49,8 +50,8 @@ class BuildInfoTests { void basicExecution() { Properties properties = buildInfoProperties(createTask(createProject("test"))); assertThat(properties).containsKey("build.time"); - assertThat(properties).containsEntry("build.artifact", "unspecified"); - assertThat(properties).containsEntry("build.group", ""); + assertThat(properties).doesNotContainKey("build.artifact"); + assertThat(properties).doesNotContainKey("build.group"); assertThat(properties).containsEntry("build.name", "test"); assertThat(properties).containsEntry("build.version", "unspecified"); } @@ -62,6 +63,20 @@ class BuildInfoTests { assertThat(buildInfoProperties(task)).containsEntry("build.artifact", "custom"); } + @Test + void artifactCanBeRemovedFromPropertiesUsingNull() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setArtifact(null); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.artifact"); + } + + @Test + void artifactCanBeRemovedFromPropertiesUsingEmptyString() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setArtifact(""); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.artifact"); + } + @Test void projectGroupIsReflectedInProperties() { BuildInfo task = createTask(createProject("test")); @@ -76,6 +91,20 @@ class BuildInfoTests { assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example"); } + @Test + void groupCanBeRemovedFromPropertiesUsingNull() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setGroup(null); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.group"); + } + + @Test + void groupCanBeRemovedFromPropertiesUsingEmptyString() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setGroup(""); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.group"); + } + @Test void customNameIsReflectedInProperties() { BuildInfo task = createTask(createProject("test")); @@ -83,6 +112,20 @@ class BuildInfoTests { assertThat(buildInfoProperties(task)).containsEntry("build.name", "Example"); } + @Test + void nameCanBeRemovedFromPropertiesUsingNull() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setName(null); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.name"); + } + + @Test + void nameCanBeRemovedFromPropertiesUsingEmptyString() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setName(""); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.name"); + } + @Test void projectVersionIsReflectedInProperties() { BuildInfo task = createTask(createProject("test")); @@ -97,6 +140,20 @@ class BuildInfoTests { assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4"); } + @Test + void versionCanBeRemovedFromPropertiesUsingNull() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setVersion(null); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.version"); + } + + @Test + void versionCanBeRemovedFromPropertiesUsingEmptyString() { + BuildInfo task = createTask(createProject("test")); + task.getProperties().setVersion(""); + assertThat(buildInfoProperties(task)).doesNotContainKey("build.version"); + } + @Test void timeIsSetInProperties() { BuildInfo task = createTask(createProject("test")); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingEmptyStrings.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingEmptyStrings.gradle new file mode 100644 index 0000000000..f14ef603c3 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingEmptyStrings.gradle @@ -0,0 +1,16 @@ +plugins { + id 'org.springframework.boot' version '{version}' apply false +} + +group = 'foo' +version = '0.1.0' + +task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) { + destinationDir project.buildDir + properties { + group = '' + artifact = '' + version = '' + name = '' + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingNulls.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingNulls.gradle new file mode 100644 index 0000000000..0ec0aba36c --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests-removePropertiesUsingNulls.gradle @@ -0,0 +1,16 @@ +plugins { + id 'org.springframework.boot' version '{version}' apply false +} + +group = 'foo' +version = '0.1.0' + +task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) { + destinationDir project.buildDir + properties { + group = null + artifact = null + version = null + name = null + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java index 0ba85f4ecd..a250cee1bc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Properties; import org.springframework.core.CollectionFactory; +import org.springframework.util.StringUtils; /** * A {@code BuildPropertiesWriter} writes the {@code build-info.properties} for @@ -32,6 +33,7 @@ import org.springframework.core.CollectionFactory; * * @author Andy Wilkinson * @author Stephane Nicoll + * @author Vedran Pavic * @since 1.0.0 */ public final class BuildPropertiesWriter { @@ -71,10 +73,18 @@ public final class BuildPropertiesWriter { protected Properties createBuildInfo(ProjectDetails project) { Properties properties = CollectionFactory.createSortedProperties(true); - properties.put("build.group", project.getGroup()); - properties.put("build.artifact", project.getArtifact()); - properties.put("build.name", project.getName()); - properties.put("build.version", project.getVersion()); + if (StringUtils.hasText(project.getGroup())) { + properties.put("build.group", project.getGroup()); + } + if (StringUtils.hasText(project.getArtifact())) { + properties.put("build.artifact", project.getArtifact()); + } + if (StringUtils.hasText(project.getName())) { + properties.put("build.name", project.getName()); + } + if (StringUtils.hasText(project.getVersion())) { + properties.put("build.version", project.getVersion()); + } if (project.getTime() != null) { properties.put("build.time", DateTimeFormatter.ISO_INSTANT.format(project.getTime())); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java index bd28abeea9..26982cae39 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java @@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Integration tests for the Maven plugin's build info support. * * @author Andy Wilkinson + * @author Vedran Pavic */ @ExtendWith(MavenBuildExtension.class) class BuildInfoIntegrationTests { @@ -63,6 +64,14 @@ class BuildInfoIntegrationTests { .hasBuildTime("2019-07-08T08:00:00Z"))); } + @TestTemplate + void generatedBuildInfoUsesCustomBuildProperties(MavenBuild mavenBuild) { + mavenBuild.project("build-info-custom-build-properties") + .execute(buildInfo((buildInfo) -> assertThat(buildInfo).hasBuildGroup("test-group") + .hasBuildArtifact("test-artifact").hasBuildName("test-name").hasBuildVersion("test-version") + .containsBuildTime())); + } + @TestTemplate void generatedBuildInfoReproducible(MavenBuild mavenBuild) { mavenBuild.project("build-info-reproducible") @@ -89,6 +98,13 @@ class BuildInfoIntegrationTests { .doesNotContainBuildTime())); } + @TestTemplate + void whenBuildPropertiesAreEmptyTheyDoNotAppearInGeneratedBuildInfo(MavenBuild mavenBuild) { + mavenBuild.project("build-info-disable-build-properties").execute( + buildInfo((buildInfo) -> assertThat(buildInfo).doesNotContainBuildGroup().doesNotContainBuildArtifact() + .doesNotContainBuildName().doesNotContainBuildVersion().containsBuildTime())); + } + private ProjectCallback buildInfo(Consumer> buildInfo) { return buildInfo("target/classes/META-INF/build-info.properties", buildInfo); } @@ -130,18 +146,34 @@ class BuildInfoIntegrationTests { return containsEntry("build.group", expected); } + BuildInfoAssert doesNotContainBuildGroup() { + return doesNotContainKey("build.group"); + } + BuildInfoAssert hasBuildArtifact(String expected) { return containsEntry("build.artifact", expected); } + BuildInfoAssert doesNotContainBuildArtifact() { + return doesNotContainKey("build.artifact"); + } + BuildInfoAssert hasBuildName(String expected) { return containsEntry("build.name", expected); } + BuildInfoAssert doesNotContainBuildName() { + return doesNotContainKey("build.name"); + } + BuildInfoAssert hasBuildVersion(String expected) { return containsEntry("build.version", expected); } + BuildInfoAssert doesNotContainBuildVersion() { + return doesNotContainKey("build.version"); + } + BuildInfoAssert containsBuildTime() { return containsKey("build.time"); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml new file mode 100644 index 0000000000..d8fddf6223 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + org.springframework.boot.maven.it + build-info-custom-build-properties + 0.0.1.BUILD-SNAPSHOT + Generate build info with custom build properties + + UTF-8 + @java.version@ + @java.version@ + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + test-group + test-artifact + test-version + test-name + + + build-info + + + + + + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java new file mode 100644 index 0000000000..16c76e92c5 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.test; + +public class SampleApplication { + + public static void main(String[] args) { + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml new file mode 100644 index 0000000000..095515d796 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + org.springframework.boot.maven.it + build-info-disable-build-properties + 0.0.1.BUILD-SNAPSHOT + Generate build info with disabled build properties + + UTF-8 + @java.version@ + @java.version@ + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + off + off + off + off + + + build-info + + + + + + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java new file mode 100644 index 0000000000..16c76e92c5 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.test; + +public class SampleApplication { + + public static void main(String[] args) { + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java index 871d077e8c..a2a6d3c9d5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java @@ -41,6 +41,7 @@ import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetail * {@link MavenProject}. * * @author Stephane Nicoll + * @author Vedran Pavic * @since 1.4.0 */ @Mojo(name = "build-info", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true) @@ -55,18 +56,48 @@ public class BuildInfoMojo extends AbstractMojo { @Parameter(defaultValue = "${session}", readonly = true, required = true) private MavenSession session; - /** - * The Maven project. - */ - @Parameter(defaultValue = "${project}", readonly = true, required = true) - private MavenProject project; - /** * The location of the generated {@code build-info.properties} file. */ @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/build-info.properties") private File outputFile; + /** + * The value used for the {@code build.group} property. Defaults to + * {@code project.groupId}. To disable the {@code build.group} property entirely, use + * {@code 'off'}. + * @since 2.6.0 + */ + @Parameter(defaultValue = "${project.groupId}") + private String group; + + /** + * The value used for the {@code build.artifact} property. Defaults to + * {@code project.artifactId}. To disable the {@code build.artifact} property + * entirely, use {@code 'off'}. + * @since 2.6.0 + */ + @Parameter(defaultValue = "${project.artifactId}") + private String artifact; + + /** + * The value used for the {@code build.version} property. Defaults to + * {@code project.version}. To disable the {@code build.version} property entirely, + * use {@code 'off'}. + * @since 2.6.0 + */ + @Parameter(defaultValue = "${project.version}") + private String version; + + /** + * The value used for the {@code build.name} property. Defaults to + * {@code project.name}. To disable the {@code build.name} property entirely, use + * {@code 'off'}. + * @since 2.6.0 + */ + @Parameter(defaultValue = "${project.name}") + private String name; + /** * The value used for the {@code build.time} property in a form suitable for * {@link Instant#parse(CharSequence)}. Defaults to @@ -88,8 +119,8 @@ public class BuildInfoMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException, MojoFailureException { try { - ProjectDetails details = new ProjectDetails(this.project.getGroupId(), this.project.getArtifactId(), - this.project.getVersion(), this.project.getName(), getBuildTime(), this.additionalProperties); + ProjectDetails details = new ProjectDetails(getGroup(), getArtifact(), getVersion(), getName(), + getBuildTime(), this.additionalProperties); new BuildPropertiesWriter(this.outputFile).writeBuildProperties(details); this.buildContext.refresh(this.outputFile); } @@ -101,6 +132,34 @@ public class BuildInfoMojo extends AbstractMojo { } } + private String getGroup() { + if ("off".equalsIgnoreCase(this.group)) { + return null; + } + return this.group; + } + + private String getArtifact() { + if ("off".equalsIgnoreCase(this.artifact)) { + return null; + } + return this.artifact; + } + + private String getVersion() { + if ("off".equalsIgnoreCase(this.version)) { + return null; + } + return this.version; + } + + private String getName() { + if ("off".equalsIgnoreCase(this.name)) { + return null; + } + return this.name; + } + private Instant getBuildTime() { if (this.time == null || this.time.isEmpty()) { Date startTime = this.session.getRequest().getStartTime(); From f4bd8956afe5ffd3dd7fb668da8fa283661991e2 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 13 Oct 2021 14:04:13 -0700 Subject: [PATCH 2/2] Polish "Allow build info properties to be excluded" Update the Maven plugin to use an alternative syntax to exclude the info properties and apply some minor polishing. See gh-27412 --- .../gradle/tasks/buildinfo/BuildInfo.java | 7 +- .../loader/tools/BuildPropertiesWriter.java | 22 ++--- .../boot/maven/BuildInfoIntegrationTests.java | 20 ++-- .../pom.xml | 14 +-- .../main/java/org/test/SampleApplication.java | 0 .../pom.xml | 26 +++-- .../main/java/org/test/SampleApplication.java | 0 .../boot/maven/BuildInfoMojo.java | 95 +++++++------------ 8 files changed, 86 insertions(+), 98 deletions(-) rename spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/{build-info-disable-build-properties => build-info-exclude-build-properties}/pom.xml (70%) rename spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/{build-info-custom-build-properties => build-info-exclude-build-properties}/src/main/java/org/test/SampleApplication.java (100%) rename spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/{build-info-custom-build-properties => build-info-exclude-build-time}/pom.xml (54%) rename spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/{build-info-disable-build-properties => build-info-exclude-build-time}/src/main/java/org/test/SampleApplication.java (100%) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java index b5cd12aa3b..f48226e5d8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.java @@ -59,10 +59,11 @@ public class BuildInfo extends ConventionTask { @TaskAction public void generateBuildProperties() { try { + ProjectDetails details = new ProjectDetails(this.properties.getGroup(), this.properties.getArtifact(), + this.properties.getVersion(), this.properties.getName(), this.properties.getTime(), + coerceToStringValues(this.properties.getAdditional())); new BuildPropertiesWriter(new File(getDestinationDir(), "build-info.properties")) - .writeBuildProperties(new ProjectDetails(this.properties.getGroup(), this.properties.getArtifact(), - this.properties.getVersion(), this.properties.getName(), this.properties.getTime(), - coerceToStringValues(this.properties.getAdditional()))); + .writeBuildProperties(details); } catch (IOException ex) { throw new TaskExecutionException(this, ex); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java index a250cee1bc..fe103f2101 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/BuildPropertiesWriter.java @@ -73,18 +73,10 @@ public final class BuildPropertiesWriter { protected Properties createBuildInfo(ProjectDetails project) { Properties properties = CollectionFactory.createSortedProperties(true); - if (StringUtils.hasText(project.getGroup())) { - properties.put("build.group", project.getGroup()); - } - if (StringUtils.hasText(project.getArtifact())) { - properties.put("build.artifact", project.getArtifact()); - } - if (StringUtils.hasText(project.getName())) { - properties.put("build.name", project.getName()); - } - if (StringUtils.hasText(project.getVersion())) { - properties.put("build.version", project.getVersion()); - } + addIfHasValue(properties, "build.group", project.getGroup()); + addIfHasValue(properties, "build.artifact", project.getArtifact()); + addIfHasValue(properties, "build.name", project.getName()); + addIfHasValue(properties, "build.version", project.getVersion()); if (project.getTime() != null) { properties.put("build.time", DateTimeFormatter.ISO_INSTANT.format(project.getTime())); } @@ -94,6 +86,12 @@ public final class BuildPropertiesWriter { return properties; } + private void addIfHasValue(Properties properties, String name, String value) { + if (StringUtils.hasText(value)) { + properties.put(name, value); + } + } + /** * Build-system agnostic details of a project. */ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java index 26982cae39..3c648a0d98 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildInfoIntegrationTests.java @@ -64,14 +64,6 @@ class BuildInfoIntegrationTests { .hasBuildTime("2019-07-08T08:00:00Z"))); } - @TestTemplate - void generatedBuildInfoUsesCustomBuildProperties(MavenBuild mavenBuild) { - mavenBuild.project("build-info-custom-build-properties") - .execute(buildInfo((buildInfo) -> assertThat(buildInfo).hasBuildGroup("test-group") - .hasBuildArtifact("test-artifact").hasBuildName("test-name").hasBuildVersion("test-version") - .containsBuildTime())); - } - @TestTemplate void generatedBuildInfoReproducible(MavenBuild mavenBuild) { mavenBuild.project("build-info-reproducible") @@ -99,8 +91,16 @@ class BuildInfoIntegrationTests { } @TestTemplate - void whenBuildPropertiesAreEmptyTheyDoNotAppearInGeneratedBuildInfo(MavenBuild mavenBuild) { - mavenBuild.project("build-info-disable-build-properties").execute( + void whenBuildTimeIsExcludedIfDoesNotAppearInGeneratedBuildInfo(MavenBuild mavenBuild) { + mavenBuild.project("build-info-exclude-build-time").execute(buildInfo((buildInfo) -> assertThat(buildInfo) + .hasBuildGroup("org.springframework.boot.maven.it").hasBuildArtifact("build-info-exclude-build-time") + .hasBuildName("Generate build info with excluded build time").hasBuildVersion("0.0.1.BUILD-SNAPSHOT") + .doesNotContainBuildTime())); + } + + @TestTemplate + void whenBuildPropertiesAreExcludedTheyDoNotAppearInGeneratedBuildInfo(MavenBuild mavenBuild) { + mavenBuild.project("build-info-exclude-build-properties").execute( buildInfo((buildInfo) -> assertThat(buildInfo).doesNotContainBuildGroup().doesNotContainBuildArtifact() .doesNotContainBuildName().doesNotContainBuildVersion().containsBuildTime())); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-properties/pom.xml similarity index 70% rename from spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml rename to spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-properties/pom.xml index 095515d796..f58f33a49a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-properties/pom.xml @@ -3,9 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot.maven.it - build-info-disable-build-properties + build-info-exclude-build-properties 0.0.1.BUILD-SNAPSHOT - Generate build info with disabled build properties + Generate build info with excluded build properties UTF-8 @java.version@ @@ -20,10 +20,12 @@ - off - off - off - off + + group + artifact + version + name + build-info diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-properties/src/main/java/org/test/SampleApplication.java similarity index 100% rename from spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/src/main/java/org/test/SampleApplication.java rename to spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-properties/src/main/java/org/test/SampleApplication.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-time/pom.xml similarity index 54% rename from spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml rename to spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-time/pom.xml index d8fddf6223..e2d702508e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-custom-build-properties/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-time/pom.xml @@ -1,11 +1,11 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot.maven.it - build-info-custom-build-properties + build-info-exclude-build-time 0.0.1.BUILD-SNAPSHOT - Generate build info with custom build properties + Generate build info with excluded build time UTF-8 @java.version@ @@ -20,10 +20,9 @@ - test-group - test-artifact - test-version - test-name + + time + build-info @@ -33,4 +32,17 @@ + + + org.springframework + spring-context + @spring-framework.version@ + + + jakarta.servlet + jakarta.servlet-api + @jakarta-servlet.version@ + provided + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-time/src/main/java/org/test/SampleApplication.java similarity index 100% rename from spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-disable-build-properties/src/main/java/org/test/SampleApplication.java rename to spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-info-exclude-build-time/src/main/java/org/test/SampleApplication.java diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java index a2a6d3c9d5..c4b3a111ae 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildInfoMojo.java @@ -19,6 +19,8 @@ package org.springframework.boot.maven; import java.io.File; import java.time.Instant; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.apache.maven.execution.MavenSession; @@ -56,54 +58,24 @@ public class BuildInfoMojo extends AbstractMojo { @Parameter(defaultValue = "${session}", readonly = true, required = true) private MavenSession session; + /** + * The Maven project. + */ + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + /** * The location of the generated {@code build-info.properties} file. */ @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/build-info.properties") private File outputFile; - /** - * The value used for the {@code build.group} property. Defaults to - * {@code project.groupId}. To disable the {@code build.group} property entirely, use - * {@code 'off'}. - * @since 2.6.0 - */ - @Parameter(defaultValue = "${project.groupId}") - private String group; - - /** - * The value used for the {@code build.artifact} property. Defaults to - * {@code project.artifactId}. To disable the {@code build.artifact} property - * entirely, use {@code 'off'}. - * @since 2.6.0 - */ - @Parameter(defaultValue = "${project.artifactId}") - private String artifact; - - /** - * The value used for the {@code build.version} property. Defaults to - * {@code project.version}. To disable the {@code build.version} property entirely, - * use {@code 'off'}. - * @since 2.6.0 - */ - @Parameter(defaultValue = "${project.version}") - private String version; - - /** - * The value used for the {@code build.name} property. Defaults to - * {@code project.name}. To disable the {@code build.name} property entirely, use - * {@code 'off'}. - * @since 2.6.0 - */ - @Parameter(defaultValue = "${project.name}") - private String name; - /** * The value used for the {@code build.time} property in a form suitable for * {@link Instant#parse(CharSequence)}. Defaults to * {@code project.build.outputTimestamp} or {@code session.request.startTime} if the * former is not set. To disable the {@code build.time} property entirely, use - * {@code 'off'}. + * {@code 'off'} or add it to {@code excludeInfoProperties}. * @since 2.2.0 */ @Parameter(defaultValue = "${project.build.outputTimestamp}") @@ -116,11 +88,19 @@ public class BuildInfoMojo extends AbstractMojo { @Parameter private Map additionalProperties; + /** + * Properties that should be excluded {@code build-info.properties} file. Can be used + * to exclude the standard {@code group}, {@code artifact}, {@code name}, + * {@code version} or {@code time} properties as well as items from + * {@code additionalProperties}. + */ + @Parameter + private List excludeInfoProperties; + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { - ProjectDetails details = new ProjectDetails(getGroup(), getArtifact(), getVersion(), getName(), - getBuildTime(), this.additionalProperties); + ProjectDetails details = getProjectDetails(); new BuildPropertiesWriter(this.outputFile).writeBuildProperties(details); this.buildContext.refresh(this.outputFile); } @@ -132,32 +112,27 @@ public class BuildInfoMojo extends AbstractMojo { } } - private String getGroup() { - if ("off".equalsIgnoreCase(this.group)) { - return null; - } - return this.group; + private ProjectDetails getProjectDetails() { + String group = getIfNotExcluded("group", this.project.getGroupId()); + String artifact = getIfNotExcluded("artifact", this.project.getArtifactId()); + String version = getIfNotExcluded("version", this.project.getVersion()); + String name = getIfNotExcluded("name", this.project.getName()); + Instant time = getIfNotExcluded("time", getBuildTime()); + Map additionalProperties = applyExclusions(this.additionalProperties); + return new ProjectDetails(group, artifact, version, name, time, additionalProperties); } - private String getArtifact() { - if ("off".equalsIgnoreCase(this.artifact)) { - return null; - } - return this.artifact; + private T getIfNotExcluded(String name, T value) { + return (this.excludeInfoProperties == null || !this.excludeInfoProperties.contains(name)) ? value : null; } - private String getVersion() { - if ("off".equalsIgnoreCase(this.version)) { - return null; + private Map applyExclusions(Map source) { + if (source == null || this.excludeInfoProperties == null) { + return source; } - return this.version; - } - - private String getName() { - if ("off".equalsIgnoreCase(this.name)) { - return null; - } - return this.name; + Map result = new LinkedHashMap<>(source); + this.excludeInfoProperties.forEach(result::remove); + return result; } private Instant getBuildTime() {