Rework BuildInfo to separate task and build info properties

Previously, the properties that applied to the BuildInfo task itself
and those that would be written into the build-info.properties file
were all configured on BuildInfo directly. This lack of separation
could be confusing.

This commit rework BuildInfo to separate the task's own properties
from those that are written into the build-info.properties file.

The task has also been updated so that changes to a project's
properties made after the task has been configured are reflected in
the build info properties.
pull/8686/merge
Andy Wilkinson 8 years ago
parent 201ea133e1
commit b9b402e3db

@ -27,6 +27,7 @@ import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo;
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfoProperties;
/**
* Entry point to Spring Boot's Gradle DSL.
@ -76,8 +77,12 @@ public class SpringBootExtension {
this.project.getPlugins().withType(JavaPlugin.class, plugin -> {
this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME)
.dependsOn(bootBuildInfo);
bootBuildInfo.getConventionMapping().map("projectArtifact",
(Callable<String>) () -> determineArtifactBaseName());
this.project.afterEvaluate(evaluated -> {
BuildInfoProperties properties = bootBuildInfo.getProperties();
if (properties.getArtifact() == null) {
properties.setArtifact(determineArtifactBaseName());
}
});
bootBuildInfo.getConventionMapping()
.map("destinationDir",
(Callable<File>) () -> new File(

@ -22,6 +22,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.internal.ConventionTask;
import org.gradle.api.tasks.Input;
@ -34,92 +36,77 @@ import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetail
/**
* {@link Task} for generating a {@code build-info.properties} file from a
* {@code Project}. The {@link #setDestinationDir destination dir} and
* {@link #setProjectArtifact project artifact} must be configured before execution.
* {@code Project}.
*
* @author Andy Wilkinson
*/
public class BuildInfo extends ConventionTask {
private File destinationDir;
private String projectGroup = getProject().getGroup().toString();
private String projectArtifact;
private String projectVersion = getProject().getVersion().toString();
private String projectName = getProject().getName();
private final BuildInfoProperties properties = new BuildInfoProperties(getProject());
private Map<String, Object> additionalProperties = new HashMap<>();
private File destinationDir;
/**
* Generates the {@code build-info.properties} file in the configured
* {@link #setDestinationDir(File) destination}.
*/
@TaskAction
public void generateBuildProperties() {
try {
new BuildPropertiesWriter(
new File(getDestinationDir(), "build-info.properties"))
.writeBuildProperties(new ProjectDetails(this.projectGroup,
getProjectArtifact(), this.projectVersion,
this.projectName,
coerceToStringValues(this.additionalProperties)));
.writeBuildProperties(new ProjectDetails(
this.properties.getGroup(),
this.properties.getArtifact() == null ? "unspecified"
: this.properties.getArtifact(),
this.properties.getVersion(),
this.properties.getName(), coerceToStringValues(
this.properties.getAdditional())));
}
catch (IOException ex) {
throw new TaskExecutionException(this, ex);
}
}
@Input
public String getProjectGroup() {
return this.projectGroup;
}
public void setProjectGroup(String projectGroup) {
this.projectGroup = projectGroup;
}
@Input
public String getProjectArtifact() {
return this.projectArtifact;
}
public void setProjectArtifact(String projectArtifact) {
this.projectArtifact = projectArtifact;
}
@Input
public String getProjectVersion() {
return this.projectVersion;
}
public void setProjectVersion(String projectVersion) {
this.projectVersion = projectVersion;
}
@Input
public String getProjectName() {
return this.projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
/**
* Returns the directory to which the {@code build-info.properties} file will be
* written. Defaults to the {@link Project#getBuildDir() Project's build directory}.
*
* @return the destination directory
*/
@OutputDirectory
public File getDestinationDir() {
return this.destinationDir;
return this.destinationDir != null ? this.destinationDir
: getProject().getBuildDir();
}
/**
* Sets the directory to which the {@code build-info.properties} file will be written.
*
* @param destinationDir the destination directory
*/
public void setDestinationDir(File destinationDir) {
this.destinationDir = destinationDir;
}
/**
* Returns the {@link BuildInfoProperties properties} that will be included in the
* {@code build-info.properties} file.
*
* @return the properties
*/
@Input
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
public BuildInfoProperties getProperties() {
return this.properties;
}
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
/**
* Executes the given {@code action} on the {@link #getProperties()} properties.
*
* @param action the action
*/
public void properties(Action<BuildInfoProperties> action) {
action.execute(this.properties);
}
private Map<String, String> coerceToStringValues(Map<String, Object> input) {

@ -0,0 +1,212 @@
/*
* Copyright 2012-2017 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
*
* http://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.springframework.boot.gradle.tasks.buildinfo;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.gradle.api.Project;
/**
* The properties that are written into the {@code build-info.properties} file.
*
* @author Andy Wilkinson
*/
public class BuildInfoProperties implements Serializable {
private final transient Project project;
private String group;
private String artifact;
private String version;
private String name;
private Map<String, Object> additionalProperties = new HashMap<>();
BuildInfoProperties(Project project) {
this.project = project;
}
/**
* Returns the value used for the {@code build.group} property. Defaults to the
* {@link Project#getGroup() Project's group}.
*
* @return the group
*/
public String getGroup() {
return this.group != null ? this.group : this.project.getGroup().toString();
}
/**
* Sets the value used for the {@code build.group} property.
*
* @param group the group name
*/
public void setGroup(String group) {
this.group = group;
}
/**
* Returns the value used for the {@code build.artifact} property.
*
* @return the artifact
*/
public String getArtifact() {
return this.artifact;
}
/**
* Sets the value used for the {@code build.artifact} property.
*
* @param artifact the artifact
*/
public void setArtifact(String artifact) {
this.artifact = artifact;
}
/**
* Returns the value used for the {@code build.version} property. Defaults to the
* {@link Project#getVersion() Project's version}.
*
* @return the version
*/
public String getVersion() {
return this.version != null ? this.version : this.project.getVersion().toString();
}
/**
* Sets the value used for the {@code build.version} property.
*
* @param version the version
*/
public void setVersion(String version) {
this.version = version;
}
/**
* Returns the value used for the {@code build.name} property. Defaults to the
* {@link Project#getDisplayName() Project's display name}.
*
* @return the name
*/
public String getName() {
return this.name != null ? this.name : this.project.getName();
}
/**
* Sets the value used for the {@code build.name} property.
*
* @param name the name
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns the additional properties that will be included. When written, the name of
* each additional property is prefixed with {@code build.}.
*
* @return the additional properties
*/
public Map<String, Object> getAdditional() {
return this.additionalProperties;
}
/**
* Sets the additional properties that will be included. When written, the name of
* each additional property is prefixed with {@code build.}.
*
* @param additionalProperties the additional properties
*/
public void setAdditional(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.additionalProperties == null) ? 0
: this.additionalProperties.hashCode());
result = prime * result
+ ((this.artifact == null) ? 0 : this.artifact.hashCode());
result = prime * result + ((this.group == null) ? 0 : this.group.hashCode());
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
result = prime * result + ((this.version == null) ? 0 : this.version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
BuildInfoProperties other = (BuildInfoProperties) obj;
if (this.additionalProperties == null) {
if (other.additionalProperties != null) {
return false;
}
}
else if (!this.additionalProperties.equals(other.additionalProperties)) {
return false;
}
if (this.artifact == null) {
if (other.artifact != null) {
return false;
}
}
else if (!this.artifact.equals(other.artifact)) {
return false;
}
if (this.group == null) {
if (other.group != null) {
return false;
}
}
else if (!this.group.equals(other.group)) {
return false;
}
if (this.name == null) {
if (other.name != null) {
return false;
}
}
else if (!this.name.equals(other.name)) {
return false;
}
if (this.version == null) {
if (other.version != null) {
return false;
}
}
else if (!this.version.equals(other.version)) {
return false;
}
return true;
}
}

@ -16,6 +16,11 @@
package org.springframework.boot.gradle.tasks.buildinfo;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
@ -34,10 +39,30 @@ public class BuildInfoIntegrationTests {
@Rule
public final GradleBuild gradleBuild = new GradleBuild();
@Test
public void defaultValues() {
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).containsEntry("build.name",
this.gradleBuild.getProjectDir().getName());
assertThat(buildInfoProperties).containsEntry("build.version", "unspecified");
}
@Test
public void basicExecution() {
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome())
.isEqualTo(TaskOutcome.SUCCESS);
Properties buildInfoProperties = buildInfoProperties();
assertThat(buildInfoProperties).containsKey("build.time");
assertThat(buildInfoProperties).containsEntry("build.artifact", "foo");
assertThat(buildInfoProperties).containsEntry("build.group", "foo");
assertThat(buildInfoProperties).containsEntry("build.additional", "foo");
assertThat(buildInfoProperties).containsEntry("build.name", "foo");
assertThat(buildInfoProperties).containsEntry("build.version", "1.0");
}
@Test
@ -55,34 +80,48 @@ public class BuildInfoIntegrationTests {
@Test
public void notUpToDateWhenProjectArtifactChanges() {
notUpToDateWithChangeToProperty("buildInfoProjectArtifact");
notUpToDateWithChangeToProperty("buildInfoArtifact");
}
@Test
public void notUpToDateWhenProjectGroupChanges() {
notUpToDateWithChangeToProperty("buildInfoProjectGroup");
notUpToDateWithChangeToProperty("buildInfoGroup");
}
@Test
public void notUpToDateWhenProjectVersionChanges() {
notUpToDateWithChangeToProperty("buildInfoProjectVersion");
notUpToDateWithChangeToProperty("buildInfoVersion");
}
@Test
public void notUpToDateWhenProjectNameChanges() {
notUpToDateWithChangeToProperty("buildInfoProjectName");
notUpToDateWithChangeToProperty("buildInfoName");
}
@Test
public void notUpToDateWhenAdditionalPropertyChanges() {
notUpToDateWithChangeToProperty("buildInfoAdditionalProperty");
notUpToDateWithChangeToProperty("buildInfoAdditional");
}
private void notUpToDateWithChangeToProperty(String name) {
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome())
.isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.build("buildInfo", "--stacktrace").task(":buildInfo")
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.build("buildInfo", "-P" + name + "=changed")
.task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}
private Properties buildInfoProperties() {
File file = new File(this.gradleBuild.getProjectDir(),
"build/build-info.properties");
assertThat(file).isFile();
Properties properties = new Properties();
try (FileReader reader = new FileReader(file)) {
properties.load(reader);
return properties;
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

@ -0,0 +1,136 @@
/*
* Copyright 2012-2017 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
*
* http://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.springframework.boot.gradle.tasks.buildinfo;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import org.gradle.api.Project;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link BuildInfo}.
*
* @author Andy Wilkinson
*/
public class BuildInfoTests {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Test
public 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).containsEntry("build.name", "test");
assertThat(properties).containsEntry("build.version", "unspecified");
}
@Test
public void customArtifactIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().setArtifact("custom");
assertThat(buildInfoProperties(task)).containsEntry("build.artifact", "custom");
}
@Test
public void projectGroupIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProject().setGroup("com.example");
assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example");
}
@Test
public void customGroupIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().setGroup("com.example");
assertThat(buildInfoProperties(task)).containsEntry("build.group", "com.example");
}
@Test
public void customNameIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().setName("Example");
assertThat(buildInfoProperties(task)).containsEntry("build.name", "Example");
}
@Test
public void projectVersionIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProject().setVersion("1.2.3");
assertThat(buildInfoProperties(task)).containsEntry("build.version", "1.2.3");
}
@Test
public void customVersionIsReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().setVersion("2.3.4");
assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4");
}
@Test
public void additionalPropertiesAreReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().getAdditional().put("a", "alpha");
task.getProperties().getAdditional().put("b", "bravo");
assertThat(buildInfoProperties(task)).containsEntry("build.a", "alpha");
assertThat(buildInfoProperties(task)).containsEntry("build.b", "bravo");
}
private Project createProject(String projectName) {
try {
File projectDir = this.temp.newFolder(projectName);
return ProjectBuilder.builder().withProjectDir(projectDir)
.withName(projectName).build();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private BuildInfo createTask(Project project) {
return project.getTasks().create("testBuildInfo", BuildInfo.class);
}
private Properties buildInfoProperties(BuildInfo task) {
task.generateBuildProperties();
return buildInfoProperties(
new File(task.getDestinationDir(), "build-info.properties"));
}
private Properties buildInfoProperties(File file) {
assertThat(file).isFile();
Properties properties = new Properties();
try (FileReader reader = new FileReader(file)) {
properties.load(reader);
return properties;
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

@ -12,6 +12,10 @@ version = '1.0'
springBoot {
buildInfo {
additionalProperties 'a': 'alpha', 'b': 'bravo'
properties {
additionalProperties = [
'a': 'alpha', 'b': 'bravo'
]
}
}
}

@ -0,0 +1,11 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
def property(String name, Object defaultValue) {
project.hasProperty(name) ? project.getProperty(name) : defaultValue
}
task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo)

@ -10,9 +10,11 @@ def property(String name, Object defaultValue) {
task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) {
destinationDir file(property('buildInfoDestinationDir', project.buildDir))
projectArtifact property('buildInfoProjectArtifact', 'foo')
projectVersion property('buildInfoProjectVersion', '1.0')
projectGroup property('buildInfoProjectGroup', 'foo')
projectName property('buildInfoProjectName', 'foo')
additionalProperties 'additional': property('buildInfoAdditionalProperty', 'foo')
properties {
artifact = property('buildInfoArtifact', 'foo')
version = property('buildInfoVersion', '1.0')
group = property('buildInfoGroup', 'foo')
name = property('buildInfoName', 'foo')
additional = ['additional': property('buildInfoAdditional', 'foo')]
}
}

Loading…
Cancel
Save