diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc index b0b7bd88ae0..8577974b88e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/integrating-with-actuator.adoc @@ -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"] 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 415fa3350f2..1088319a836 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 @@ -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) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java index 2faedab6def..322686b18cb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoProperties.java @@ -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 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; } 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 2e0a8eb6fb6..e18ffcc8d20 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 @@ -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); - } - - @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"); + .isEqualTo(TaskOutcome.SUCCESS); } @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() { 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 f459919e619..9cbb386acee 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 @@ -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")); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle index 26ce586d5b5..e97674aee0b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/buildinfo/BuildInfoIntegrationTests.gradle @@ -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 + } } } 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 c3367fdc3d1..0f67a728192 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-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 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 additionalProperties; public ProjectDetails(String group, String artifact, String version, String name, - Map additionalProperties) { + Instant time, Map 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 getAdditionalProperties() { return this.additionalProperties; } 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 f016d4f316d..96ca7d647c4 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 @@ -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) {