Consider time in BuildInfo up-to-date checks and allow it to be set

Closes gh-12111
Closes gh-12266
This commit is contained in:
Andy Wilkinson
2018-02-27 19:58:17 +00:00
parent 6b3b7909f3
commit fdb9a1ea8f
8 changed files with 96 additions and 49 deletions

View File

@@ -38,6 +38,9 @@ By default, the generated build information is derived from the project:
| `build.version`
| The version of the project
| `build.time`
| The time at which the project is being built
|===
The properties can be customized using the DSL:
@@ -47,6 +50,13 @@ The properties can be customized using the DSL:
include::../gradle/integrating-with-actuator/build-info-custom-values.gradle[tags=custom-values]
----
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 and, therefore, builds will
take slighly longer as more tasks will have to be executed. Another side-effect is that
the task's output will always change and, therefore, the build will not be truly
repeatable. If you value build performance or repeatability more highly than the accuracy
of the `build.time` property, set `time` to `null` or a fixed value.
Additional properties can also be added to the build information:
[source,groovy,indent=0,subs="verbatim"]

View File

@@ -61,7 +61,8 @@ public class BuildInfo extends ConventionTask {
? "unspecified"
: this.properties.getArtifact(),
this.properties.getVersion(),
this.properties.getName(), coerceToStringValues(
this.properties.getName(), this.properties.getTime(),
coerceToStringValues(
this.properties.getAdditional())));
}
catch (IOException ex) {

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.gradle.tasks.buildinfo;
import java.io.Serializable;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
@@ -40,10 +41,13 @@ public class BuildInfoProperties implements Serializable {
private String name;
private Instant time;
private Map<String, Object> additionalProperties = new HashMap<>();
BuildInfoProperties(Project project) {
this.project = project;
this.time = Instant.now();
}
/**
@@ -121,6 +125,23 @@ public class BuildInfoProperties implements Serializable {
this.name = name;
}
/**
* Returns the value used for the {@code build.time} property. Defaults to
* {@link Instant#now} when the {@code BuildInfoProperties} instance was created.
* @return the time
*/
public Instant getTime() {
return this.time;
}
/**
* Sets the value used for the {@code build.time} property.
* @param time the build time
*/
public void setTime(Instant time) {
this.time = time;
}
/**
* Returns the additional properties that will be included. When written, the name of
* each additional property is prefixed with {@code build.}.
@@ -152,6 +173,7 @@ public class BuildInfoProperties implements Serializable {
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());
result = prime * result + ((this.time == null) ? 0 : this.time.hashCode());
return result;
}
@@ -207,6 +229,14 @@ public class BuildInfoProperties implements Serializable {
else if (!this.version.equals(other.version)) {
return false;
}
if (this.time == null) {
if (other.time != null) {
return false;
}
}
else if (!this.time.equals(other.time)) {
return false;
}
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -69,48 +69,19 @@ public class BuildInfoIntegrationTests {
}
@Test
public void upToDateWhenExecutedTwice() {
public void notUpToDateWhenExecutedTwiceAsTimeChanges() {
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome())
.isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.build("buildInfo").task(":buildInfo").getOutcome())
.isEqualTo(TaskOutcome.UP_TO_DATE);
.isEqualTo(TaskOutcome.SUCCESS);
}
@Test
public void notUpToDateWhenDestinationDirChanges() {
notUpToDateWithChangeToProperty("buildInfoDestinationDir");
}
@Test
public void notUpToDateWhenProjectArtifactChanges() {
notUpToDateWithChangeToProperty("buildInfoArtifact");
}
@Test
public void notUpToDateWhenProjectGroupChanges() {
notUpToDateWithChangeToProperty("buildInfoGroup");
}
@Test
public void notUpToDateWhenProjectVersionChanges() {
notUpToDateWithChangeToProperty("buildInfoVersion");
}
@Test
public void notUpToDateWhenProjectNameChanges() {
notUpToDateWithChangeToProperty("buildInfoName");
}
@Test
public void notUpToDateWhenAdditionalPropertyChanges() {
notUpToDateWithChangeToProperty("buildInfoAdditional");
}
private void notUpToDateWithChangeToProperty(String name) {
assertThat(this.gradleBuild.build("buildInfo", "--stacktrace").task(":buildInfo")
public void upToDateWhenExecutedTwiceWithFixedTime() {
assertThat(this.gradleBuild.build("buildInfo", "-PnullTime").task(":buildInfo")
.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.build("buildInfo", "-P" + name + "=changed")
.task(":buildInfo").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(this.gradleBuild.build("buildInfo", "-PnullTime").task(":buildInfo")
.getOutcome()).isEqualTo(TaskOutcome.UP_TO_DATE);
}
private Properties buildInfoProperties() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -19,6 +19,8 @@ package org.springframework.boot.gradle.tasks.buildinfo;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import org.gradle.api.Project;
@@ -91,6 +93,29 @@ public class BuildInfoTests {
assertThat(buildInfoProperties(task)).containsEntry("build.version", "2.3.4");
}
@Test
public void timeIsSetInProperties() {
BuildInfo task = createTask(createProject("test"));
assertThat(buildInfoProperties(task)).containsEntry("build.time",
DateTimeFormatter.ISO_INSTANT.format(task.getProperties().getTime()));
}
@Test
public void timeCanBeRemovedFromProperties() {
BuildInfo task = createTask(createProject("test"));
task.getProperties().setTime(null);
assertThat(buildInfoProperties(task)).doesNotContainKey("build.time");
}
@Test
public void timeCanBeCustomizedInProperties() {
Instant now = Instant.now();
BuildInfo task = createTask(createProject("test"));
task.getProperties().setTime(now);
assertThat(buildInfoProperties(task)).containsEntry("build.time",
DateTimeFormatter.ISO_INSTANT.format(now));
}
@Test
public void additionalPropertiesAreReflectedInProperties() {
BuildInfo task = createTask(createProject("test"));

View File

@@ -16,5 +16,8 @@ task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo)
group = property('buildInfoGroup', 'foo')
name = property('buildInfoName', 'foo')
additional = ['additional': property('buildInfoAdditional', 'foo')]
if (project.hasProperty('nullTime')) {
time = null
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -19,8 +19,8 @@ package org.springframework.boot.loader.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
@@ -74,7 +74,10 @@ public final class BuildPropertiesWriter {
properties.put("build.artifact", project.getArtifact());
properties.put("build.name", project.getName());
properties.put("build.version", project.getVersion());
properties.put("build.time", formatDate(new Date()));
if (project.getTime() != null) {
properties.put("build.time",
DateTimeFormatter.ISO_INSTANT.format(project.getTime()));
}
if (project.getAdditionalProperties() != null) {
for (Map.Entry<String, String> entry : project.getAdditionalProperties()
.entrySet()) {
@@ -84,11 +87,6 @@ public final class BuildPropertiesWriter {
return properties;
}
private String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
return sdf.format(date);
}
/**
* Build-system agnostic details of a project.
*/
@@ -102,14 +100,17 @@ public final class BuildPropertiesWriter {
private final String version;
private final Instant time;
private final Map<String, String> additionalProperties;
public ProjectDetails(String group, String artifact, String version, String name,
Map<String, String> additionalProperties) {
Instant time, Map<String, String> additionalProperties) {
this.group = group;
this.artifact = artifact;
this.name = name;
this.version = version;
this.time = time;
validateAdditionalProperties(additionalProperties);
this.additionalProperties = additionalProperties;
}
@@ -141,6 +142,10 @@ public final class BuildPropertiesWriter {
return this.version;
}
public Instant getTime() {
return this.time;
}
public Map<String, String> getAdditionalProperties() {
return this.additionalProperties;
}

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.maven;
import java.io.File;
import java.time.Instant;
import java.util.Map;
import org.apache.maven.plugin.AbstractMojo;
@@ -71,7 +72,8 @@ public class BuildInfoMojo extends AbstractMojo {
new BuildPropertiesWriter(this.outputFile)
.writeBuildProperties(new ProjectDetails(this.project.getGroupId(),
this.project.getArtifactId(), this.project.getVersion(),
this.project.getName(), this.additionalProperties));
this.project.getName(), Instant.now(),
this.additionalProperties));
this.buildContext.refresh(this.outputFile);
}
catch (NullAdditionalPropertyValueException ex) {