Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ The following table summarizes the available properties and their default values
| `builder`
| `--builder`
| Name of the builder image to use.
| `paketobuildpacks/builder-noble-java-tiny:latest`
| `paketobuildpacks/ubuntu-resolute-builder:latest`

| `trustBuilder`
| `--trustBuilder`
| Whether to treat the builder as {url-buildpacks-docs}/for-platform-operators/how-to/integrate-ci/pack/concepts/trusted_builders/#what-is-a-trusted-builder[trusted].
| `true` if the builder is one of `paketobuildpacks/builder-noble-java-tiny`, `paketobuildpacks/builder-jammy-java-tiny`, `paketobuildpacks/builder-jammy-tiny`, `paketobuildpacks/builder-jammy-base`, `paketobuildpacks/builder-jammy-full`, `paketobuildpacks/builder-jammy-buildpackless-tiny`, `paketobuildpacks/builder-jammy-buildpackless-base`, `paketobuildpacks/builder-jammy-buildpackless-full`, `gcr.io/buildpacks/builder`, `heroku/builder`; `false` otherwise.
| `true` if the builder is one of `paketobuildpacks/ubuntu-resolute-builder`, `paketobuildpacks/ubuntu-resolute-builder-buildpackless`, `paketobuildpacks/builder-noble-java-tiny`, `paketobuildpacks/builder-jammy-java-tiny`, `paketobuildpacks/builder-jammy-tiny`, `paketobuildpacks/builder-jammy-base`, `paketobuildpacks/builder-jammy-full`, `paketobuildpacks/builder-jammy-buildpackless-tiny`, `paketobuildpacks/builder-jammy-buildpackless-base`, `paketobuildpacks/builder-jammy-buildpackless-full`, `gcr.io/buildpacks/builder`, `heroku/builder`; `false` otherwise.

| `imagePlatform`
| `--imagePlatform`
Expand Down Expand Up @@ -263,8 +263,8 @@ NOTE: The plugin detects the target Java compatibility of the project using the
When using the default Paketo builder and buildpacks, the plugin instructs the buildpacks to install the same Java version.
You can override this behavior as shown in the xref:packaging-oci-image.adoc#build-image.examples.builder-configuration[builder configuration] examples.

NOTE: The default builder `paketobuildpacks/builder-noble-java-tiny:latest` contains a reduced set of system libraries and does not include a shell.
Applications that require a shell to run a start script, as might be the case when the {url-gradle-docs-application-plugin}[`application` plugin] has been applied to generate a distribution zip archive, or that depend upon a system library that is not present, should override the `runImage` configuration to use one that includes a shell and a broader set of system libraries, such as `paketobuildpacks/ubuntu-noble-run:latest`.
NOTE: The default builder `paketobuildpacks/ubuntu-resolute-builder:latest` uses the `paketobuildpacks/ubuntu-resolute-run:latest` run image, which includes a shell and a broad set of system libraries.
Applications that want a reduced footprint and attack surface, as is typically the case for native images, should override the `runImage` configuration to use one that does not include a shell, such as `paketobuildpacks/ubuntu-resolute-run-tiny:latest`.



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSetContainer;

import org.springframework.boot.gradle.tasks.bundling.BootBuildImage;
import org.springframework.boot.gradle.tasks.bundling.BootJar;

