diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java index 577600f9200b..5d22f08bb398 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 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. @@ -17,6 +17,8 @@ package org.springframework.boot.buildpack.platform.build; import java.io.File; +import java.time.Instant; +import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; @@ -79,6 +81,8 @@ public class BuildRequest { private final Cache launchCache; + private final Instant createdDate; + BuildRequest(ImageReference name, Function applicationContent) { Assert.notNull(name, "Name must not be null"); Assert.notNull(applicationContent, "ApplicationContent must not be null"); @@ -98,12 +102,14 @@ public class BuildRequest { this.tags = Collections.emptyList(); this.buildCache = null; this.launchCache = null; + this.createdDate = null; } BuildRequest(ImageReference name, Function applicationContent, ImageReference builder, ImageReference runImage, Creator creator, Map env, boolean cleanCache, boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List buildpacks, - List bindings, String network, List tags, Cache buildCache, Cache launchCache) { + List bindings, String network, List tags, Cache buildCache, Cache launchCache, + Instant createdDate) { this.name = name; this.applicationContent = applicationContent; this.builder = builder; @@ -120,6 +126,7 @@ public class BuildRequest { this.tags = tags; this.buildCache = buildCache; this.launchCache = launchCache; + this.createdDate = createdDate; } /** @@ -131,7 +138,8 @@ public BuildRequest withBuilder(ImageReference builder) { Assert.notNull(builder, "Builder must not be null"); return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, - this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache); + this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache, + this.createdDate); } /** @@ -142,7 +150,8 @@ public BuildRequest withBuilder(ImageReference builder) { public BuildRequest withRunImage(ImageReference runImageName) { return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(), this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, - this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache); + this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache, + this.createdDate); } /** @@ -154,7 +163,7 @@ public BuildRequest withCreator(Creator creator) { Assert.notNull(creator, "Creator must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -170,7 +179,8 @@ public BuildRequest withEnv(String name, String value) { env.put(name, value); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, - this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache); + this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache, + this.createdDate); } /** @@ -185,7 +195,7 @@ public BuildRequest withEnv(Map env) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, - this.launchCache); + this.launchCache, this.createdDate); } /** @@ -196,7 +206,7 @@ public BuildRequest withEnv(Map env) { public BuildRequest withCleanCache(boolean cleanCache) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -207,7 +217,7 @@ public BuildRequest withCleanCache(boolean cleanCache) { public BuildRequest withVerboseLogging(boolean verboseLogging) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -218,7 +228,7 @@ public BuildRequest withVerboseLogging(boolean verboseLogging) { public BuildRequest withPullPolicy(PullPolicy pullPolicy) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -229,7 +239,7 @@ public BuildRequest withPullPolicy(PullPolicy pullPolicy) { public BuildRequest withPublish(boolean publish) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -253,7 +263,7 @@ public BuildRequest withBuildpacks(List buildpacks) { Assert.notNull(buildpacks, "Buildpacks must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -277,7 +287,7 @@ public BuildRequest withBindings(List bindings) { Assert.notNull(bindings, "Bindings must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings, - this.network, this.tags, this.buildCache, this.launchCache); + this.network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -289,7 +299,7 @@ public BuildRequest withBindings(List bindings) { public BuildRequest withNetwork(String network) { return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - network, this.tags, this.buildCache, this.launchCache); + network, this.tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -311,7 +321,7 @@ public BuildRequest withTags(List tags) { Assert.notNull(tags, "Tags must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, tags, this.buildCache, this.launchCache); + this.network, tags, this.buildCache, this.launchCache, this.createdDate); } /** @@ -323,7 +333,7 @@ public BuildRequest withBuildCache(Cache buildCache) { Assert.notNull(buildCache, "BuildCache must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, buildCache, this.launchCache); + this.network, this.tags, buildCache, this.launchCache, this.createdDate); } /** @@ -335,7 +345,31 @@ public BuildRequest withLaunchCache(Cache launchCache) { Assert.notNull(launchCache, "LaunchCache must not be null"); return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, - this.network, this.tags, this.buildCache, launchCache); + this.network, this.tags, this.buildCache, launchCache, this.createdDate); + } + + /** + * Return a new {@link BuildRequest} with an updated created date. + * @param createdDate the created date + * @return an updated build request + */ + public BuildRequest withCreatedDate(String createdDate) { + Assert.notNull(createdDate, "CreatedDate must not be null"); + return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env, + this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings, + this.network, this.tags, this.buildCache, this.launchCache, parseCreatedDate(createdDate)); + } + + private Instant parseCreatedDate(String createdDate) { + if ("now".equalsIgnoreCase(createdDate)) { + return Instant.now(); + } + try { + return Instant.parse(createdDate); + } + catch (DateTimeParseException ex) { + throw new IllegalArgumentException("Error parsing '" + createdDate + "' as an image created date", ex); + } } /** @@ -471,6 +505,14 @@ public Cache getLaunchCache() { return this.launchCache; } + /** + * Return the custom created date that should be used by the lifecycle. + * @return the created date + */ + public Instant getCreatedDate() { + return this.createdDate; + } + /** * Factory method to create a new {@link BuildRequest} from a JAR file. * @param jarFile the source jar file diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java index b31cacf0e3f3..3c3e89acfae6 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java @@ -50,6 +50,8 @@ class Lifecycle implements Closeable { private static final String PLATFORM_API_VERSION_KEY = "CNB_PLATFORM_API"; + private static final String SOURCE_DATE_EPOCH_KEY = "SOURCE_DATE_EPOCH"; + private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock"; private final BuildLog log; @@ -184,6 +186,9 @@ private Phase createPhase() { if (this.request.getNetwork() != null) { phase.withNetworkMode(this.request.getNetwork()); } + if (this.request.getCreatedDate() != null) { + phase.withEnv(SOURCE_DATE_EPOCH_KEY, Long.toString(this.request.getCreatedDate().getEpochSecond())); + } return phase; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java index bbcd5cba9714..24a488bd4a14 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/Image.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -44,12 +44,15 @@ public class Image extends MappedObject { private final String os; + private final String created; + Image(JsonNode node) { super(node, MethodHandles.lookup()); this.digests = getDigests(getNode().at("/RepoDigests")); this.config = new ImageConfig(getNode().at("/Config")); this.layers = extractLayers(valueAt("/RootFS/Layers", String[].class)); this.os = valueAt("/Os", String.class); + this.created = valueAt("/Created", String.class); } private List getDigests(JsonNode node) { @@ -100,6 +103,14 @@ public String getOs() { return (this.os != null) ? this.os : "linux"; } + /** + * Return the created date of the image. + * @return the image created date + */ + public String getCreated() { + return this.created; + } + /** * Create a new {@link Image} instance from the specified JSON content. * @param content the JSON content diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java index f5ef8aa7fff4..0d36b8a82c3d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java @@ -21,6 +21,9 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -258,6 +261,37 @@ void withLaunchVolumeCacheWhenCacheIsNullThrowsException() throws IOException { .withMessage("LaunchCache must not be null"); } + @Test + void withCreatedDateSetsCreatedDate() throws Exception { + Instant createDate = Instant.now(); + BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")); + BuildRequest withCreatedDate = request.withCreatedDate(createDate.toString()); + assertThat(withCreatedDate.getCreatedDate()).isEqualTo(createDate); + } + + @Test + void withCreatedDateNowSetsCreatedDate() throws Exception { + OffsetDateTime now = OffsetDateTime.now(); + BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")); + BuildRequest withCreatedDate = request.withCreatedDate("now"); + OffsetDateTime createdDate = OffsetDateTime.ofInstant(withCreatedDate.getCreatedDate(), ZoneId.of("UTC")); + assertThat(createdDate.getYear()).isEqualTo(now.getYear()); + assertThat(createdDate.getMonth()).isEqualTo(now.getMonth()); + assertThat(createdDate.getDayOfMonth()).isEqualTo(now.getDayOfMonth()); + withCreatedDate = request.withCreatedDate("NOW"); + createdDate = OffsetDateTime.ofInstant(withCreatedDate.getCreatedDate(), ZoneId.of("UTC")); + assertThat(createdDate.getYear()).isEqualTo(now.getYear()); + assertThat(createdDate.getMonth()).isEqualTo(now.getMonth()); + assertThat(createdDate.getDayOfMonth()).isEqualTo(now.getDayOfMonth()); + } + + @Test + void withCreatedDateAndInvalidDateThrowsException() throws Exception { + BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")); + assertThatIllegalArgumentException().isThrownBy(() -> request.withCreatedDate("not a date")) + .withMessageContaining("'not a date'"); + } + private void hasExpectedJarContent(TarArchive archive) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java index dac256363d23..ef877fd3ee0a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java @@ -116,7 +116,7 @@ void getArchiveHasTag() throws Exception { } @Test - void getArchiveHasFixedCreateDate() throws Exception { + void getArchiveHasFixedCreatedDate() throws Exception { EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, this.creator, this.env, this.buildpacks); Instant createInstant = builder.getArchive().getCreateDate(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java index e333cf9b459a..c4b6ae15b26b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java @@ -218,6 +218,17 @@ void executeWithCacheVolumeNamesExecutesPhases() throws Exception { assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'"); } + @Test + void executeWithCreatedDateExecutesPhases() throws Exception { + given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId()); + given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId()); + given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null)); + BuildRequest request = getTestRequest().withCreatedDate("2020-07-01T12:34:56Z"); + createLifecycle(request).execute(); + assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-created-date.json")); + assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'"); + } + @Test void executeWithDockerHostAndRemoteAddressExecutesPhases() throws Exception { given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageTests.java index 2826fc1f1342..851a9c781a7a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageTests.java @@ -67,6 +67,12 @@ void getOsReturnsOs() throws Exception { assertThat(image.getOs()).isEqualTo("linux"); } + @Test + void getCreatedReturnsDate() throws Exception { + Image image = getImage(); + assertThat(image.getCreated()).isEqualTo("2019-10-30T19:34:56.296666503Z"); + } + private Image getImage() throws IOException { return Image.of(getContent("image.json")); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/build/lifecycle-creator-created-date.json b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/build/lifecycle-creator-created-date.json new file mode 100644 index 000000000000..a316d9633a44 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/build/lifecycle-creator-created-date.json @@ -0,0 +1,40 @@ +{ + "User": "root", + "Image": "pack.local/ephemeral-builder", + "Cmd": [ + "/cnb/lifecycle/creator", + "-app", + "/workspace", + "-platform", + "/platform", + "-run-image", + "docker.io/cloudfoundry/run:latest", + "-layers", + "/layers", + "-cache-dir", + "/cache", + "-launch-cache", + "/launch-cache", + "-daemon", + "docker.io/library/my-application:latest" + ], + "Env": [ + "CNB_PLATFORM_API=0.8", + "SOURCE_DATE_EPOCH=1593606896" + ], + "Labels": { + "author": "spring-boot" + }, + "HostConfig": { + "Binds": [ + "/var/run/docker.sock:/var/run/docker.sock", + "pack-layers-aaaaaaaaaa:/layers", + "pack-app-aaaaaaaaaa:/workspace", + "pack-cache-b35197ac41ea.build:/cache", + "pack-cache-b35197ac41ea.launch:/launch-cache" + ], + "SecurityOpt" : [ + "label=disable" + ] + } +} \ No newline at end of file diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc index 22c725b679c3..7965e002fe4c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc @@ -193,6 +193,12 @@ The values provided to the `tags` option should be full image references in the | A cache containing layers created by buildpacks and used by the image launching process. | A named volume in the Docker daemon, with a name derived from the image name. +| `createdDate` +| `--createdDate` +| A date that will be used to set the `Created` field in the generated image's metadata. +The value must be a string in the ISO 8601 instant format, or `now` to use the current date and time. +| A fixed date that enables https://buildpacks.io/docs/features/reproducibility/[build reproducibility]. + |=== NOTE: The plugin detects the target Java compatibility of the project using the JavaPlugin's `targetCompatibility` property. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java index 0817bd731e8b..4e1073eba257 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.java @@ -260,6 +260,16 @@ public void launchCache(Action action) { action.execute(this.launchCache); } + /** + * Returns the date that will be used as the {@code Created} date of the image. When + * {@code null}, a fixed date that enables build reproducibility will be used. + * @return the created date + */ + @Input + @Optional + @Option(option = "createdDate", description = "The date to use as the created date of the image") + public abstract Property getCreatedDate(); + /** * Returns the Docker configuration the builder will use. * @return docker configuration. @@ -305,6 +315,7 @@ private BuildRequest customize(BuildRequest request) { request = customizeTags(request); request = customizeCaches(request); request = request.withNetwork(getNetwork().getOrNull()); + request = customizeCreatedDate(request); return request; } @@ -387,4 +398,12 @@ private BuildRequest customizeCaches(BuildRequest request) { return request; } + private BuildRequest customizeCreatedDate(BuildRequest request) { + String createdDate = getCreatedDate().getOrNull(); + if (createdDate != null) { + return request.withCreatedDate(createdDate); + } + return request; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java index 91ebfc40623e..fda628793636 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.java @@ -26,6 +26,7 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; +import java.time.OffsetDateTime; import java.util.Random; import java.util.Set; @@ -42,6 +43,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi; import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi; import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi; +import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.docker.type.VolumeName; import org.springframework.boot.buildpack.platform.io.FilePermissions; @@ -140,12 +142,15 @@ void buildsImageWithCommandLineOptions() throws IOException { writeLongNameResource(); BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT", "--imageName=example/test-image-cmd", - "--builder=projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1", - "--runImage=projects.registry.vmware.com/springboot/run:tiny-cnb"); + "--builder=projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2", + "--runImage=projects.registry.vmware.com/springboot/run:tiny-cnb", + "--createdDate=2020-07-01T12:34:56Z"); assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(result.getOutput()).contains("example/test-image-cmd"); assertThat(result.getOutput()).contains("---> Test Info buildpack building"); assertThat(result.getOutput()).contains("---> Test Info buildpack done"); + Image image = new DockerApi().image().inspect(ImageReference.of("example/test-image-cmd")); + assertThat(image.getCreated()).isEqualTo("2020-07-01T12:34:56Z"); removeImages("example/test-image-cmd"); } @@ -290,6 +295,49 @@ void buildsImageWithVolumeCaches() throws IOException { deleteVolumes("cache-" + projectName + ".build", "cache-" + projectName + ".launch"); } + @TestTemplate + void buildsImageWithCreatedDate() throws IOException { + writeMainClass(); + writeLongNameResource(); + BuildResult result = this.gradleBuild.build("bootBuildImage"); + String projectName = this.gradleBuild.getProjectDir().getName(); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("docker.io/library/" + projectName); + assertThat(result.getOutput()).contains("---> Test Info buildpack building"); + assertThat(result.getOutput()).contains("---> Test Info buildpack done"); + Image image = new DockerApi().image().inspect(ImageReference.of("docker.io/library/" + projectName)); + assertThat(image.getCreated()).isEqualTo("2020-07-01T12:34:56Z"); + removeImages(projectName); + } + + @TestTemplate + void buildsImageWithCurrentCreatedDate() throws IOException { + writeMainClass(); + writeLongNameResource(); + BuildResult result = this.gradleBuild.build("bootBuildImage"); + String projectName = this.gradleBuild.getProjectDir().getName(); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("docker.io/library/" + projectName); + assertThat(result.getOutput()).contains("---> Test Info buildpack building"); + assertThat(result.getOutput()).contains("---> Test Info buildpack done"); + Image image = new DockerApi().image().inspect(ImageReference.of("docker.io/library/" + projectName)); + OffsetDateTime createdDateTime = OffsetDateTime.parse(image.getCreated()); + OffsetDateTime current = OffsetDateTime.now(); + assertThat(createdDateTime.getYear()).isEqualTo(current.getYear()); + assertThat(createdDateTime.getMonth()).isEqualTo(current.getMonth()); + assertThat(createdDateTime.getDayOfMonth()).isEqualTo(current.getDayOfMonth()); + removeImages(projectName); + } + + @TestTemplate + void failsWithInvalidCreatedDate() throws IOException { + writeMainClass(); + writeLongNameResource(); + BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage"); + assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED); + assertThat(result.getOutput()).contains("Error parsing 'invalid date' as an image created date"); + } + @TestTemplate void failsWithBuilderError() throws IOException { writeMainClass(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBinding.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBinding.gradle index 02021081b0e1..ab51cb7d1b52 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBinding.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBinding.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" bindings = [ "${projectDir}/bindings/ca-certificates:/platform/bindings/certificates" as String ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromBuilder.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromBuilder.gradle index a8e5438363f5..5cadc95cd27e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromBuilder.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromBuilder.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildpacks = [ "spring-boot/test-info" ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromDirectory.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromDirectory.gradle index d94ea2389251..f94f97267fb0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromDirectory.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromDirectory.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildpacks = [ "file://${projectDir}/buildpack/hello-world" as String ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromTarGzip.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromTarGzip.gradle index cdf1445965c9..48dc35e6ddde 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromTarGzip.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpackFromTarGzip.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildpacks = [ "file://${projectDir}/hello-world.tgz" as String ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpacksFromImages.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpacksFromImages.gradle index 085aeba22ecf..33a8d4062b62 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpacksFromImages.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithBuildpacksFromImages.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildpacks = ["projects.registry.vmware.com/springboot/test-info:latest"] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCreatedDate.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCreatedDate.gradle new file mode 100644 index 000000000000..e2b8cdc67c6e --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCreatedDate.gradle @@ -0,0 +1,10 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '{version}' +} + +bootBuildImage { + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" + pullPolicy = "IF_NOT_PRESENT" + createdDate = "2020-07-01T12:34:56Z" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCurrentCreatedDate.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCurrentCreatedDate.gradle new file mode 100644 index 000000000000..11400e426194 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCurrentCreatedDate.gradle @@ -0,0 +1,10 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '{version}' +} + +bootBuildImage { + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" + pullPolicy = "IF_NOT_PRESENT" + createdDate = "now" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomBuilderAndRunImage.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomBuilderAndRunImage.gradle index 2e00a133c9b5..36a215742b42 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomBuilderAndRunImage.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomBuilderAndRunImage.gradle @@ -8,7 +8,7 @@ targetCompatibility = '1.8' bootBuildImage { imageName = "example/test-image-custom" - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" runImage = "projects.registry.vmware.com/springboot/run:tiny-cnb" pullPolicy = "IF_NOT_PRESENT" } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomName.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomName.gradle index 8c2297397df7..d31430112243 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomName.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithCustomName.gradle @@ -8,6 +8,6 @@ targetCompatibility = '1.8' bootBuildImage { imageName = "example/test-image-name" - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithLaunchScript.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithLaunchScript.gradle index 4e88e59a0ec0..6a718f3bca33 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithLaunchScript.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithLaunchScript.gradle @@ -11,6 +11,6 @@ bootJar { } bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithNetworkModeNone.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithNetworkModeNone.gradle index 148225686d4e..e8f97e05a6df 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithNetworkModeNone.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithNetworkModeNone.gradle @@ -11,7 +11,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" network = "none" } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithPullPolicy.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithPullPolicy.gradle index 7330c2b70178..c4b59a7dfe87 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithPullPolicy.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithPullPolicy.gradle @@ -13,6 +13,6 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = PullPolicy.ALWAYS } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithTag.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithTag.gradle index 15fabd8dc739..49b129bb1af4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithTag.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithTag.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" tags = [ "example.com/myapp:latest" ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithVolumeCaches.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithVolumeCaches.gradle index 77bb0e2b3b6a..225121980b3a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithVolumeCaches.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithVolumeCaches.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildCache { volume { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithWarPackagingAndJarConfiguration.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithWarPackagingAndJarConfiguration.gradle index 88e79fb85473..54b7fdf49d4b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithWarPackagingAndJarConfiguration.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-buildsImageWithWarPackagingAndJarConfiguration.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" archiveFile = bootWar.archiveFile } \ No newline at end of file diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWhenCachesAreConfiguredTwice.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWhenCachesAreConfiguredTwice.gradle index 22da9fc0b617..1d4da7173940 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWhenCachesAreConfiguredTwice.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWhenCachesAreConfiguredTwice.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" buildCache { volume { name = "build-cache-volume1" diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuilderError.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuilderError.gradle index a442e7c51f0e..99ca576a1cc0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuilderError.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuilderError.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" environment = ["FORCE_FAILURE": "true"] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuildpackNotInBuilder.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuildpackNotInBuilder.gradle index f1996746e2d9..b5b3bed5d714 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuildpackNotInBuilder.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithBuildpackNotInBuilder.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" buildpacks = [ "urn:cnb:builder:example/does-not-exist:0.0.1" ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidCreatedDate.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidCreatedDate.gradle new file mode 100644 index 000000000000..fccae838bfe2 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidCreatedDate.gradle @@ -0,0 +1,10 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '{version}' +} + +bootBuildImage { + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" + pullPolicy = "IF_NOT_PRESENT" + createdDate = "invalid date" +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidTag.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidTag.gradle index a50a642312f2..c18ac5c06dca 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidTag.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests-failsWithInvalidTag.gradle @@ -7,7 +7,7 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" tags = [ "example/Invalid-Tag-Name" ] } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.gradle index cfbb0060139d..71086c7ec513 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageIntegrationTests.gradle @@ -11,6 +11,6 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" pullPolicy = "IF_NOT_PRESENT" } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.gradle index d6abd1d1618e..e1a1c532d113 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/bundling/BootBuildImageRegistryIntegrationTests.gradle @@ -7,6 +7,6 @@ sourceCompatibility = '1.8' targetCompatibility = '1.8' bootBuildImage { - builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1" + builder = "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2" publish = true } \ No newline at end of file diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc index 86d2de80bd35..73858896b8ad 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc @@ -203,6 +203,12 @@ The values provided to the `tags` option should be full image references in the | A cache containing layers created by buildpacks and used by the image launching process. | A named volume in the Docker daemon, with a name derived from the image name. +| `createdDate` + +(`spring-boot.build-image.createdDate`) +| A date that will be used to set the `Created` field in the generated image's metadata. +The value must be a string in the ISO 8601 instant format, or `now` to use the current date and time. +| A fixed date that enables https://buildpacks.io/docs/features/reproducibility/[build reproducibility]. + |=== NOTE: The plugin detects the target Java compatibility of the project using the compiler's plugin configuration or the `maven.compiler.target` property. diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java index 888d87529a20..2092ddc9e954 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java @@ -21,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.OffsetDateTime; import java.util.Random; import java.util.stream.IntStream; @@ -29,6 +30,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi; import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi; +import org.springframework.boot.buildpack.platform.docker.type.Image; import org.springframework.boot.buildpack.platform.docker.type.ImageName; import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.docker.type.VolumeName; @@ -240,14 +242,18 @@ void whenBuildImageIsInvokedWithCommandLineParameters(MavenBuild mavenBuild) { .systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT") .systemProperty("spring-boot.build-image.imageName", "example.com/test/cmd-property-name:v1") .systemProperty("spring-boot.build-image.builder", - "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1") + "projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2") .systemProperty("spring-boot.build-image.runImage", "projects.registry.vmware.com/springboot/run:tiny-cnb") + .systemProperty("spring-boot.build-image.createdDate", "2020-07-01T12:34:56Z") .execute((project) -> { assertThat(buildLog(project)).contains("Building image") .contains("example.com/test/cmd-property-name:v1") .contains("---> Test Info buildpack building") .contains("---> Test Info buildpack done") .contains("Successfully built image"); + Image image = new DockerApi().image() + .inspect(ImageReference.of("example.com/test/cmd-property-name:v1")); + assertThat(image.getCreated()).isEqualTo("2020-07-01T12:34:56Z"); removeImage("example.com/test/cmd-property-name", "v1"); }); } @@ -387,6 +393,47 @@ void whenBuildImageIsInvokedWithVolumeCaches(MavenBuild mavenBuild) { }); } + @TestTemplate + void whenBuildImageIsInvokedWithCreatedDate(MavenBuild mavenBuild) { + String testBuildId = randomString(); + mavenBuild.project("build-image-created-date") + .goals("package") + .systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT") + .systemProperty("test-build-id", testBuildId) + .execute((project) -> { + assertThat(buildLog(project)).contains("Building image") + .contains("docker.io/library/build-image-created-date:0.0.1.BUILD-SNAPSHOT") + .contains("Successfully built image"); + Image image = new DockerApi().image() + .inspect(ImageReference.of("docker.io/library/build-image-created-date:0.0.1.BUILD-SNAPSHOT")); + assertThat(image.getCreated()).isEqualTo("2020-07-01T12:34:56Z"); + removeImage("build-image-created-date", "0.0.1.BUILD-SNAPSHOT"); + }); + } + + @TestTemplate + void whenBuildImageIsInvokedWithCurrentCreatedDate(MavenBuild mavenBuild) { + String testBuildId = randomString(); + mavenBuild.project("build-image-current-created-date") + .goals("package") + .systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT") + .systemProperty("test-build-id", testBuildId) + .execute((project) -> { + assertThat(buildLog(project)).contains("Building image") + .contains("docker.io/library/build-image-current-created-date:0.0.1.BUILD-SNAPSHOT") + .contains("Successfully built image"); + Image image = new DockerApi().image() + .inspect(ImageReference + .of("docker.io/library/build-image-current-created-date:0.0.1.BUILD-SNAPSHOT")); + OffsetDateTime createdDateTime = OffsetDateTime.parse(image.getCreated()); + OffsetDateTime current = OffsetDateTime.now(); + assertThat(createdDateTime.getYear()).isEqualTo(current.getYear()); + assertThat(createdDateTime.getMonth()).isEqualTo(current.getMonth()); + assertThat(createdDateTime.getDayOfMonth()).isEqualTo(current.getDayOfMonth()); + removeImage("build-image-current-created-date", "0.0.1.BUILD-SNAPSHOT"); + }); + } + @TestTemplate void failsWhenBuildImageIsInvokedOnMultiModuleProjectWithBuildImageGoal(MavenBuild mavenBuild) { mavenBuild.project("build-image-multi-module") diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bad-buildpack/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bad-buildpack/pom.xml index 97b406768484..23544a5ac989 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bad-buildpack/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bad-buildpack/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 urn:cnb:builder:example/does-not-exist:0.0.1 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bindings/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bindings/pom.xml index 01d1cfd0b0ff..1435bce3164a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bindings/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-bindings/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 ${basedir}/bindings/ca-certificates:/platform/bindings/ca-certificates diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-builder-error/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-builder-error/pom.xml index 9ac7f0123372..5695aa6843bd 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-builder-error/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-builder-error/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 true diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches-multiple/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches-multiple/pom.xml index b8389ef7b503..54db991bb65a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches-multiple/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches-multiple/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 build-cache-volume1 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches/pom.xml index 068baa57a9c8..f95eb39f874e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-caches/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 cache-${test-build-id}.build diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source-with-repackage/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source-with-repackage/pom.xml index 820dd2efc560..1781d5aaa4c5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source-with-repackage/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source-with-repackage/pom.xml @@ -45,7 +45,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source/pom.xml index 6ea0b72ee4f3..b0de38b46c59 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-source/pom.xml @@ -39,7 +39,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-with-repackage/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-with-repackage/pom.xml index 839c40e235bd..40c95a1b2e4a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-with-repackage/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier-with-repackage/pom.xml @@ -29,7 +29,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier/pom.xml index 7f1fa3cbda0e..3fa3f66639e2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-classifier/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-cmd-line/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-cmd-line/pom.xml index 861846524468..d2d3acb6522c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-cmd-line/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-cmd-line/pom.xml @@ -19,7 +19,7 @@ @project.version@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/pom.xml new file mode 100644 index 000000000000..5dc2dc8b16c9 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + org.springframework.boot.maven.it + build-image-created-date + 0.0.1.BUILD-SNAPSHOT + + UTF-8 + @java.version@ + @java.version@ + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + build-image-no-fork + + + + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 + 2020-07-01T12:34:56Z + + + + + + + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/src/main/java/org/test/SampleApplication.java new file mode 100644 index 000000000000..58ebebbbb234 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-created-date/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2023 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) throws Exception { + System.out.println("Launched"); + synchronized(args) { + args.wait(); // Prevent exit" + } + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/pom.xml new file mode 100644 index 000000000000..09624ddf10b4 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + org.springframework.boot.maven.it + build-image-current-created-date + 0.0.1.BUILD-SNAPSHOT + + UTF-8 + @java.version@ + @java.version@ + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + build-image-no-fork + + + + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 + now + + + + + + + + diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/src/main/java/org/test/SampleApplication.java new file mode 100644 index 000000000000..58ebebbbb234 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-current-created-date/src/main/java/org/test/SampleApplication.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2023 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) throws Exception { + System.out.println("Launched"); + synchronized(args) { + args.wait(); // Prevent exit" + } + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-builder/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-builder/pom.xml index 954d07c775ad..d86573b86a25 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-builder/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-builder/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 projects.registry.vmware.com/springboot/run:tiny-cnb diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-buildpacks/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-buildpacks/pom.xml index 80e44c41e7ee..a03238d5f720 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-buildpacks/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-buildpacks/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 urn:cnb:builder:spring-boot/test-info diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-name/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-name/pom.xml index 9307d7478072..83f84956e6e1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-name/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-custom-name/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 example.com/test/build-image:${project.version} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-empty-env-entry/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-empty-env-entry/pom.xml index b3225611fd01..519d7fdb6cf1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-empty-env-entry/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-empty-env-entry/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-final-name/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-final-name/pom.xml index 7a1843c930ca..8e302d04b243 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-final-name/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-final-name/pom.xml @@ -25,7 +25,7 @@ final-name - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-multi-module/app/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-multi-module/app/pom.xml index 1c707deea864..c6f13bd46f5e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-multi-module/app/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-multi-module/app/pom.xml @@ -31,7 +31,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-network/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-network/pom.xml index e1f913f562b4..77abe93a83d0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-network/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-network/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 none diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-publish/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-publish/pom.xml index 0ece9c9aaaac..1633e249f314 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-publish/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-publish/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 true diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-tags/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-tags/pom.xml index 5bdff1bfc6b7..7117b9a3484a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-tags/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-tags/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 ${project.artifactId}:latest diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml index e1152cb2316e..9fdc2795c0d2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml @@ -24,7 +24,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-with-repackage/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-with-repackage/pom.xml index d9c70b8c68d9..9ccb262c1043 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-with-repackage/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-with-repackage/pom.xml @@ -29,7 +29,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-zip-packaging/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-zip-packaging/pom.xml index 5dedb458f5c6..1eed54562f1c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-zip-packaging/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-zip-packaging/pom.xml @@ -26,7 +26,7 @@ ZIP - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image/pom.xml index f7f605e9df03..fba71758e43a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image/pom.xml @@ -23,7 +23,7 @@ - projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 + projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java index 3e80b75f29e7..d0e4aba43b52 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java @@ -155,6 +155,14 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo { @Parameter(property = "spring-boot.build-image.network", readonly = true) String network; + /** + * Alias for {@link Image#createdDate} to support configuration through command-line + * property. + * @since 3.1.0 + */ + @Parameter(property = "spring-boot.build-image.createdDate", readonly = true) + String createdDate; + /** * Docker configuration options. * @since 2.4.0 @@ -253,6 +261,9 @@ private BuildRequest getBuildRequest(Libraries libraries) { if (image.network == null && this.network != null) { image.setNetwork(this.network); } + if (image.createdDate == null && this.createdDate != null) { + image.setCreatedDate(this.createdDate); + } return customize(image.getBuildRequest(this.project.getArtifact(), content)); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Image.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Image.java index 98917ef6c8b5..21e166cb3028 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Image.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Image.java @@ -73,6 +73,8 @@ public class Image { CacheInfo launchCache; + String createdDate; + /** * The name of the created image. * @return the image name @@ -173,6 +175,18 @@ public void setNetwork(String network) { this.network = network; } + /** + * Returns the created date for the image. + * @return the created date + */ + public String getCreatedDate() { + return this.createdDate; + } + + public void setCreatedDate(String createdDate) { + this.createdDate = createdDate; + } + BuildRequest getBuildRequest(Artifact artifact, Function applicationContent) { return customize(BuildRequest.of(getOrDeduceName(artifact), applicationContent)); } @@ -221,6 +235,9 @@ private BuildRequest customize(BuildRequest request) { if (this.launchCache != null) { request = request.withLaunchCache(this.launchCache.asCache()); } + if (StringUtils.hasText(this.createdDate)) { + request = request.withCreatedDate(this.createdDate); + } return request; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ImageTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ImageTests.java index 3eb5f293362c..aadee4d35a9e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ImageTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/ImageTests.java @@ -185,6 +185,14 @@ void getBuildRequestWhenHasLaunchVolumeCacheUsesCache() { assertThat(request.getLaunchCache()).isEqualTo(Cache.volume("launch-cache-vol")); } + @Test + void getBuildRequestWhenHasCreatedDateUsesCreatedDate() { + Image image = new Image(); + image.createdDate = "2020-07-01T12:34:56Z"; + BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent()); + assertThat(request.getCreatedDate()).isEqualTo("2020-07-01T12:34:56Z"); + } + private Artifact createArtifact() { return new DefaultArtifact("com.example", "my-app", VersionRange.createFromVersion("0.0.1-SNAPSHOT"), "compile", "jar", null, new DefaultArtifactHandler()); diff --git a/src/spring-boot-builder/build.sh b/src/spring-boot-builder/build.sh index aaeb7ec6f48d..606638a568d9 100755 --- a/src/spring-boot-builder/build.sh +++ b/src/spring-boot-builder/build.sh @@ -10,6 +10,6 @@ docker tag paketobuildpacks/run:tiny-cnb projects.registry.vmware.com/springboot docker push projects.registry.vmware.com/springboot/run:tiny-cnb cd builder -pack builder create projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 --config builder.toml -docker push projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1 -cd - \ No newline at end of file +pack builder create projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 --config builder.toml +docker push projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2 +cd - diff --git a/src/spring-boot-builder/builder/builder.toml b/src/spring-boot-builder/builder/builder.toml index 7088dcbd7096..302ebbbe1654 100644 --- a/src/spring-boot-builder/builder/builder.toml +++ b/src/spring-boot-builder/builder/builder.toml @@ -1,17 +1,21 @@ # Buildpacks to include in builder [[buildpacks]] id = "spring-boot/test-info" -version = "0.0.1" +version = "0.0.2" uri = "../buildpacks/test-info" # Order used for detection [[order]] [[order.group]] id = "spring-boot/test-info" -version = "0.0.1" +version = "0.0.2" # Stack that will be used by the builder [stack] id = "io.paketo.stacks.tiny" build-image = "projects.registry.vmware.com/springboot/build:tiny-cnb" run-image = "projects.registry.vmware.com/springboot/run:tiny-cnb" + +# Lifecycle executable version that will be used by the builder +[lifecycle] +version = "0.15.3" \ No newline at end of file diff --git a/src/spring-boot-builder/buildpacks/test-info/buildpack.toml b/src/spring-boot-builder/buildpacks/test-info/buildpack.toml index a146a2abe873..df6b3cdde8d0 100644 --- a/src/spring-boot-builder/buildpacks/test-info/buildpack.toml +++ b/src/spring-boot-builder/buildpacks/test-info/buildpack.toml @@ -1,10 +1,10 @@ # Buildpack API version -api = "0.2" +api = "0.8" # Buildpack ID and metadata [buildpack] id = "spring-boot/test-info" -version = "0.0.1" +version = "0.0.2" name = "Spring Boot Test Info Buildpack" homepage = "https://github.com/spring-projects/spring-boot"