From de65c0dae30281cf02741276e3b53db17afe19f7 Mon Sep 17 00:00:00 2001 From: Carsten Reckord Date: Tue, 19 Jul 2022 16:07:37 +0200 Subject: [PATCH 1/3] Use Gradle Property and Provider for jib.container.creationTime and jib.container.filesModificationTime (#3708) --- .../tools/jib/gradle/ContainerParameters.java | 24 ++++++++++++++----- .../cloud/tools/jib/gradle/JibPluginTest.java | 8 +++++++ .../projects/lazy-evaluation/build.gradle | 13 ++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java index f8a489b398..f0a9d26873 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java @@ -27,6 +27,8 @@ import javax.inject.Inject; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; @@ -50,12 +52,14 @@ public class ContainerParameters { private String appRoot = ""; @Nullable private String user; @Nullable private String workingDirectory; - private String filesModificationTime = "EPOCH_PLUS_SECOND"; - private String creationTime = "EPOCH"; + private Property filesModificationTime; + private Property creationTime; @Inject public ContainerParameters(ObjectFactory objectFactory) { labels = objectFactory.mapProperty(String.class, String.class).empty(); + filesModificationTime = objectFactory.property(String.class).value("EPOCH_PLUS_SECOND"); + creationTime = objectFactory.property(String.class).value("EPOCH"); } @Input @@ -266,11 +270,15 @@ public String getFilesModificationTime() { if (System.getProperty(PropertyNames.CONTAINER_FILES_MODIFICATION_TIME) != null) { return System.getProperty(PropertyNames.CONTAINER_FILES_MODIFICATION_TIME); } - return filesModificationTime; + return filesModificationTime.getOrNull(); + } + + public void setFilesModificationTime(Provider filesModificationTime) { + this.filesModificationTime.set(filesModificationTime); } public void setFilesModificationTime(String filesModificationTime) { - this.filesModificationTime = filesModificationTime; + this.filesModificationTime.set(filesModificationTime); } @Input @@ -279,10 +287,14 @@ public String getCreationTime() { if (System.getProperty(PropertyNames.CONTAINER_CREATION_TIME) != null) { return System.getProperty(PropertyNames.CONTAINER_CREATION_TIME); } - return creationTime; + return creationTime.getOrNull(); + } + + public void setCreationTime(Provider creationTime) { + this.creationTime.set(creationTime); } public void setCreationTime(String creationTime) { - this.creationTime = creationTime; + this.creationTime.set(creationTime); } } diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibPluginTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibPluginTest.java index 7ba5ed904c..2ccb105f08 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibPluginTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibPluginTest.java @@ -399,6 +399,14 @@ public void testLazyEvalForExtraDirectories_individualPaths() throws IOException .contains("extraDirectories (excludes): [[exclude.txt]]"); } + @Test + public void testLazyEvalForContainerCreationAndFileModificationTimes() { + BuildResult showTimes = testProject.build("showtimes", "-Djib.console=plain"); + String output = showTimes.getOutput(); + assertThat(output).contains("creationTime=2022-07-19T10:23:42Z"); + assertThat(output).contains("filesModificationTime=2022-07-19T11:23:42Z"); + } + private Project createProject(String... plugins) { Project project = ProjectBuilder.builder().withProjectDir(testProjectRoot.getRoot()).withName("root").build(); diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle b/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle index ed358e4008..c3e041af5d 100644 --- a/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle @@ -15,10 +15,14 @@ dependencies { } project.ext.value = 'original' +project.ext.jibCreationTime = '1970-01-23T00:23:42Z' +project.ext.jibFilesModificationTime = '1970-01-23T01:23:42Z' project.afterEvaluate { project.ext.value = 'updated' project.ext.getCustomPermissions = { -> return ['/updated': '755'] } + project.ext.jibCreationTime = '2022-07-19T10:23:42Z' + project.ext.jibFilesModificationTime = '2022-07-19T11:23:42Z' } jib { @@ -33,6 +37,8 @@ jib { secondKey: project.ext.value + '-second-label' ] } + creationTime = project.provider { project.ext.jibCreationTime } + filesModificationTime = project.provider { project.ext.jibFilesModificationTime } } extraDirectories { paths = project.provider { ['src/main/' + project.ext.value + '-custom-extra-dir'] } @@ -51,3 +57,10 @@ tasks.register('check-extra-directories') { println('extraDirectories paths: ' + paths) println('extraDirectories permissions: ' + permissions) } + +tasks.register('showtimes') { + String prop = project.extensions.getByName('jib')['container']['creationTime'] + println('creationTime=' + prop) + prop = project.extensions.getByName('jib')['container']['filesModificationTime'] + println('filesModificationTime=' + prop) +} From c9aee5ab64a5aa672a2122d1ee89b6f909c73864 Mon Sep 17 00:00:00 2001 From: Carsten Reckord Date: Wed, 21 Sep 2022 13:59:21 +0200 Subject: [PATCH 2/3] Replace container creationTime and filesModificationTime getters/setters with Property (#3708) * Removes String getters/setters * Adds getter for Property instead --- .../tools/jib/gradle/ContainerParameters.java | 43 ++++++------------ .../jib/gradle/GradleRawConfiguration.java | 4 +- .../gradle/GradleRawConfigurationTest.java | 9 +++- .../tools/jib/gradle/JibExtensionTest.java | 45 ++++++++++++++++--- .../projects/lazy-evaluation/build.gradle | 4 +- 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java index f0a9d26873..5ebf8b5de1 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java @@ -28,7 +28,6 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; @@ -52,14 +51,14 @@ public class ContainerParameters { private String appRoot = ""; @Nullable private String user; @Nullable private String workingDirectory; - private Property filesModificationTime; - private Property creationTime; + private final Property filesModificationTime; + private final Property creationTime; @Inject public ContainerParameters(ObjectFactory objectFactory) { labels = objectFactory.mapProperty(String.class, String.class).empty(); - filesModificationTime = objectFactory.property(String.class).value("EPOCH_PLUS_SECOND"); - creationTime = objectFactory.property(String.class).value("EPOCH"); + filesModificationTime = objectFactory.property(String.class).convention("EPOCH_PLUS_SECOND"); + creationTime = objectFactory.property(String.class).convention("EPOCH"); } @Input @@ -266,35 +265,21 @@ public void setWorkingDirectory(String workingDirectory) { @Input @Optional - public String getFilesModificationTime() { - if (System.getProperty(PropertyNames.CONTAINER_FILES_MODIFICATION_TIME) != null) { - return System.getProperty(PropertyNames.CONTAINER_FILES_MODIFICATION_TIME); + public Property getFilesModificationTime() { + String property = System.getProperty(PropertyNames.CONTAINER_FILES_MODIFICATION_TIME); + if (property != null && !property.equals(filesModificationTime.get())) { + filesModificationTime.set(property); } - return filesModificationTime.getOrNull(); - } - - public void setFilesModificationTime(Provider filesModificationTime) { - this.filesModificationTime.set(filesModificationTime); - } - - public void setFilesModificationTime(String filesModificationTime) { - this.filesModificationTime.set(filesModificationTime); + return filesModificationTime; } @Input @Optional - public String getCreationTime() { - if (System.getProperty(PropertyNames.CONTAINER_CREATION_TIME) != null) { - return System.getProperty(PropertyNames.CONTAINER_CREATION_TIME); + public Property getCreationTime() { + String property = System.getProperty(PropertyNames.CONTAINER_CREATION_TIME); + if (property != null && !property.equals(creationTime.get())) { + creationTime.set(property); } - return creationTime.getOrNull(); - } - - public void setCreationTime(Provider creationTime) { - this.creationTime.set(creationTime); - } - - public void setCreationTime(String creationTime) { - this.creationTime.set(creationTime); + return creationTime; } } diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java index 186ed0f020..10195cc756 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java @@ -153,12 +153,12 @@ public Optional getProperty(String propertyName) { @Override public String getFilesModificationTime() { - return jibExtension.getContainer().getFilesModificationTime(); + return jibExtension.getContainer().getFilesModificationTime().get(); } @Override public String getCreationTime() { - return jibExtension.getContainer().getCreationTime(); + return jibExtension.getContainer().getCreationTime().get(); } @Override diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleRawConfigurationTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleRawConfigurationTest.java index 3eea89c0e4..8655241553 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleRawConfigurationTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleRawConfigurationTest.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.Optional; import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,6 +52,8 @@ public void testGetters() { OutputPathsParameters outputPathsParameters = Mockito.mock(OutputPathsParameters.class); CredHelperParameters fromCredHelperParameters = Mockito.mock(CredHelperParameters.class); CredHelperParameters toCredHelperParameters = Mockito.mock(CredHelperParameters.class); + Property filesModificationTime = Mockito.mock(Property.class); + Property creationTime = Mockito.mock(Property.class); Mockito.when(authParameters.getUsername()).thenReturn("user"); Mockito.when(authParameters.getPassword()).thenReturn("password"); @@ -90,7 +93,10 @@ public void testGetters() { Mockito.when(containerParameters.getMainClass()).thenReturn("com.example.Main"); Mockito.when(containerParameters.getPorts()).thenReturn(Arrays.asList("80/tcp", "0")); Mockito.when(containerParameters.getUser()).thenReturn("admin:wheel"); - Mockito.when(containerParameters.getFilesModificationTime()).thenReturn("2011-12-03T22:42:05Z"); + Mockito.when(containerParameters.getFilesModificationTime()).thenReturn(filesModificationTime); + Mockito.when(filesModificationTime.get()).thenReturn("2011-12-03T22:42:05Z"); + Mockito.when(containerParameters.getCreationTime()).thenReturn(creationTime); + Mockito.when(creationTime.get()).thenReturn("2011-12-03T11:42:05Z"); Mockito.when(dockerClientParameters.getExecutablePath()).thenReturn(Paths.get("test")); Mockito.when(dockerClientParameters.getEnvironment()) @@ -135,6 +141,7 @@ public void testGetters() { rawConfiguration.getToCredHelper().getEnvironment()); Assert.assertEquals("admin:wheel", rawConfiguration.getUser().get()); Assert.assertEquals("2011-12-03T22:42:05Z", rawConfiguration.getFilesModificationTime()); + Assert.assertEquals("2011-12-03T11:42:05Z", rawConfiguration.getCreationTime()); Assert.assertEquals(Paths.get("test"), rawConfiguration.getDockerExecutable().get()); Assert.assertEquals( new HashMap<>(ImmutableMap.of("docker", "client")), diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java index bd15b9e0c4..440ae17779 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Properties; import org.gradle.api.Project; +import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; import org.gradle.testfixtures.ProjectBuilder; @@ -184,9 +185,9 @@ public void testContainer() { assertThat(testJibExtension.getContainer().getPorts()).isEmpty(); assertThat(testJibExtension.getContainer().getLabels().get()).isEmpty(); assertThat(testJibExtension.getContainer().getAppRoot()).isEmpty(); - assertThat(testJibExtension.getContainer().getFilesModificationTime()) + assertThat(testJibExtension.getContainer().getFilesModificationTime().get()) .isEqualTo("EPOCH_PLUS_SECOND"); - assertThat(testJibExtension.getContainer().getCreationTime()).isEqualTo("EPOCH"); + assertThat(testJibExtension.getContainer().getCreationTime().get()).isEqualTo("EPOCH"); testJibExtension.container( container -> { @@ -200,7 +201,8 @@ public void testContainer() { container.setPorts(Arrays.asList("1000", "2000-2010", "3000")); container.setFormat(ImageFormat.OCI); container.setAppRoot("some invalid appRoot value"); - container.setFilesModificationTime("some invalid time value"); + container.getFilesModificationTime().set("some invalid time value"); + container.getCreationTime().set("some other invalid time value"); }); ContainerParameters container = testJibExtension.getContainer(); assertThat(container.getEntrypoint()).containsExactly("foo", "bar", "baz").inOrder(); @@ -215,7 +217,16 @@ public void testContainer() { assertThat(container.getPorts()).containsExactly("1000", "2000-2010", "3000").inOrder(); assertThat(container.getFormat()).isSameInstanceAs(ImageFormat.OCI); assertThat(container.getAppRoot()).isEqualTo("some invalid appRoot value"); - assertThat(container.getFilesModificationTime()).isEqualTo("some invalid time value"); + assertThat(container.getFilesModificationTime().get()).isEqualTo("some invalid time value"); + assertThat(container.getCreationTime().get()).isEqualTo("some other invalid time value"); + testJibExtension.container( + extensionContainer -> { + extensionContainer.getFilesModificationTime().set((String) null); + extensionContainer.getCreationTime().set((String) null); + }); + container = testJibExtension.getContainer(); + assertThat(container.getFilesModificationTime().get()).isEqualTo("EPOCH_PLUS_SECOND"); + assertThat(container.getCreationTime().get()).isEqualTo("EPOCH"); } @Test @@ -482,8 +493,16 @@ public void testProperties() { System.setProperty("jib.container.user", "myUser"); assertThat(testJibExtension.getContainer().getUser()).isEqualTo("myUser"); System.setProperty("jib.container.filesModificationTime", "2011-12-03T22:42:05Z"); - assertThat(testJibExtension.getContainer().getFilesModificationTime()) + testJibExtension + .getContainer() + .getFilesModificationTime() + .set("property should override value"); + assertThat(testJibExtension.getContainer().getFilesModificationTime().get()) .isEqualTo("2011-12-03T22:42:05Z"); + System.setProperty("jib.container.creationTime", "2011-12-03T11:42:05Z"); + testJibExtension.getContainer().getCreationTime().set("property should override value"); + assertThat(testJibExtension.getContainer().getCreationTime().get()) + .isEqualTo("2011-12-03T11:42:05Z"); System.setProperty("jib.containerizingMode", "packaged"); assertThat(testJibExtension.getContainerizingMode()).isEqualTo("packaged"); @@ -507,6 +526,22 @@ public void testProperties() { .inOrder(); } + @Test + public void testLazyPropertiesFinalization() { + Property filesModificationTime = + testJibExtension.getContainer().getFilesModificationTime(); + filesModificationTime.set((String) null); + filesModificationTime.finalizeValue(); + System.setProperty("jib.container.filesModificationTime", "EPOCH_PLUS_SECOND"); + assertThat(testJibExtension.getContainer().getFilesModificationTime().get()) + .isEqualTo("EPOCH_PLUS_SECOND"); + Property creationTime = testJibExtension.getContainer().getCreationTime(); + creationTime.set((String) null); + creationTime.finalizeValue(); + System.setProperty("jib.container.creationTime", "EPOCH"); + assertThat(testJibExtension.getContainer().getCreationTime().get()).isEqualTo("EPOCH"); + } + @Test public void testSystemPropertiesWithInvalidPlatform() { System.setProperty("jib.from.platforms", "linux /amd64"); diff --git a/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle b/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle index c3e041af5d..166749d568 100644 --- a/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle +++ b/jib-gradle-plugin/src/test/resources/gradle/projects/lazy-evaluation/build.gradle @@ -59,8 +59,8 @@ tasks.register('check-extra-directories') { } tasks.register('showtimes') { - String prop = project.extensions.getByName('jib')['container']['creationTime'] + String prop = project.extensions.jib.container.creationTime.get() println('creationTime=' + prop) - prop = project.extensions.getByName('jib')['container']['filesModificationTime'] + prop = project.extensions.jib.container.filesModificationTime.get() println('filesModificationTime=' + prop) } From c940921d4a2d114d3069320f04504701ef881328 Mon Sep 17 00:00:00 2001 From: Carsten Reckord Date: Wed, 21 Sep 2022 16:38:55 +0200 Subject: [PATCH 3/3] Updated README for property use in creationTime/filesModificationTime (#3708) --- jib-gradle-plugin/README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/jib-gradle-plugin/README.md b/jib-gradle-plugin/README.md index 76a08d3411..03810f6b9f 100644 --- a/jib-gradle-plugin/README.md +++ b/jib-gradle-plugin/README.md @@ -36,6 +36,7 @@ For information about the project, see the [Jib project README](../README.md). * [Using Docker Credential Helpers](#using-docker-credential-helpers) * [Using Specific Credentials](#using-specific-credentials) * [Custom Container Entrypoint](#custom-container-entrypoint) + * [Reproducible Build Timestamps](#reproducible-build-timestamps) * [Jib Extensions](#jib-extensions) * [WAR Projects](#war-projects) * [Skaffold Integration](#skaffold-integration) @@ -247,12 +248,12 @@ Property | Type | Default | Description --- | --- | --- | --- `appRoot` | `String` | `/app` | The root directory on the container where the app's contents are placed. Particularly useful for WAR-packaging projects to work with different Servlet engine base images by designating where to put exploded WAR contents; see [WAR usage](#war-projects) as an example. `args` | `List` | *None* | Additional program arguments appended to the command to start the container (similar to Docker's [CMD](https://docs.docker.com/engine/reference/builder/#cmd) instruction in relation with [ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint)). In the default case where you do not set a custom `entrypoint`, this parameter is effectively the arguments to the main method of your Java application. -`creationTime` | `String` | `EPOCH` | Sets the container creation time. (Note that this property does not affect the file modification times, which are configured using `jib.container.filesModificationTime`.) The value can be `EPOCH` to set the timestamps to Epoch (default behavior), `USE_CURRENT_TIMESTAMP` to forgo reproducibility and use the real creation time, or an ISO 8601 date-time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z`. +`creationTime` | `String` | `EPOCH` | Sets the container creation time. (Note that this property does not affect the file modification times, which are configured using `jib.container.filesModificationTime`.) The value can be `EPOCH` to set the timestamps to Epoch (default behavior), `USE_CURRENT_TIMESTAMP` to forgo reproducibility and use the real creation time, or an ISO 8601 date-time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z`. The value can also be initialized [lazily](https://docs.gradle.org/current/userguide/lazy_configuration.html) with a provider. `entrypoint` | `List` | *None* | The command to start the container with (similar to Docker's [ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint) instruction). If set, then `jvmFlags`, `mainClass`, `extraClasspath`, and `expandClasspathDependencies` are ignored. You may also set `jib.container.entrypoint = 'INHERIT'` to indicate that the `entrypoint` and `args` should be inherited from the base image.\* `environment` | `Map` | *None* | Key-value pairs for setting environment variables on the container (similar to Docker's [ENV](https://docs.docker.com/engine/reference/builder/#env) instruction). `extraClasspath` | `List` | *None* | Additional paths in the container to prepend to the computed Java classpath. `expandClasspathDependencies` | `boolean` | `false` |
  • Java 8 *or* Jib < 3.1: When set to true, does not use a wildcard (for example, `/app/lib/*`) for dependency JARs in the default Java runtime classpath but instead enumerates the JARs. Has the effect of preserving the classpath loading order as defined by the Gradle project.
  • Java >= 9 *and* Jib >= 3.1: The option has no effect. Jib *always* enumerates the dependency JARs. This is achieved by [creating and using an argument file](#custom-container-entrypoint) for the `--class-path` JVM argument.
-`filesModificationTime` | `String` | `EPOCH_PLUS_SECOND` | Sets the modification time (last modified time) of files in the image put by Jib. (Note that this does not set the image creation time, which can be set using `jib.container.creationTime`.) The value should either be `EPOCH_PLUS_SECOND` to set the timestamps to Epoch + 1 second (default behavior), or an ISO 8601 date-time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z`. +`filesModificationTime` | `String` | `EPOCH_PLUS_SECOND` | Sets the modification time (last modified time) of files in the image put by Jib. (Note that this does not set the image creation time, which can be set using `jib.container.creationTime`.) The value should either be `EPOCH_PLUS_SECOND` to set the timestamps to Epoch + 1 second (default behavior), or an ISO 8601 date-time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z`. The value can also be initialized [lazily](https://docs.gradle.org/current/userguide/lazy_configuration.html) with a provider. `format` | `String` | `Docker` | Use `OCI` to build an [OCI container image](https://www.opencontainers.org/). `jvmFlags` | `List` | *None* | Additional flags to pass into the JVM when running your application. `labels` | `Map` | *None* | Key-value pairs for applying image metadata (similar to Docker's [LABEL](https://docs.docker.com/engine/reference/builder/#label) instruction). @@ -572,6 +573,21 @@ Therefore, *for example*, the following commands will be able to launch your app - (Java 9+) `java -cp @/app/jib-classpath-file @/app/jib-main-class-file` - (with shell) `java -cp $( cat /app/jib-classpath-file ) $( cat /app/jib-main-class-file )` +### Reproducible Build Timestamps + +To ensure that a Jib build is reproducible, Jib sets the image creation time to the Unix epoch (00:00:00, January 1st, 1970 in UTC) and all file modification times to one second past the epoch by default. See the [Jib FAQ](https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#why-is-my-image-created-48-years-ago) for more details on reproducible builds. + +Another, more complex way to achieve reproducible builds with stable creation times is to leverage commit timestamps from the project's SCM. For example, the [gradle-git-properties](https://plugins.gradle.org/plugin/com.gorylenko.gradle-git-properties) plugin can be used to inject Git commit information into the current build. These can then be used to configure `jib.container.creationTime`. Since the actual Git information is not yet available at the time the build is configured, it needs to be set through [lazy configuration in Gradle](https://docs.gradle.org/current/userguide/lazy_configuration.html), using a provider in `build.gradle`: + +```groovy +jib { + container { + creationTime = project.provider { project.ext.git['git.commit.time'] } + } +} +``` + +This would build an image with the creation time set to the time of the latest commit from `project.ext.git['git.commit.time']`. ### Jib Extensions