/**
Expand Down Expand Up @@ -59,6 +60,7 @@ public void execute(Project project) {
configureTestNativeBinaryClasspath(sourceSets, graalVmExtension);
copyReachabilityMetadataToBootJar(project);
configureJarManifestNativeAttribute(project);
configureBuildImageNativeEnvironment(project);
});
}

Expand Down Expand Up @@ -114,4 +116,10 @@ private void addNativeProcessedAttribute(Manifest manifest) {
manifest.getAttributes().put("Spring-Boot-Native-Processed", true);
}

private void configureBuildImageNativeEnvironment(Project project) {
project.getTasks()
.named(SpringBootPlugin.BOOT_BUILD_IMAGE_TASK_NAME, BootBuildImage.class)
.configure((buildImage) -> buildImage.getEnvironment().put("BP_NATIVE_IMAGE", "true"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ void classesGeneratedDuringAotTestProcessingAreOnTheTestNativeImageClasspath() {
projectPath("build/resources/aotTest"), projectPath("build/generated/aotTestClasses"));
}

@TestTemplate
void bootBuildImageIsConfiguredWithNativeImageEnvironment() {
assertThat(this.gradleBuild.build("bootBuildImageEnvironment").getOutput()).contains("BP_NATIVE_IMAGE = true");
}

@TestTemplate
void nativeEntryIsAddedToManifest() throws IOException {
writeDummySpringApplicationAotProcessorMainClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void whenUsingDefaultConfigurationThenRequestHasPublishDisabled() {
@Test
void whenNoBuilderIsConfiguredThenRequestHasDefaultBuilder() {
BuildRequest request = this.buildImage.createRequest();
assertThat(request.getBuilder().getName()).isEqualTo("paketobuildpacks/builder-noble-java-tiny");
assertThat(request.getBuilder().getName()).isEqualTo("paketobuildpacks/ubuntu-resolute-builder");
assertThat(request.isTrustBuilder()).isTrue();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2012-present 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.
*/

plugins {
id 'java'
id 'org.springframework.boot'
id 'org.springframework.boot.aot'
}

apply plugin: 'org.graalvm.buildtools.native'

