Add buildpack option for image building
This commit adds configuration to the Maven and Gradle plugins to allow a list of buildpacks to be provided to the image building goal and task. Fixes gh-21722
This commit is contained in:
@@ -130,6 +130,18 @@ Acceptable values are `ALWAYS`, `NEVER`, and `IF_NOT_PRESENT`.
|
||||
| Environment variables that should be passed to the builder.
|
||||
|
|
||||
|
||||
| `buildpacks`
|
||||
|
|
||||
a|Buildpacks that the builder should use when building the image.
|
||||
Only the specified buildpacks will be used, overriding the default buildpacks included in the builder.
|
||||
Buildpack references must be in one of the following forms:
|
||||
|
||||
* Buildpack in the builder - [urn:cnb:builder:]<buildpack id>[@<version>]
|
||||
* Buildpack in a directory on the file system - [file://]<path>
|
||||
* Buildpack in a gzipped tar (.tgz) file on the file system - [file://]<path>/<file name>
|
||||
* Buildpack in an OCI image - [docker://]<host>/<repo>[:<tag>][@<digest>]
|
||||
| None, indicating the builder should use the buildpacks included in it.
|
||||
|
||||
| `cleanCache`
|
||||
| `--cleanCache`
|
||||
| Whether to clean the cache before building.
|
||||
@@ -249,6 +261,58 @@ The image name can be specified on the command line as well, as shown in this ex
|
||||
$ gradle bootBuildImage --imageName=example.com/library/my-app:v1
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[build-image-example-buildpacks]]
|
||||
==== Buildpacks
|
||||
By default, the builder will use buildpacks included in the builder image and apply them in a pre-defined order.
|
||||
An alternative set of buildpacks can be provided to apply buildpacks that are not included in the builder, or to change the order of included buildpacks.
|
||||
When one or more buildpacks are provided, only the specified buildpacks will be applied.
|
||||
|
||||
The following example instructs the builder to use a custom buildpack packaged in a `.tgz` file, followed by a buildpack included in the builder.
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Groovy
|
||||
----
|
||||
include::../gradle/packaging/boot-build-image-buildpacks.gradle[tags=buildpacks]
|
||||
----
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
include::../gradle/packaging/boot-build-image-buildpacks.gradle.kts[tags=buildpacks]
|
||||
----
|
||||
|
||||
Buildpacks can be specified in any of the forms shown below.
|
||||
|
||||
A buildpack located in a CNB Builder (version may be omitted if there is only one buildpack in the builder matching the `buildpack-id`):
|
||||
|
||||
* `urn:cnb:builder:buildpack-id`
|
||||
* `urn:cnb:builder:buildpack-id@0.0.1`
|
||||
* `buildpack-id`
|
||||
* `buildpack-id@0.0.1`
|
||||
|
||||
A path to a directory containing buildpack content (not supported on Windows):
|
||||
|
||||
* `\file:///path/to/buildpack/`
|
||||
* `/path/to/buildpack/`BootBuildImageIntegrationTests
|
||||
|
||||
A path to a gzipped tar file containing buildpack content:
|
||||
|
||||
* `\file:///path/to/buildpack.tgz`
|
||||
* `/path/to/buildpack.tgz`
|
||||
|
||||
An OCI image containing a https://buildpacks.io/docs/buildpack-author-guide/package-a-buildpack/[packaged buildpack]:
|
||||
|
||||
* `docker://example/buildpack`
|
||||
* `docker:///example/buildpack:latest`
|
||||
* `docker:///example/buildpack@sha256:45b23dee08...`
|
||||
* `example/buildpack`
|
||||
* `example/buildpack:latest`
|
||||
* `example/buildpack@sha256:45b23dee08...`
|
||||
|
||||
|
||||
|
||||
[[build-image-example-publish]]
|
||||
==== Image Publishing
|
||||
The generated image can be published to a Docker registry by enabling a `publish` option and configuring authentication for the registry using `docker.publishRegistry` properties.
|
||||
@@ -272,6 +336,8 @@ The publish option can be specified on the command line as well, as shown in thi
|
||||
$ gradle bootBuildImage --imageName=docker.example.com/library/my-app:v1 --publishImage
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[build-image-example-docker]]
|
||||
==== Docker Configuration
|
||||
If you need the plugin to communicate with the Docker daemon using a remote connection instead of the default local connection, the connection details can be provided using `docker` properties as shown in the following example:
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{gradle-project-version}'
|
||||
}
|
||||
|
||||
// tag::buildpacks[]
|
||||
bootBuildImage {
|
||||
buildpacks = ["file:///path/to/example-buildpack.tgz", "urn:cnb:builder:paketo-buildpacks/java"]
|
||||
}
|
||||
// end::buildpacks[]
|
||||
|
||||
task bootBuildImageBuildpacks {
|
||||
doFirst {
|
||||
bootBuildImage.buildpacks.each { reference -> println "$reference" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("org.springframework.boot") version "{gradle-project-version}"
|
||||
}
|
||||
|
||||
// tag::buildpacks[]
|
||||
tasks.getByName<BootBuildImage>("bootBuildImage") {
|
||||
buildpacks = listOf("file:///path/to/example-buildpack.tgz", "urn:cnb:builder:paketo-buildpacks/java")
|
||||
}
|
||||
// end::buildpacks[]
|
||||
|
||||
tasks.register("bootBuildImageBuildpacks") {
|
||||
doFirst {
|
||||
for((reference) in tasks.getByName<BootBuildImage>("bootBuildImage").buildpacks) {
|
||||
print(reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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.
|
||||
@@ -18,7 +18,9 @@ package org.springframework.boot.gradle.tasks.bundling;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import groovy.lang.Closure;
|
||||
import org.gradle.api.Action;
|
||||
@@ -28,6 +30,7 @@ import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
@@ -38,6 +41,7 @@ import org.gradle.util.ConfigureUtil;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.buildpack.platform.build.Builder;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.Creator;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
@@ -83,6 +87,8 @@ public class BootBuildImage extends DefaultTask {
|
||||
|
||||
private boolean publish;
|
||||
|
||||
private ListProperty<String> buildpacks;
|
||||
|
||||
private DockerSpec docker = new DockerSpec();
|
||||
|
||||
public BootBuildImage() {
|
||||
@@ -92,6 +98,7 @@ public class BootBuildImage extends DefaultTask {
|
||||
this.projectVersion = getProject().getObjects().property(String.class);
|
||||
Project project = getProject();
|
||||
this.projectVersion.set(getProject().provider(() -> project.getVersion().toString()));
|
||||
this.buildpacks = getProject().getObjects().listProperty(String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,6 +290,40 @@ public class BootBuildImage extends DefaultTask {
|
||||
this.publish = publish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the buildpacks that will be used when building the image.
|
||||
* @return the buildpacks
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
public List<String> getBuildpacks() {
|
||||
return this.buildpacks.getOrNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the buildpacks that will be used when building the image.
|
||||
* @param buildpacks the buildpacks
|
||||
*/
|
||||
public void setBuildpacks(List<String> buildpacks) {
|
||||
this.buildpacks.set(buildpacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the buildpacks that will be used when building the image.
|
||||
* @param buildpack the buildpack reference
|
||||
*/
|
||||
public void buildpack(String buildpack) {
|
||||
this.buildpacks.add(buildpack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entries to the environment that will be used when building the image.
|
||||
* @param buildpacks the buildpack references
|
||||
*/
|
||||
public void buildpacks(List<String> buildpacks) {
|
||||
this.buildpacks.addAll(buildpacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Docker configuration the builder will use.
|
||||
* @return docker configuration.
|
||||
@@ -316,8 +357,8 @@ public class BootBuildImage extends DefaultTask {
|
||||
if (!this.jar.isPresent()) {
|
||||
throw new GradleException("Executable jar file required for building image");
|
||||
}
|
||||
Builder builder = new Builder(this.docker.asDockerConfiguration());
|
||||
BuildRequest request = createRequest();
|
||||
Builder builder = new Builder(this.docker.asDockerConfiguration());
|
||||
builder.build(request);
|
||||
}
|
||||
|
||||
@@ -346,6 +387,7 @@ public class BootBuildImage extends DefaultTask {
|
||||
request = request.withVerboseLogging(this.verboseLogging);
|
||||
request = customizePullPolicy(request);
|
||||
request = customizePublish(request);
|
||||
request = customizeBuildpacks(request);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -398,6 +440,14 @@ public class BootBuildImage extends DefaultTask {
|
||||
return request;
|
||||
}
|
||||
|
||||
private BuildRequest customizeBuildpacks(BuildRequest request) {
|
||||
List<String> buildpacks = this.buildpacks.getOrNull();
|
||||
if (buildpacks != null && !buildpacks.isEmpty()) {
|
||||
return request.withBuildpacks(buildpacks.stream().map(BuildpackReference::of).collect(Collectors.toList()));
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private String translateTargetJavaVersion() {
|
||||
return this.targetJavaVersion.get().getMajorVersion() + ".*";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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.
|
||||
@@ -23,11 +23,21 @@ import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.gradle.testkit.runner.TaskOutcome;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
|
||||
@@ -145,7 +155,72 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithLaunchScript() {
|
||||
void buildsImageWithBuildpackFromBuilder() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
String projectName = this.gradleBuild.getProjectDir().getName();
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
|
||||
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
|
||||
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
|
||||
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
|
||||
}
|
||||
finally {
|
||||
new DockerApi().image().remove(imageReference, false);
|
||||
}
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
void buildsImageWithBuildpackFromDirectory() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
writeBuildpackContent();
|
||||
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
String projectName = this.gradleBuild.getProjectDir().getName();
|
||||
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
|
||||
assertThat(result.getOutput()).contains("---> Hello World buildpack");
|
||||
new DockerApi().image().remove(imageReference, false);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
void buildsImageWithBuildpackFromTarGzip() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
writeBuildpackContent();
|
||||
tarGzipBuildpackContent();
|
||||
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
String projectName = this.gradleBuild.getProjectDir().getName();
|
||||
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
|
||||
assertThat(result.getOutput()).contains("---> Hello World buildpack");
|
||||
new DockerApi().image().remove(imageReference, false);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void buildsImageWithBuildpackFromImage() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
String projectName = this.gradleBuild.getProjectDir().getName();
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
|
||||
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
|
||||
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
|
||||
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
|
||||
}
|
||||
finally {
|
||||
new DockerApi().image().remove(imageReference, false);
|
||||
}
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithLaunchScript() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage");
|
||||
@@ -154,7 +229,7 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithBuilderError() {
|
||||
void failsWithBuilderError() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
@@ -163,7 +238,7 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithInvalidImageName() {
|
||||
void failsWithInvalidImageName() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "--imageName=example/Invalid-Image-Name");
|
||||
@@ -173,7 +248,7 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithPublishMissingPublishRegistry() {
|
||||
void failsWithPublishMissingPublishRegistry() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "--publishImage");
|
||||
@@ -182,7 +257,7 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithWarPackaging() {
|
||||
void failsWithWarPackaging() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "-PapplyWarPlugin");
|
||||
@@ -190,6 +265,15 @@ class BootBuildImageIntegrationTests {
|
||||
assertThat(result.getOutput()).contains("Executable jar file required for building image");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWithBuildpackNotInBuilder() throws IOException {
|
||||
writeMainClass();
|
||||
writeLongNameResource();
|
||||
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT");
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
|
||||
assertThat(result.getOutput()).contains("'urn:cnb:builder:example/does-not-exist:0.0.1' not found in builder");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void buildsImageWithWarPackagingAndJarConfiguration() throws IOException {
|
||||
writeMainClass();
|
||||
@@ -210,7 +294,7 @@ class BootBuildImageIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMainClass() {
|
||||
private void writeMainClass() throws IOException {
|
||||
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
|
||||
examplePackage.mkdirs();
|
||||
File main = new File(examplePackage, "Main.java");
|
||||
@@ -230,23 +314,70 @@ class BootBuildImageIntegrationTests {
|
||||
writer.println();
|
||||
writer.println("}");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
private void writeLongNameResource() throws IOException {
|
||||
StringBuilder name = new StringBuilder();
|
||||
new Random().ints('a', 'z' + 1).limit(128).forEach((i) -> name.append((char) i));
|
||||
Path path = this.gradleBuild.getProjectDir().toPath()
|
||||
.resolve(Paths.get("src", "main", "resources", name.toString()));
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.createFile(path);
|
||||
}
|
||||
|
||||
private void writeBuildpackContent() throws IOException {
|
||||
File buildpackDir = new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world");
|
||||
buildpackDir.mkdirs();
|
||||
File descriptor = new File(buildpackDir, "buildpack.toml");
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(descriptor))) {
|
||||
writer.println("api = \"0.2\"");
|
||||
writer.println("[buildpack]");
|
||||
writer.println("id = \"example/hello-world\"");
|
||||
writer.println("version = \"0.0.1\"");
|
||||
writer.println("name = \"Hello World Buildpack\"");
|
||||
writer.println("homepage = \"https://github.com/buildpacks/samples/tree/main/buildpacks/hello-world\"");
|
||||
writer.println("[[stacks]]\n");
|
||||
writer.println("id = \"io.buildpacks.stacks.bionic\"");
|
||||
}
|
||||
File binDir = new File(buildpackDir, "bin");
|
||||
binDir.mkdirs();
|
||||
FileAttribute<Set<PosixFilePermission>> attribute = PosixFilePermissions
|
||||
.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
|
||||
File detect = Files.createFile(Paths.get(binDir.getAbsolutePath(), "detect"), attribute).toFile();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(detect))) {
|
||||
writer.println("#!/usr/bin/env bash");
|
||||
writer.println("set -eo pipefail");
|
||||
writer.println("exit 0");
|
||||
}
|
||||
File build = Files.createFile(Paths.get(binDir.getAbsolutePath(), "build"), attribute).toFile();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(build))) {
|
||||
writer.println("#!/usr/bin/env bash");
|
||||
writer.println("set -eo pipefail");
|
||||
writer.println("echo \"---> Hello World buildpack\"");
|
||||
writer.println("echo \"---> done\"");
|
||||
writer.println("exit 0");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLongNameResource() {
|
||||
StringBuilder name = new StringBuilder();
|
||||
new Random().ints('a', 'z' + 1).limit(128).forEach((i) -> name.append((char) i));
|
||||
try {
|
||||
Path path = this.gradleBuild.getProjectDir().toPath()
|
||||
.resolve(Paths.get("src", "main", "resources", name.toString()));
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.createFile(path);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
private void tarGzipBuildpackContent() throws IOException {
|
||||
Path tarGzipPath = Paths.get(this.gradleBuild.getProjectDir().getAbsolutePath(), "hello-world.tgz");
|
||||
try (TarArchiveOutputStream tar = new TarArchiveOutputStream(
|
||||
new GzipCompressorOutputStream(Files.newOutputStream(Files.createFile(tarGzipPath))))) {
|
||||
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/buildpack.toml"),
|
||||
"buildpack.toml", 0644);
|
||||
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/bin/detect"),
|
||||
"bin/detect", 0777);
|
||||
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/bin/build"),
|
||||
"bin/build", 0777);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileToTar(TarArchiveOutputStream tar, File file, String name, int mode) throws IOException {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(file, name);
|
||||
entry.setMode(mode);
|
||||
tar.putArchiveEntry(entry);
|
||||
IOUtils.copy(Files.newInputStream(file.toPath()), tar);
|
||||
tar.closeArchiveEntry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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,7 @@
|
||||
package org.springframework.boot.gradle.tasks.bundling;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -28,6 +29,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -221,4 +223,31 @@ class BootBuildImageTests {
|
||||
assertThat(this.buildImage.createRequest().getPullPolicy()).isEqualTo(PullPolicy.NEVER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenNoBuildpacksAreConfiguredThenRequestUsesDefaultBuildpacks() {
|
||||
assertThat(this.buildImage.createRequest().getBuildpacks()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBuildpacksAreConfiguredThenRequestHasBuildpacks() {
|
||||
this.buildImage.setBuildpacks(Arrays.asList("example/buildpack1", "example/buildpack2"));
|
||||
assertThat(this.buildImage.createRequest().getBuildpacks()).containsExactly(
|
||||
BuildpackReference.of("example/buildpack1"), BuildpackReference.of("example/buildpack2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEntriesAreAddedToBuildpacksThenRequestHasBuildpacks() {
|
||||
this.buildImage.buildpacks(Arrays.asList("example/buildpack1", "example/buildpack2"));
|
||||
assertThat(this.buildImage.createRequest().getBuildpacks()).containsExactly(
|
||||
BuildpackReference.of("example/buildpack1"), BuildpackReference.of("example/buildpack2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenIndividualEntriesAreAddedToBuildpacksThenRequestHasBuildpacks() {
|
||||
this.buildImage.buildpack("example/buildpack1");
|
||||
this.buildImage.buildpack("example/buildpack2");
|
||||
assertThat(this.buildImage.createRequest().getBuildpacks()).containsExactly(
|
||||
BuildpackReference.of("example/buildpack1"), BuildpackReference.of("example/buildpack2"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||
import com.sun.jna.Platform;
|
||||
import io.spring.gradle.dependencymanagement.DependencyManagementPlugin;
|
||||
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.conn.HttpClientConnectionManager;
|
||||
@@ -49,6 +50,7 @@ import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient;
|
||||
import org.jetbrains.kotlin.gradle.model.KotlinProject;
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin;
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlugin;
|
||||
import org.tomlj.Toml;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
@@ -116,7 +118,8 @@ public class GradleBuild {
|
||||
new File(pathOfJarContaining(HttpRequest.class)), new File(pathOfJarContaining(Module.class)),
|
||||
new File(pathOfJarContaining(Versioned.class)),
|
||||
new File(pathOfJarContaining(ParameterNamesModule.class)),
|
||||
new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)));
|
||||
new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)),
|
||||
new File(pathOfJarContaining(Toml.class)), new File(pathOfJarContaining(Lexer.class)));
|
||||
}
|
||||
|
||||
private String pathOfJarContaining(Class<?> type) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
bootBuildImage {
|
||||
buildpacks = [ "paketo-buildpacks/java" ]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
bootBuildImage {
|
||||
buildpacks = [ "file://${projectDir}/buildpack/hello-world" ]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
bootBuildImage {
|
||||
buildpacks = [ "gcr.io/paketo-buildpacks/java:latest" ]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
bootBuildImage {
|
||||
buildpacks = [ "file://${projectDir}/hello-world.tgz" ]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
bootBuildImage {
|
||||
buildpacks = [ "urn:cnb:builder:example/does-not-exist:0.0.1" ]
|
||||
}
|
||||
Reference in New Issue
Block a user