tasks.register("bootBuildImageEnvironment") {
def environment = tasks.bootBuildImage.environment
doLast {
println "BP_NATIVE_IMAGE = ${environment.get()['BP_NATIVE_IMAGE']}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<runImage>paketobuildpacks/ubuntu-resolute-run-tiny</runImage>
</image>
</configuration>
<executions>
<execution>
<id>build-image</id>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ The following table summarizes the available parameters and their default values
| `builder` +
(`spring-boot.build-image.builder`)
| Name of the builder image to use.
| `paketobuildpacks/builder-noble-java-tiny:latest`
| `paketobuildpacks/ubuntu-resolute-builder:latest`

| `trustBuilder` +
(`spring-boot.build-image.trustBuilder`)
| Whether to treat the builder as {url-buildpacks-docs}/for-platform-operators/how-to/integrate-ci/pack/concepts/trusted_builders/#what-is-a-trusted-builder[trusted].
| `true` if the builder is one of `paketobuildpacks/builder-noble-java-tiny`, `paketobuildpacks/builder-jammy-java-tiny`, `paketobuildpacks/builder-jammy-tiny`, `paketobuildpacks/builder-jammy-base`, `paketobuildpacks/builder-jammy-full`, `paketobuildpacks/builder-jammy-buildpackless-tiny`, `paketobuildpacks/builder-jammy-buildpackless-base`, `paketobuildpacks/builder-jammy-buildpackless-full`, `gcr.io/buildpacks/builder`, `heroku/builder`; `false` otherwise.
| `true` if the builder is one of `paketobuildpacks/ubuntu-resolute-builder`, `paketobuildpacks/ubuntu-resolute-builder-buildpackless`, `paketobuildpacks/builder-noble-java-tiny`, `paketobuildpacks/builder-jammy-java-tiny`, `paketobuildpacks/builder-jammy-tiny`, `paketobuildpacks/builder-jammy-base`, `paketobuildpacks/builder-jammy-full`, `paketobuildpacks/builder-jammy-buildpackless-tiny`, `paketobuildpacks/builder-jammy-buildpackless-base`, `paketobuildpacks/builder-jammy-buildpackless-full`, `gcr.io/buildpacks/builder`, `heroku/builder`; `false` otherwise.

| `imagePlatform` +
(`spring-boot.build-image.imagePlatform`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void getBuildRequestWhenNameIsSetUsesName() {
void getBuildRequestWhenNoCustomizationsUsesDefaults() {
BuildRequest request = new Image().getBuildRequest(createArtifact(), mockApplicationContent());
assertThat(request.getName()).hasToString("docker.io/library/my-app:0.0.1-SNAPSHOT");
assertThat(request.getBuilder().toString()).contains("paketobuildpacks/builder-noble-java-tiny");
assertThat(request.getBuilder().toString()).contains("paketobuildpacks/ubuntu-resolute-builder");
assertThat(request.isTrustBuilder()).isTrue();
assertThat(request.getRunImage()).isNull();
assertThat(request.getEnv()).isEmpty();
Expand Down Expand Up @@ -111,7 +111,7 @@ void getBuildRequestWhenHasDefaultBuilderAndTrustBuilderUsesTrustBuilder() {
Image image = new Image();
image.trustBuilder = false;
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
assertThat(request.getBuilder().toString()).contains("paketobuildpacks/builder-noble-java-tiny");
assertThat(request.getBuilder().toString()).contains("paketobuildpacks/ubuntu-resolute-builder");
assertThat(request.isTrustBuilder()).isFalse();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,15 @@
*/
public class BuildRequest {

static final String DEFAULT_BUILDER_IMAGE_NAME = "paketobuildpacks/builder-noble-java-tiny";
static final String DEFAULT_BUILDER_IMAGE_NAME = "paketobuildpacks/ubuntu-resolute-builder";

static final String DEFAULT_BUILDER_IMAGE_REF = DEFAULT_BUILDER_IMAGE_NAME + ":latest";

static final String DEFAULT_BUILDER_NATIVE_RUN_IMAGE_NAME = "paketobuildpacks/ubuntu-resolute-run-tiny";

static final List<ImageReference> KNOWN_TRUSTED_BUILDERS = List.of(
ImageReference.of("paketobuildpacks/ubuntu-resolute-builder"),
ImageReference.of("paketobuildpacks/ubuntu-resolute-builder-buildpackless"),
ImageReference.of("paketobuildpacks/builder-noble-java-tiny"),
ImageReference.of("paketobuildpacks/builder-jammy-java-tiny"),
ImageReference.of("paketobuildpacks/builder-jammy-tiny"),
Expand Down Expand Up @@ -545,6 +549,28 @@ private boolean isBuilderKnownAndTrusted() {
return this.runImage;
}

/**
* Return the run image that should be used by default when no run image has been
* configured, or {@code null} if the builder's own default run image should be used.
* A native image build ({@code BP_NATIVE_IMAGE=true}) that uses the default builder
* defaults to a tiny run image.
* @return the default run image or {@code null}
*/
@Nullable ImageReference getDefaultRunImage() {
if (isNativeImageBuild() && isDefaultBuilder()) {
return ImageReference.of(DEFAULT_BUILDER_NATIVE_RUN_IMAGE_NAME).inTaggedOrDigestForm();
}
return null;
}

private boolean isNativeImageBuild() {
return "true".equalsIgnoreCase(this.env.get("BP_NATIVE_IMAGE"));
}

private boolean isDefaultBuilder() {
return this.builder.getName().equals(DEFAULT_BUILDER.getName());
}

/**
* Return the {@link Creator} the builder should use.
* @return the {@code Creator}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ private BuildRequest withRunImageIfNeeded(BuildRequest request, BuilderMetadata
if (request.getRunImage() != null) {
return request;
}
return request.withRunImage(getRunImageReference(metadata));
ImageReference defaultRunImage = request.getDefaultRunImage();
return request.withRunImage((defaultRunImage != null) ? defaultRunImage : getRunImageReference(metadata));
}

private ImageReference getRunImageReference(BuilderMetadata metadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,36 @@ void withTrustBuilderAndBuilderUpdatesTrustBuilder() throws IOException {
assertThat(request.isTrustBuilder()).isTrue();
}

@Test
void getDefaultRunImageWhenNativeImageAndDefaultBuilderReturnsTinyRunImage() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withEnv("BP_NATIVE_IMAGE", "true");
assertThat(request.getDefaultRunImage())
.hasToString("docker.io/paketobuildpacks/ubuntu-resolute-run-tiny:latest");
}

@Test
void getDefaultRunImageWhenNativeImageValueIsMixedCaseReturnsTinyRunImage() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withEnv("BP_NATIVE_IMAGE", "True");
assertThat(request.getDefaultRunImage())
.hasToString("docker.io/paketobuildpacks/ubuntu-resolute-run-tiny:latest");
}

@Test
void getDefaultRunImageWhenNotNativeImageReturnsNull() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThat(request.getDefaultRunImage()).isNull();
}

@Test
void getDefaultRunImageWhenNativeImageAndCustomBuilderReturnsNull() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of("spring/builder"))
.withEnv("BP_NATIVE_IMAGE", "true");
assertThat(request.getDefaultRunImage()).isNull();
}

@Test
void withRunImageUpdatesRunImage() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ This means you can just type a single command and quickly get a sensible image i
The resulting image doesn't contain a JVM, instead the native image is compiled statically.
This leads to smaller images.

NOTE: The CNB builder used for the images is `paketobuildpacks/builder-noble-java-tiny:latest`.
It has a small footprint and reduced attack surface. It does not include a shell and contains a reduced set of system libraries.
If you need more tools in the resulting image, you can use `paketobuildpacks/ubuntu-noble-run:latest` as the *run* image.
NOTE: The CNB builder used for the images is `paketobuildpacks/ubuntu-resolute-builder:latest`.
For native images, configure the `runImage` to use `paketobuildpacks/ubuntu-resolute-run-tiny:latest`, which has a small footprint and reduced attack surface: it does not include a shell and contains a reduced set of system libraries.
If you need more tools in the resulting image, you can use the default `paketobuildpacks/ubuntu-resolute-run:latest` as the *run* image.

NOTE: You have to build your application with at least JDK 25, because Buildpacks use the same GraalVM native-image version as the Java version used for compilation.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ Assuming an AOT processed Spring Boot executable jar built as `myproject-0.0.1-S

[source,shell]
----
$ pack build --builder paketobuildpacks/builder-noble-java-tiny \
$ pack build --builder paketobuildpacks/ubuntu-resolute-builder \
--run-image paketobuildpacks/ubuntu-resolute-run-tiny \
--path target/myproject-0.0.1-SNAPSHOT.jar \
--env 'BP_NATIVE_IMAGE=true' \
my-application:0.0.1-SNAPSHOT
Expand Down
7 changes: 7 additions & 0 deletions starter/spring-boot-starter-parent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,13 @@ publishing.publications.withType(MavenPublication) {
plugin {
delegate.groupId('org.springframework.boot')
delegate.artifactId('spring-boot-maven-plugin')
configuration {
image {
env {
delegate.'BP_NATIVE_IMAGE'('true')
}
}
}
executions {
execution {
delegate.id('process-aot')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ application {

bootBuildImage {
archiveFile = distZip.archiveFile
runImage = "paketobuildpacks/ubuntu-noble-run:latest"
runImage = "paketobuildpacks/ubuntu-resolute-run:latest"
environment = ['BP_JVM_VERSION': java.targetCompatibility.getMajorVersion()]
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ war {

bootBuildImage {
archiveFile = war.archiveFile
runImage = "paketobuildpacks/ubuntu-noble-run:latest"
runImage = "paketobuildpacks/ubuntu-resolute-run:latest"
environment = [
'BP_JVM_VERSION': java.targetCompatibility.getMajorVersion(),
'BP_TOMCAT_VERSION': '11.*',
Expand Down
Loading