Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
|
104fe6e638 |
|
@ -23,7 +23,7 @@ inputs:
|
|||
java-version:
|
||||
description: 'Java version to use for the build'
|
||||
required: false
|
||||
default: '25'
|
||||
default: '24'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
|
@ -42,12 +42,12 @@ runs:
|
|||
${{ inputs.java-toolchain == 'true' && '24' || '' }}
|
||||
- name: Set Up Gradle With Read/Write Cache
|
||||
if: ${{ inputs.cache-read-only == 'false' }}
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
with:
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
with:
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
develocity-token-expiry: 4
|
||||
|
|
|
@ -21,7 +21,7 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Artifacts
|
||||
|
|
|
@ -17,7 +17,7 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Release Artifacts
|
||||
|
|
|
@ -23,13 +23,16 @@ jobs:
|
|||
toolchain: true
|
||||
- version: 21
|
||||
toolchain: true
|
||||
- version: 25
|
||||
- version: 24
|
||||
toolchain: false
|
||||
- version: 25
|
||||
early-access: true
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 25
|
||||
version: 24
|
||||
- os:
|
||||
name: ${{ github.repository == 'spring-projects/spring-boot-commercial' && 'Windows' }}
|
||||
steps:
|
||||
|
|
|
@ -75,7 +75,7 @@ jobs:
|
|||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote build
|
||||
|
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote open source build
|
||||
|
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
with:
|
||||
stable: true
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=25-librca
|
||||
java=24.0.2-librca
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"@springio/antora-xref-extension": "1.0.0-alpha.4",
|
||||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||
"patch-package": "^8.0.1"
|
||||
"patch-package": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@antora/asciidoc-loader": {
|
||||
|
@ -575,6 +575,14 @@
|
|||
"resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
|
||||
"integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
|
@ -1398,17 +1406,17 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"license": "MIT",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"dependencies": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-mkdirp-stream": {
|
||||
|
@ -1985,10 +1993,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"license": "MIT",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
|
@ -2324,30 +2331,38 @@
|
|||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"node_modules/patch-package": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz",
|
||||
"integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==",
|
||||
"license": "MIT",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
|
||||
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
|
||||
"dependencies": {
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"ci-info": "^3.7.0",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"find-yarn-workspace-root": "^2.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"json-stable-stringify": "^1.0.2",
|
||||
"klaw-sync": "^6.0.0",
|
||||
"minimist": "^1.2.6",
|
||||
"open": "^7.4.2",
|
||||
"rimraf": "^2.6.3",
|
||||
"semver": "^7.5.3",
|
||||
"slash": "^2.0.0",
|
||||
"tmp": "^0.2.4",
|
||||
"tmp": "^0.0.33",
|
||||
"yaml": "^2.2.2"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -2735,6 +2750,18 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
@ -3091,12 +3118,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||
"license": "MIT",
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"dependencies": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/to-absolute-glob": {
|
||||
|
@ -3209,7 +3238,6 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||
"patch-package": "^8.0.1"
|
||||
"patch-package": "^8.0.0"
|
||||
},
|
||||
"config": {
|
||||
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip"
|
||||
|
|
|
@ -80,6 +80,7 @@ tasks.register("integrationTest") {
|
|||
ant.propertyref(name: "ivy.class.path")
|
||||
}
|
||||
plainlistener()
|
||||
file(layout.buildDirectory.dir("test-results/integrationTest")).mkdirs()
|
||||
xmllistener(toDir: resultsDir)
|
||||
fileset(dir: layout.buildDirectory.dir("it").get().asFile.toString(), includes: "**/build.xml")
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ tasks.named("bootRun") {
|
|||
|
||||
tasks.register("configuredSystemProperties") {
|
||||
doLast {
|
||||
bootRun.systemProperties.each { k, v ->
|
||||
bootRun.systemProperties.each { k, v ->
|
||||
println "$k = $v"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,13 +141,9 @@ final class ApplicationPluginAction implements PluginApplicationAction {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void configureFileMode(CopySpec copySpec, int mode) {
|
||||
try {
|
||||
copySpec.getClass().getMethod("setFileMode", Integer.class).invoke(copySpec, Integer.valueOf(mode));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to set file mode on CopySpec", ex);
|
||||
}
|
||||
copySpec.setFileMode(mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.gradle.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
@ -32,7 +33,7 @@ import org.gradle.api.specs.Spec;
|
|||
*/
|
||||
class JarTypeFileSpec implements Spec<File> {
|
||||
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Set.of("dependencies-starter", "development-tool");
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(File file) {
|
||||
|
|
|
@ -285,6 +285,7 @@ final class JavaPluginAction implements PluginApplicationAction {
|
|||
private void configureProductionRuntimeClasspathConfiguration(Project project) {
|
||||
Configuration productionRuntimeClasspath = project.getConfigurations()
|
||||
.create(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
productionRuntimeClasspath.setVisible(false);
|
||||
Configuration runtimeClasspath = project.getConfigurations()
|
||||
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
productionRuntimeClasspath.attributes((attributes) -> {
|
||||
|
|
|
@ -104,6 +104,7 @@ class WarPluginAction implements PluginApplicationAction {
|
|||
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
|
||||
bootWar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
|
||||
});
|
||||
bootWarProvider.map(War::getClasspath);
|
||||
return bootWarProvider;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.springframework.util.Assert;
|
|||
* @since 3.0.0
|
||||
*/
|
||||
@CacheableTask
|
||||
public abstract class ProcessTestAot extends AbstractAot {
|
||||
public class ProcessTestAot extends AbstractAot {
|
||||
|
||||
private @Nullable FileCollection classpathRoots;
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.gradle.api.tasks.Nested;
|
|||
import org.gradle.api.tasks.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A Spring Boot "fat" archive task.
|
||||
*
|
||||
|
@ -135,6 +137,15 @@ public interface BootArchive extends Task {
|
|||
*/
|
||||
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used with the archive.
|
||||
* @return the loader implementation
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
Property<LoaderImplementation> getLoaderImplementation();
|
||||
|
||||
/**
|
||||
* Returns whether the JAR tools should be included as a dependency in the layered
|
||||
* archive.
|
||||
|
|
|
@ -22,24 +22,32 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.file.ConfigurableFilePermissions;
|
||||
import org.gradle.api.file.CopySpec;
|
||||
import org.gradle.api.file.FileCopyDetails;
|
||||
import org.gradle.api.file.FileTreeElement;
|
||||
import org.gradle.api.file.RelativePath;
|
||||
import org.gradle.api.internal.file.copy.CopyAction;
|
||||
import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
|
||||
import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
|
||||
import org.gradle.api.java.archives.Attributes;
|
||||
import org.gradle.api.java.archives.Manifest;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.specs.Specs;
|
||||
import org.gradle.api.tasks.WorkResult;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.util.PatternSet;
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* Support class for implementations of {@link BootArchive}.
|
||||
*
|
||||
|
@ -115,11 +123,13 @@ class BootArchiveSupport {
|
|||
return (version != null) ? version : "unknown";
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile) {
|
||||
return createCopyAction(jar, resolvedDependencies, supportsSignatureFile, null, null);
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile) {
|
||||
return createCopyAction(jar, resolvedDependencies, loaderImplementation, supportsSignatureFile, null, null);
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile,
|
||||
@Nullable LayerResolver layerResolver, @Nullable String jarmodeToolsLocation) {
|
||||
File output = jar.getArchiveFile().get().getAsFile();
|
||||
Manifest manifest = jar.getManifest();
|
||||
|
@ -135,8 +145,9 @@ class BootArchiveSupport {
|
|||
String encoding = jar.getMetadataCharset();
|
||||
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirPermissions,
|
||||
filePermissions, includeDefaultLoader, jarmodeToolsLocation, requiresUnpack, exclusions, launchScript,
|
||||
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver);
|
||||
return action;
|
||||
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver,
|
||||
loaderImplementation);
|
||||
return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
|
||||
}
|
||||
|
||||
private @Nullable Integer getUnixNumericDirPermissions(CopySpec copySpec) {
|
||||
|
@ -153,22 +164,14 @@ class BootArchiveSupport {
|
|||
return permissions.isPresent() ? permissions.get().toUnixNumeric() : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private @Nullable Integer getDirMode(CopySpec copySpec) {
|
||||
try {
|
||||
return (Integer) copySpec.getClass().getMethod("getDirMode").invoke(copySpec);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get dir mode from CopySpec", ex);
|
||||
}
|
||||
return copySpec.getDirMode();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private @Nullable Integer getFileMode(CopySpec copySpec) {
|
||||
try {
|
||||
return (Integer) copySpec.getClass().getMethod("getFileMode").invoke(copySpec);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get file mode from CopySpec", ex);
|
||||
}
|
||||
return copySpec.getFileMode();
|
||||
}
|
||||
|
||||
private boolean isUsingDefaultLoader(Jar jar) {
|
||||
|
@ -231,4 +234,26 @@ class BootArchiveSupport {
|
|||
details.setRelativePath(details.getRelativeSourcePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CopyAction} variant that sorts entries to ensure reproducible ordering.
|
||||
*/
|
||||
private static final class ReproducibleOrderingCopyAction implements CopyAction {
|
||||
|
||||
private final CopyAction delegate;
|
||||
|
||||
private ReproducibleOrderingCopyAction(CopyAction delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkResult execute(CopyActionProcessingStream stream) {
|
||||
return this.delegate.execute((action) -> {
|
||||
Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();
|
||||
stream.process((details) -> detailsByPath.put(details.getRelativePath(), details));
|
||||
detailsByPath.values().forEach(action::processFile);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ import org.gradle.api.tasks.bundling.Jar;
|
|||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link Jar} task that produces a Spring Boot executable jar.
|
||||
*
|
||||
|
@ -143,12 +145,13 @@ public abstract class BootJar extends Jar implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
LayerResolver layerResolver = null;
|
||||
if (!isLayeredDisabled()) {
|
||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
}
|
||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, true, layerResolver,
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, true, layerResolver,
|
||||
jarmodeToolsLocation);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ import org.gradle.api.tasks.bundling.War;
|
|||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link War} task that produces a Spring Boot executable war.
|
||||
*
|
||||
|
@ -117,13 +119,14 @@ public abstract class BootWar extends War implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
LayerResolver layerResolver = null;
|
||||
if (!isLayeredDisabled()) {
|
||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
}
|
||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, false, layerResolver,
|
||||
jarmodeToolsLocation);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, false,
|
||||
layerResolver, jarmodeToolsLocation);
|
||||
}
|
||||
|
||||
private boolean isIncludeJarmodeTools() {
|
||||
|
|
|
@ -23,10 +23,13 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HexFormat;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
@ -60,6 +63,7 @@ import org.springframework.boot.loader.tools.JarModeLibrary;
|
|||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.LayersIndex;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.NativeImageArgFile;
|
||||
import org.springframework.boot.loader.tools.ReachabilityMetadataProperties;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -116,13 +120,15 @@ class BootZipCopyAction implements CopyAction {
|
|||
|
||||
private final @Nullable LayerResolver layerResolver;
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, @Nullable Integer dirMode,
|
||||
@Nullable Integer fileMode, boolean includeDefaultLoader, @Nullable String jarmodeToolsLocation,
|
||||
Spec<FileTreeElement> requiresUnpack, Spec<FileTreeElement> exclusions,
|
||||
@Nullable LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
|
||||
Function<FileCopyDetails, ZipCompression> compressionResolver, @Nullable String encoding,
|
||||
ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||
@Nullable LayerResolver layerResolver) {
|
||||
@Nullable LayerResolver layerResolver, LoaderImplementation loaderImplementation) {
|
||||
this.output = output;
|
||||
this.manifest = manifest;
|
||||
this.preserveFileTimestamps = preserveFileTimestamps;
|
||||
|
@ -139,6 +145,7 @@ class BootZipCopyAction implements CopyAction {
|
|||
this.resolvedDependencies = resolvedDependencies;
|
||||
this.supportsSignatureFile = supportsSignatureFile;
|
||||
this.layerResolver = layerResolver;
|
||||
this.loaderImplementation = loaderImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -322,7 +329,8 @@ class BootZipCopyAction implements CopyAction {
|
|||
// Always write loader entries after META-INF directory (see gh-16698)
|
||||
return;
|
||||
}
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode());
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode(),
|
||||
BootZipCopyAction.this.loaderImplementation);
|
||||
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
|
||||
if (BootZipCopyAction.this.layerResolver != null) {
|
||||
for (String name : this.writtenLoaderEntries.getFiles()) {
|
||||
|
@ -504,13 +512,9 @@ class BootZipCopyAction implements CopyAction {
|
|||
? details.getPermissions().toUnixNumeric() : getMode(details);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private int getMode(FileCopyDetails details) {
|
||||
try {
|
||||
return (int) details.getClass().getMethod("getMode").invoke(details);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get mode from FileCopyDetails", ex);
|
||||
}
|
||||
return details.getMode();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -586,24 +590,36 @@ class BootZipCopyAction implements CopyAction {
|
|||
|
||||
private static final int BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
private final boolean unpack;
|
||||
private final @Nullable MessageDigest messageDigest;
|
||||
|
||||
private final CRC32 crc = new CRC32();
|
||||
|
||||
private long size;
|
||||
|
||||
StoredEntryPreparator(InputStream inputStream, boolean unpack) throws IOException {
|
||||
this.unpack = unpack;
|
||||
this.messageDigest = (unpack) ? sha1Digest() : null;
|
||||
try (inputStream) {
|
||||
load(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageDigest sha1Digest() {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA-1");
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void load(InputStream inputStream) throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
this.crc.update(buffer, 0, bytesRead);
|
||||
if (this.messageDigest != null) {
|
||||
this.messageDigest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
this.size += bytesRead;
|
||||
}
|
||||
}
|
||||
|
@ -613,8 +629,8 @@ class BootZipCopyAction implements CopyAction {
|
|||
entry.setCompressedSize(this.size);
|
||||
entry.setCrc(this.crc.getValue());
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
if (this.unpack) {
|
||||
entry.setComment("UNPACK");
|
||||
if (this.messageDigest != null) {
|
||||
entry.setComment("UNPACK:" + HexFormat.of().formatHex(this.messageDigest.digest()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
|||
import org.gradle.api.file.FileTreeElement;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
|
@ -41,22 +42,27 @@ import org.springframework.util.StreamUtils;
|
|||
*/
|
||||
class LoaderZipEntries {
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
private final @Nullable Long entryTime;
|
||||
|
||||
private final int dirMode;
|
||||
|
||||
private final int fileMode;
|
||||
|
||||
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode) {
|
||||
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode,
|
||||
@Nullable LoaderImplementation loaderImplementation) {
|
||||
this.entryTime = entryTime;
|
||||
this.dirMode = dirMode;
|
||||
this.fileMode = fileMode;
|
||||
this.loaderImplementation = (loaderImplementation != null) ? loaderImplementation
|
||||
: LoaderImplementation.DEFAULT;
|
||||
}
|
||||
|
||||
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
|
||||
WrittenEntries written = new WrittenEntries();
|
||||
try (ZipInputStream loaderJar = new ZipInputStream(
|
||||
getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
|
||||
getResourceAsStream("/" + this.loaderImplementation.getJarResourceName()))) {
|
||||
java.util.zip.ZipEntry entry = loaderJar.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
|
||||
|
|
|
@ -124,7 +124,7 @@ class PackagingDocumentationTests {
|
|||
try (JarFile jar = new JarFile(file)) {
|
||||
JarEntry entry = jar.getJarEntry("BOOT-INF/lib/jruby-complete-1.7.25.jar");
|
||||
assertThat(entry).isNotNull();
|
||||
assertThat(entry.getComment()).isEqualTo("UNPACK");
|
||||
assertThat(entry.getComment()).startsWith("UNPACK:");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.time.format.DateTimeFormatter;
|
|||
import java.util.Properties;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.internal.project.ProjectInternal;
|
||||
import org.gradle.initialization.GradlePropertiesController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
|
@ -171,7 +173,11 @@ class BuildInfoTests {
|
|||
|
||||
private Project createProject(String projectName) {
|
||||
File projectDir = new File(this.temp, projectName);
|
||||
return GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
||||
Project project = GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
||||
((ProjectInternal) project).getServices()
|
||||
.get(GradlePropertiesController.class)
|
||||
.loadGradlePropertiesFrom(projectDir, false);
|
||||
return project;
|
||||
}
|
||||
|
||||
private BuildInfo createTask(Project project) {
|
||||
|
|
|
@ -108,6 +108,16 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
assertThat(firstHash).isEqualTo(secondHash);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void classicLoader() throws IOException {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
.isEqualTo(TaskOutcome.SUCCESS);
|
||||
File jar = new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0];
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void upToDateWhenBuiltTwice() {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
|
@ -233,8 +243,14 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
.filter((entry) -> !entry.isDirectory())
|
||||
.map(JarEntry::getName)
|
||||
.filter((name) -> name.startsWith(this.libPath));
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
||||
this.libPath + "commons-io-2.19.0.jar");
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
||||
this.libPath + "commons-io-2.19.0.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "commons-io-2.19.0.jar",
|
||||
this.libPath + "two-1.0.jar");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +293,6 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
void jarTypeFilteringIsApplied() throws IOException {
|
||||
File flatDirRepository = new File(this.gradleBuild.getProjectDir(), "repository");
|
||||
createDependenciesStarterJar(new File(flatDirRepository, "starter.jar"));
|
||||
createDependenciesDeveloperToolsJar(new File(flatDirRepository, "devonly.jar"));
|
||||
createStandardJar(new File(flatDirRepository, "standard.jar"));
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
.isEqualTo(TaskOutcome.SUCCESS);
|
||||
|
@ -654,10 +669,6 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "dependencies-starter"));
|
||||
}
|
||||
|
||||
private void createDependenciesDeveloperToolsJar(File location) throws IOException {
|
||||
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "development-tool"));
|
||||
}
|
||||
|
||||
private void createJar(File location, Consumer<Attributes> attributesConfigurer) throws IOException {
|
||||
location.getParentFile().mkdirs();
|
||||
Manifest manifest = new Manifest();
|
||||
|
|
|
@ -27,8 +27,6 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
@ -37,7 +35,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
@ -68,6 +65,7 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||
import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -285,6 +283,17 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void loaderIsWrittenToTheRootOfTheJarWhenUsingClassicLoader() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.getLoaderImplementation().set(LoaderImplementation.CLASSIC);
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
|
@ -292,7 +301,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
this.task.requiresUnpack("**/one.jar");
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isEqualTo("UNPACK");
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).startsWith("UNPACK:");
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isNull();
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +313,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar"));
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isEqualTo("UNPACK");
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).startsWith("UNPACK:");
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isNull();
|
||||
}
|
||||
}
|
||||
|
@ -410,46 +419,23 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
}
|
||||
|
||||
@Test
|
||||
void archiveIsReproducibleByDefault() throws IOException {
|
||||
void reproducibleOrderingCanBeEnabled() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||
this.task.from(newFile("bravo.txt"), newFile("alpha.txt"), newFile("charlie.txt"));
|
||||
this.task.setReproducibleFileOrder(true);
|
||||
executeTask();
|
||||
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||
List<String> files = new ArrayList<>();
|
||||
List<String> textFiles = new ArrayList<>();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
assertThat(entry.getLastModifiedTime().toMillis())
|
||||
.isEqualTo(ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES);
|
||||
if (entry.getName().startsWith("files/")) {
|
||||
files.add(entry.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(files).containsExactly("files/", "files/a/", "files/a/alpha.txt", "files/b/", "files/b/bravo.txt",
|
||||
"files/c/", "files/c/charlie.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void archiveReproducibilityCanBeDisabled() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||
this.task.setPreserveFileTimestamps(true);
|
||||
this.task.setReproducibleFileOrder(false);
|
||||
executeTask();
|
||||
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry.getName().endsWith(".txt") || entry.getName().startsWith("BOOT-INF/lib/")) {
|
||||
OffsetDateTime lastModifiedTime = entry.getLastModifiedTime().toInstant().atOffset(ZoneOffset.UTC);
|
||||
assertThat(lastModifiedTime)
|
||||
.isNotEqualTo(OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC));
|
||||
if (entry.getName().endsWith(".txt")) {
|
||||
textFiles.add(entry.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -689,19 +675,6 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
return entryNames;
|
||||
}
|
||||
|
||||
protected File newFiles(String... names) throws IOException {
|
||||
File dir = new File(this.temp, UUID.randomUUID().toString());
|
||||
dir.mkdir();
|
||||
List<File> files = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
File file = new File(dir, name);
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
files.add(file);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
protected File newFile(String name) throws IOException {
|
||||
File file = new File(this.temp, name);
|
||||
file.createNewFile();
|
||||
|
|
|
@ -30,8 +30,8 @@ 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 org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||
|
||||
|
|
|
@ -66,10 +66,18 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
|||
copyClasspathApplication();
|
||||
BuildResult result = this.gradleBuild.build("launch");
|
||||
String output = result.getOutput();
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
}
|
||||
assertThat(output).doesNotContain("5. ");
|
||||
}
|
||||
|
||||
|
@ -78,10 +86,18 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
|||
copyClasspathApplication();
|
||||
BuildResult result = this.gradleBuild.build("launch");
|
||||
String output = result.getOutput();
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*library-1.0-SNAPSHOT.jar");
|
||||
}
|
||||
assertThat(output).doesNotContain("5. ");
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ bootJar {
|
|||
baseName = 'foo'
|
||||
}
|
||||
else {
|
||||
archiveBaseName = 'foo'
|
||||
archiveBaseName = 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ bootWar {
|
|||
baseName = 'foo'
|
||||
}
|
||||
else {
|
||||
archiveBaseName = 'foo'
|
||||
archiveBaseName = 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ dependencies {
|
|||
}
|
||||
|
||||
task('processTestAotClasspath') {
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
doFirst {
|
||||
configurations.processTestAotClasspath.files.each { println it }
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ dependencies {
|
|||
}
|
||||
|
||||
task('processTestAotClasspath') {
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
doFirst {
|
||||
configurations.processTestAotClasspath.files.each { println it }
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
|
||||
plugins {
|
||||
id "org.springframework.boot.starter"
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
description = "Starter for testing aspect-oriented programming with AspectJ"
|
||||
|
||||
dependencies {
|
||||
api(project(":starter:spring-boot-starter-test"))
|
||||
bootJar {
|
||||
mainClass = 'com.example.Application'
|
||||
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
|
||||
}
|
|
@ -33,7 +33,7 @@ bootJar {
|
|||
include "*:*:*SNAPSHOT"
|
||||
}
|
||||
intoLayer("commons-dependencies") {
|
||||
include "org.apache.commons:*"
|
||||
include "org.apache.commons:*"
|
||||
}
|
||||
intoLayer("dependencies")
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ bootJar {
|
|||
includeProjectDependencies()
|
||||
}
|
||||
intoLayer("commons-dependencies") {
|
||||
include "org.apache.commons:*"
|
||||
include "org.apache.commons:*"
|
||||
}
|
||||
intoLayer("dependencies")
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ plugins {
|
|||
|
||||
bootJar {
|
||||
mainClass = 'com.example.Application'
|
||||
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
|
||||
plugins {
|
||||
id "org.springframework.boot.starter"
|
||||
id 'war'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
description = ""
|
||||
|
||||
dependencies {
|
||||
api(project(":starter:spring-boot-starter-test"))
|
||||
bootWar {
|
||||
mainClass = 'com.example.Application'
|
||||
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
|
||||
}
|
|
@ -21,8 +21,6 @@ plugins {
|
|||
|
||||
bootWar {
|
||||
mainClass = 'com.example.Application'
|
||||
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<!-- tag::different-versions[] -->
|
||||
<properties>
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<spring-data-bom.version>2024.1.10</spring-data-bom.version>
|
||||
<spring-data-releasetrain.version>Moore-SR6</spring-data-releasetrain.version>
|
||||
</properties>
|
||||
<!-- end::different-versions[] -->
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
<!-- Override Spring Data release train provided by Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-bom</artifactId>
|
||||
<version>2024.1.10</version>
|
||||
<artifactId>spring-data-releasetrain</artifactId>
|
||||
<version>2020.0.0-SR1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -67,9 +67,6 @@ include::example$using/different-versions-pom.xml[tags=different-versions]
|
|||
|
||||
Browse the xref:appendix:dependency-versions/properties.adoc[Dependency Versions Properties] section in the Spring Boot reference for a complete list of dependency version properties.
|
||||
|
||||
WARNING: Each Spring Boot release is designed and tested against a specific set of third-party dependencies.
|
||||
Overriding versions may cause compatibility issues and should be done with care.
|
||||
|
||||
|
||||
|
||||
[[using.import]]
|
||||
|
|
|
@ -159,7 +159,7 @@ abstract class AbstractArchiveIntegrationTests {
|
|||
Optional<JarEntry> match = entries.filter((entry) -> entry.getName().startsWith(prefix))
|
||||
.findFirst();
|
||||
assertThat(match).as("Name starting with %s", prefix)
|
||||
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).isEqualTo("UNPACK"));
|
||||
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).startsWith("UNPACK:"));
|
||||
});
|
||||
});
|
||||
return this;
|
||||
|
|
|
@ -76,6 +76,26 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenJarWithClassicLoaderIsRepackagedInPlaceOnlyRepackagedJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-with-classic-loader").goals("install").execute((project) -> {
|
||||
File original = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar.original");
|
||||
assertThat(original).isFile();
|
||||
File repackaged = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(launchScript(repackaged)).isEmpty();
|
||||
assertThat(jar(repackaged)).manifest((manifest) -> {
|
||||
manifest.hasMainClass("org.springframework.boot.loader.launch.JarLauncher");
|
||||
manifest.hasStartClass("some.random.Main");
|
||||
manifest.hasAttribute("Not-Used", "Foo");
|
||||
}).hasEntryWithName("org/springframework/boot/loader/launch/JarLauncher.class");
|
||||
assertThat(buildLog(project))
|
||||
.contains("Replacing main artifact " + repackaged + " with repackaged archive,")
|
||||
.contains("The original artifact has been renamed to " + original)
|
||||
.contains("Installing " + repackaged + " to")
|
||||
.doesNotContain("Installing " + original + " to");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAttachIsDisabledOnlyTheOriginalJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-attach-disabled").goals("install").execute((project) -> {
|
||||
|
@ -176,40 +196,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/servlet-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalByDefaultDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-default").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-default-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalAndOptionalsIncludedAppearsInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-include").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-include-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalAndOptionalsExcludedDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-exclude").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-exclude-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/servlet-api-2.5.jar");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -261,8 +248,9 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
File repackaged = new File(project, "target/jar-exclude-group-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-");
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-2.4.1.jar");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -400,7 +388,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
assertThat(layerIndex.get("application")).contains("BOOT-INF/lib/jar-release-0.0.1.RELEASE.jar",
|
||||
"BOOT-INF/lib/jar-snapshot-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(layerIndex.get("dependencies"))
|
||||
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-"));
|
||||
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-2"));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-optional-default</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-optional-include</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeOptional>true</includeOptional>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -3,7 +3,7 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-optional-exclude</artifactId>
|
||||
<artifactId>jar-with-classic-loader</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeOptional>false</includeOptional>
|
||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -31,6 +31,16 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>some.random.Main</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Not-Used>Foo</Not-Used>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
@ -41,10 +51,10 @@
|
|||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>@jakarta-servlet.version@</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -47,6 +47,7 @@ import org.springframework.boot.loader.tools.Layouts.Jar;
|
|||
import org.springframework.boot.loader.tools.Layouts.None;
|
||||
import org.springframework.boot.loader.tools.Layouts.War;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Packager;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
|
||||
|
@ -113,13 +114,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
@Parameter(defaultValue = "false")
|
||||
public boolean includeSystemScope;
|
||||
|
||||
/**
|
||||
* Include optional dependencies.
|
||||
* @since 3.5.7
|
||||
*/
|
||||
@Parameter(defaultValue = "false")
|
||||
public boolean includeOptional;
|
||||
|
||||
/**
|
||||
* Include JAR tools.
|
||||
* @since 3.3.0
|
||||
|
@ -148,6 +142,15 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loader implementation that should be used.
|
||||
* @return the loader implementation or {@code null}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the {@link LayoutType} if
|
||||
* no explicit layout is set.
|
||||
|
@ -165,6 +168,7 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
*/
|
||||
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
||||
P packager = supplier.get();
|
||||
packager.setLoaderImplementation(getLoaderImplementation());
|
||||
packager.setLayoutFactory(getLayoutFactory());
|
||||
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
||||
packager.setMainClass(this.mainClass);
|
||||
|
@ -227,9 +231,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
if (!this.includeSystemScope) {
|
||||
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
||||
}
|
||||
if (!this.includeOptional) {
|
||||
filters.add(DependencyFilter.exclude(Artifact::isOptional));
|
||||
}
|
||||
return filters.toArray(new ArtifactsFilter[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.springframework.boot.loader.tools.EntryWriter;
|
|||
import org.springframework.boot.loader.tools.ImagePackager;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
@ -207,6 +208,13 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
@Parameter
|
||||
private @Nullable LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private @Nullable LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
@ -230,6 +238,11 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter;
|
||||
|
@ -83,22 +81,4 @@ public abstract class DependencyFilter extends AbstractArtifactsFilter {
|
|||
return this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link DependencyFilter} the excludes artifacts based on the given
|
||||
* predicate.
|
||||
* @param filter the predicate used to filter the artifacts.
|
||||
* @return a new {@link DependencyFilter} instance
|
||||
* @since 3.5.7
|
||||
*/
|
||||
public static DependencyFilter exclude(Predicate<Artifact> filter) {
|
||||
return new DependencyFilter(Collections.emptyList()) {
|
||||
|
||||
@Override
|
||||
protected boolean filter(Artifact artifact) {
|
||||
return filter.test(artifact);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ import org.apache.maven.artifact.Artifact;
|
|||
*/
|
||||
class JarTypeFilter extends DependencyFilter {
|
||||
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter", "development-tool")));
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections
|
||||
.unmodifiableSet(new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter")));
|
||||
|
||||
JarTypeFilter() {
|
||||
super(Collections.emptyList());
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
|||
import org.springframework.boot.loader.tools.LaunchScript;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Repackager;
|
||||
import org.springframework.lang.Contract;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -169,6 +170,13 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
@Parameter(property = "spring-boot.repackage.layout")
|
||||
private @Nullable LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private @Nullable LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
@ -193,6 +201,11 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.artifact.DefaultArtifact;
|
||||
import org.apache.maven.artifact.handler.ArtifactHandler;
|
||||
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DependencyFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DependencyFilterTests {
|
||||
|
||||
@Test
|
||||
void excludeFiltersBasedOnPredicate() throws ArtifactFilterException {
|
||||
DependencyFilter filter = DependencyFilter.exclude(Artifact::isOptional);
|
||||
ArtifactHandler ah = new DefaultArtifactHandler();
|
||||
VersionRange v = VersionRange.createFromVersion("1.0.0");
|
||||
DefaultArtifact a1 = new DefaultArtifact("com.example", "a1", v, "compile", "jar", null, ah, false);
|
||||
DefaultArtifact a2 = new DefaultArtifact("com.example", "a2", v, "compile", "jar", null, ah, true);
|
||||
DefaultArtifact a3 = new DefaultArtifact("com.example", "a3", v, "compile", "jar", null, ah, false);
|
||||
Set<Artifact> filtered = filter.filter(Set.of(a1, a2, a3));
|
||||
assertThat(filtered).containsExactlyInAnyOrder(a1, a3);
|
||||
}
|
||||
|
||||
}
|
|
@ -31,8 +31,8 @@ import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
|||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.Cache;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -60,11 +60,6 @@ class JarTypeFilterTests {
|
|||
assertThat(new JarTypeFilter().filter(createArtifact("annotation-processor"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenArtifactHasDevelopmentToolJarTypeThenItIsExcluded() {
|
||||
assertThat(new JarTypeFilter().filter(createArtifact("development-tool"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenArtifactHasNoManifestFileThenItIsIncluded() {
|
||||
assertThat(new JarTypeFilter().filter(createArtifactWithNoManifest())).isFalse();
|
||||
|
|
|
@ -52,7 +52,7 @@ dependencies {
|
|||
implementation("commons-codec:commons-codec:${commonsCodecVersion}")
|
||||
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
||||
implementation("dev.adamko.dokkatoo:dokkatoo-plugin:2.3.1")
|
||||
implementation("dev.detekt:detekt-gradle-plugin:2.0.0-alpha.0")
|
||||
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8")
|
||||
implementation("io.spring.gradle.antora:spring-antora-plugin:0.0.1")
|
||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||
implementation("io.spring.nohttp:nohttp-gradle:0.0.11")
|
||||
|
@ -133,6 +133,10 @@ gradlePlugin {
|
|||
id = "org.springframework.boot.integration-test"
|
||||
implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
|
||||
}
|
||||
systemTestPlugin {
|
||||
id = "org.springframework.boot.system-test"
|
||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||
}
|
||||
mavenPluginPlugin {
|
||||
id = "org.springframework.boot.maven-plugin"
|
||||
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
||||
|
@ -149,22 +153,10 @@ gradlePlugin {
|
|||
id = "org.springframework.boot.starter"
|
||||
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
||||
}
|
||||
systemTestPlugin {
|
||||
id = "org.springframework.boot.system-test"
|
||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||
}
|
||||
testAutoConfigurationPlugin {
|
||||
id = "org.springframework.boot.test-auto-configuration"
|
||||
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestAutoConfigurationPlugin"
|
||||
}
|
||||
testFailuresPlugin {
|
||||
id = "org.springframework.boot.test-failures"
|
||||
implementationClass = "org.springframework.boot.build.testing.TestFailuresPlugin"
|
||||
}
|
||||
testSlicePlugin {
|
||||
id = "org.springframework.boot.test-slice"
|
||||
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestSlicePlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ import java.net.URI;
|
|||
|
||||
import dev.adamko.dokkatoo.DokkatooExtension;
|
||||
import dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin;
|
||||
import dev.detekt.gradle.Detekt;
|
||||
import dev.detekt.gradle.extensions.DetektExtension;
|
||||
import dev.detekt.gradle.plugin.DetektPlugin;
|
||||
import io.gitlab.arturbosch.detekt.Detekt;
|
||||
import io.gitlab.arturbosch.detekt.DetektPlugin;
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
@ -76,7 +76,6 @@ class KotlinConventions {
|
|||
|
||||
private void configureDokkatoo(Project project) {
|
||||
DokkatooExtension dokkatoo = project.getExtensions().getByType(DokkatooExtension.class);
|
||||
dokkatoo.getVersions().getJetbrainsDokka().set("2.1.0-Beta");
|
||||
dokkatoo.getDokkatooSourceSets().configureEach((sourceSet) -> {
|
||||
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
||||
sourceSet.getSourceRoots().setFrom(project.file("src/main/kotlin"));
|
||||
|
@ -104,9 +103,7 @@ class KotlinConventions {
|
|||
project.getPlugins().apply(DetektPlugin.class);
|
||||
DetektExtension detekt = project.getExtensions().getByType(DetektExtension.class);
|
||||
detekt.getConfig().setFrom(project.getRootProject().file("config/detekt/config.yml"));
|
||||
project.getTasks()
|
||||
.withType(Detekt.class)
|
||||
.configureEach((task) -> task.getJvmTarget().set(JVM_TARGET.getTarget()));
|
||||
project.getTasks().withType(Detekt.class).configureEach((task) -> task.setJvmTarget(JVM_TARGET.getTarget()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,13 +80,6 @@ final class ArchitectureRules {
|
|||
|
||||
private static final String AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration";
|
||||
|
||||
private static final String TEST_AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.test.autoconfigure.TestAutoConfiguration";
|
||||
|
||||
private static final Predicate<JavaPackage> NULL_MARKED_PACKAGE_FILTER = (candidate) -> !List
|
||||
.of("org.springframework.boot.cli.json", "org.springframework.boot.configurationmetadata.json",
|
||||
"org.springframework.boot.configurationprocessor.json")
|
||||
.contains(candidate.getName());
|
||||
|
||||
private ArchitectureRules() {
|
||||
}
|
||||
|
||||
|
@ -112,14 +105,13 @@ final class ArchitectureRules {
|
|||
rules.add(noClassesShouldCallStringToUpperCaseWithoutLocale());
|
||||
rules.add(noClassesShouldCallStringToLowerCaseWithoutLocale());
|
||||
rules.add(conditionalOnMissingBeanShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodReturnType());
|
||||
rules.add(enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter());
|
||||
rules.add(enumSourceShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodParameterType());
|
||||
rules.add(classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute());
|
||||
rules.add(methodLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute());
|
||||
rules.add(conditionsShouldNotBePublic());
|
||||
rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic());
|
||||
rules.add(autoConfigurationClassesShouldBePublicAndFinal());
|
||||
rules.add(autoConfigurationClassesShouldHaveNoPublicMembers());
|
||||
rules.add(testAutoConfigurationClassesShouldBePackagePrivateAndFinal());
|
||||
return List.copyOf(rules);
|
||||
}
|
||||
|
||||
|
@ -138,12 +130,7 @@ final class ArchitectureRules {
|
|||
}
|
||||
|
||||
private static ArchRule allPackagesShouldBeFreeOfTangles() {
|
||||
return SlicesRuleDefinition.slices()
|
||||
.matching("(**)")
|
||||
.should()
|
||||
.beFreeOfCycles()
|
||||
.ignoreDependency("org.springframework.boot.env.EnvironmentPostProcessor",
|
||||
"org.springframework.boot.SpringApplication");
|
||||
return SlicesRuleDefinition.slices().matching("(**)").should().beFreeOfCycles();
|
||||
}
|
||||
|
||||
private static ArchRule allBeanPostProcessorBeanMethodsShouldBeStaticAndNotCausePrematureInitialization() {
|
||||
|
@ -263,9 +250,7 @@ final class ArchitectureRules {
|
|||
}
|
||||
|
||||
static ArchRule packagesShouldBeAnnotatedWithNullMarked() {
|
||||
return ArchRuleDefinition.all(packages(NULL_MARKED_PACKAGE_FILTER))
|
||||
.should(beAnnotatedWithNullMarked())
|
||||
.allowEmptyShould(true);
|
||||
return ArchRuleDefinition.all(packages()).should(beAnnotatedWithNullMarked()).allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() {
|
||||
|
@ -273,7 +258,7 @@ final class ArchitectureRules {
|
|||
JavaAnnotation<JavaMethod> conditionalAnnotation = item
|
||||
.getAnnotationOfType("org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean");
|
||||
Map<String, Object> properties = conditionalAnnotation.getProperties();
|
||||
if (!hasProperty("type", properties) && !hasProperty("name", properties)) {
|
||||
if (!properties.containsKey("type") && !properties.containsKey("name")) {
|
||||
conditionalAnnotation.get("value").ifPresent((value) -> {
|
||||
if (containsOnlySingleType((JavaType[]) value, item.getReturnType())) {
|
||||
addViolation(events, item, conditionalAnnotation.getDescription()
|
||||
|
@ -284,24 +269,16 @@ final class ArchitectureRules {
|
|||
});
|
||||
}
|
||||
|
||||
private static boolean hasProperty(String name, Map<String, Object> properties) {
|
||||
Object property = properties.get(name);
|
||||
if (property == null) {
|
||||
return false;
|
||||
}
|
||||
return !property.getClass().isArray() || ((Object[]) property).length > 0;
|
||||
}
|
||||
|
||||
private static ArchRule enumSourceShouldNotHaveValueThatIsTheSameAsTypeOfMethodsFirstParameter() {
|
||||
private static ArchRule enumSourceShouldNotSpecifyOnlyATypeThatIsTheSameAsMethodParameterType() {
|
||||
return ArchRuleDefinition.methods()
|
||||
.that()
|
||||
.areAnnotatedWith("org.junit.jupiter.params.provider.EnumSource")
|
||||
.should(notHaveValueThatIsTheSameAsTheTypeOfTheMethodsFirstParameter())
|
||||
.should(notSpecifyOnlyATypeThatIsTheSameAsTheMethodParameterType())
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static ArchCondition<? super JavaMethod> notHaveValueThatIsTheSameAsTheTypeOfTheMethodsFirstParameter() {
|
||||
return check("not have a value that is the same as the type of the method's first parameter",
|
||||
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodParameterType() {
|
||||
return check("not specify only a type that is the same as the method's parameter type",
|
||||
ArchitectureRules::notSpecifyOnlyATypeThatIsTheSameAsTheMethodParameterType);
|
||||
}
|
||||
|
||||
|
@ -309,13 +286,15 @@ final class ArchitectureRules {
|
|||
ConditionEvents events) {
|
||||
JavaAnnotation<JavaMethod> enumSourceAnnotation = item
|
||||
.getAnnotationOfType("org.junit.jupiter.params.provider.EnumSource");
|
||||
enumSourceAnnotation.get("value").ifPresent((value) -> {
|
||||
JavaType parameterType = item.getParameterTypes().get(0);
|
||||
if (value.equals(parameterType)) {
|
||||
addViolation(events, item, enumSourceAnnotation.getDescription()
|
||||
+ " should not specify a value that is the same as the type of the method's first parameter");
|
||||
}
|
||||
});
|
||||
Map<String, Object> properties = enumSourceAnnotation.getProperties();
|
||||
if (properties.size() == 1 && item.getParameterTypes().size() == 1) {
|
||||
enumSourceAnnotation.get("value").ifPresent((value) -> {
|
||||
if (value.equals(item.getParameterTypes().get(0))) {
|
||||
addViolation(events, item, enumSourceAnnotation.getDescription()
|
||||
+ " should not specify only a value that is the same as the method's parameter type");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static ArchRule classLevelConfigurationPropertiesShouldNotSpecifyOnlyPrefixAttribute() {
|
||||
|
@ -374,7 +353,8 @@ final class ArchitectureRules {
|
|||
|
||||
private static ArchRule autoConfigurationClassesShouldBePublicAndFinal() {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that(areRegularAutoConfiguration())
|
||||
.that()
|
||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
.should()
|
||||
.bePublic()
|
||||
.andShould()
|
||||
|
@ -385,7 +365,8 @@ final class ArchitectureRules {
|
|||
private static ArchRule autoConfigurationClassesShouldHaveNoPublicMembers() {
|
||||
return ArchRuleDefinition.members()
|
||||
.that()
|
||||
.areDeclaredInClassesThat(areRegularAutoConfiguration())
|
||||
.areDeclaredInClassesThat()
|
||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
.and(areNotDefaultConstructors())
|
||||
.and(areNotConstants())
|
||||
.and(dontOverridePublicMethods())
|
||||
|
@ -394,24 +375,6 @@ final class ArchitectureRules {
|
|||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static ArchRule testAutoConfigurationClassesShouldBePackagePrivateAndFinal() {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.areAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||
.should()
|
||||
.bePackagePrivate()
|
||||
.andShould()
|
||||
.haveModifier(JavaModifier.FINAL)
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static DescribedPredicate<JavaClass> areRegularAutoConfiguration() {
|
||||
return DescribedPredicate.describe("Regular @AutoConfiguration",
|
||||
(javaClass) -> javaClass.isMetaAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
&& !javaClass.isMetaAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||
&& !javaClass.isAnnotation());
|
||||
}
|
||||
|
||||
private static DescribedPredicate<? super JavaMember> dontOverridePublicMethods() {
|
||||
OverridesPublicMethod<JavaMember> predicate = new OverridesPublicMethod<>();
|
||||
return DescribedPredicate.describe("don't override public methods", (member) -> !predicate.test(member));
|
||||
|
@ -517,11 +480,11 @@ final class ArchitectureRules {
|
|||
return string + " should be used instead";
|
||||
}
|
||||
|
||||
static ClassesTransformer<JavaPackage> packages(Predicate<JavaPackage> filter) {
|
||||
static ClassesTransformer<JavaPackage> packages() {
|
||||
return new AbstractClassesTransformer<>("packages") {
|
||||
@Override
|
||||
public Iterable<JavaPackage> doTransform(JavaClasses collection) {
|
||||
return collection.stream().map(JavaClass::getPackage).filter(filter).collect(Collectors.toSet());
|
||||
return collection.stream().map(JavaClass::getPackage).collect(Collectors.toSet());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.boot.build.autoconfigure;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -55,8 +54,8 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
|||
attributes.getOrDefault("afterName", Collections.emptyList()));
|
||||
}
|
||||
|
||||
public static AutoConfigurationClass of(InputStream input) {
|
||||
try {
|
||||
static AutoConfigurationClass of(File classFile) {
|
||||
try (FileInputStream input = new FileInputStream(classFile)) {
|
||||
ClassReader classReader = new ClassReader(input);
|
||||
AutoConfigurationClassVisitor visitor = new AutoConfigurationClassVisitor();
|
||||
classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||
|
@ -67,15 +66,6 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
|||
}
|
||||
}
|
||||
|
||||
static AutoConfigurationClass of(File classFile) {
|
||||
try (InputStream input = new FileInputStream(classFile)) {
|
||||
return of(input);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AutoConfigurationClassVisitor extends ClassVisitor {
|
||||
|
||||
private AutoConfigurationClass autoConfigurationClass;
|
||||
|
|
|
@ -38,10 +38,7 @@ import org.gradle.api.tasks.SkipWhenEmpty;
|
|||
*/
|
||||
public abstract class AutoConfigurationImportsTask extends DefaultTask {
|
||||
|
||||
/**
|
||||
* The path of the {@code AutoConfiguration.imports} file.
|
||||
*/
|
||||
public static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||
static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||
|
||||
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
|||
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
||||
|
||||
/**
|
||||
* Name of the {@link CheckSpringConfigurationMetadata} task.
|
||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
||||
*/
|
||||
public static final String CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkSpringConfigurationMetadata";
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
|
|||
@Override
|
||||
public void apply(Project project) {
|
||||
Configuration optional = project.getConfigurations().create("optional");
|
||||
optional.setCanBeConsumed(true);
|
||||
optional.setCanBeConsumed(false);
|
||||
optional.setCanBeResolved(false);
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
SourceSetContainer sourceSets = project.getExtensions()
|
||||
|
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* Copyright 2025-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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.VerificationException;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
import org.springframework.boot.build.autoconfigure.AutoConfigurationClass;
|
||||
|
||||
/**
|
||||
* Task to check the contents of a project's
|
||||
* {@code META-INF/spring/*.AutoConfigure*.imports} files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class CheckAutoConfigureImports extends DefaultTask {
|
||||
|
||||
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||
|
||||
private FileCollection classpath = getProject().getObjects().fileCollection();
|
||||
|
||||
public CheckAutoConfigureImports() {
|
||||
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
|
||||
setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@SkipWhenEmpty
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileTree getSource() {
|
||||
return this.sourceFiles.getAsFileTree()
|
||||
.matching((filter) -> filter.include("META-INF/spring/*.AutoConfigure*.imports"));
|
||||
}
|
||||
|
||||
public void setSource(Object source) {
|
||||
this.sourceFiles = getProject().getObjects().fileCollection().from(source);
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
public void setClasspath(Object classpath) {
|
||||
this.classpath = getProject().getObjects().fileCollection().from(classpath);
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getOutputDirectory();
|
||||
|
||||
@TaskAction
|
||||
void execute() {
|
||||
Map<String, List<String>> allProblems = new TreeMap<>();
|
||||
for (AutoConfigureImports autoConfigureImports : loadImports()) {
|
||||
List<String> problems = new ArrayList<>();
|
||||
if (!find(autoConfigureImports.annotationName)) {
|
||||
problems.add("Annotation '%s' was not found".formatted(autoConfigureImports.annotationName));
|
||||
}
|
||||
for (String imported : autoConfigureImports.imports) {
|
||||
String importedClassName = imported;
|
||||
if (importedClassName.startsWith("optional:")) {
|
||||
importedClassName = importedClassName.substring("optional:".length());
|
||||
}
|
||||
boolean found = find(importedClassName, (input) -> {
|
||||
if (!correctlyAnnotated(input)) {
|
||||
problems.add("Imported auto-configuration '%s' is not annotated with @AutoConfiguration"
|
||||
.formatted(imported));
|
||||
}
|
||||
});
|
||||
if (!found) {
|
||||
problems.add("Imported auto-configuration '%s' was not found".formatted(importedClassName));
|
||||
}
|
||||
|
||||
}
|
||||
List<String> sortedValues = new ArrayList<>(autoConfigureImports.imports);
|
||||
Collections.sort(sortedValues, (i1, i2) -> {
|
||||
boolean imported1 = i1.startsWith("optional:");
|
||||
boolean imported2 = i2.startsWith("optional:");
|
||||
int comparison = Boolean.compare(imported1, imported2);
|
||||
if (comparison != 0) {
|
||||
return comparison;
|
||||
}
|
||||
return i1.compareTo(i2);
|
||||
});
|
||||
if (!sortedValues.equals(autoConfigureImports.imports)) {
|
||||
File sortedOutputFile = getOutputDirectory().file("sorted-" + autoConfigureImports.fileName)
|
||||
.get()
|
||||
.getAsFile();
|
||||
writeString(sortedOutputFile, sortedValues.stream().collect(Collectors.joining(System.lineSeparator()))
|
||||
+ System.lineSeparator());
|
||||
problems.add(
|
||||
"Entries should be required then optional, each sorted alphabetically (expected content written to '%s')"
|
||||
.formatted(sortedOutputFile.getAbsolutePath()));
|
||||
}
|
||||
if (!problems.isEmpty()) {
|
||||
allProblems.computeIfAbsent(autoConfigureImports.fileName, (unused) -> new ArrayList<>())
|
||||
.addAll(problems);
|
||||
}
|
||||
}
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
writeReport(allProblems, outputFile);
|
||||
if (!allProblems.isEmpty()) {
|
||||
throw new VerificationException(
|
||||
"AutoConfigure….imports checks failed. See '%s' for details".formatted(outputFile));
|
||||
}
|
||||
}
|
||||
|
||||
private List<AutoConfigureImports> loadImports() {
|
||||
return getSource().getFiles().stream().map((file) -> {
|
||||
String fileName = file.getName();
|
||||
String annotationName = fileName.substring(0, fileName.length() - ".imports".length());
|
||||
return new AutoConfigureImports(annotationName, loadImports(file), fileName);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private List<String> loadImports(File importsFile) {
|
||||
try {
|
||||
return Files.readAllLines(importsFile.toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean find(String className) {
|
||||
return find(className, (input) -> {
|
||||
});
|
||||
}
|
||||
|
||||
private boolean find(String className, Consumer<InputStream> handler) {
|
||||
for (File root : this.classpath.getFiles()) {
|
||||
String classFilePath = className.replace(".", "/") + ".class";
|
||||
if (root.isDirectory()) {
|
||||
File classFile = new File(root, classFilePath);
|
||||
if (classFile.isFile()) {
|
||||
try (InputStream input = new FileInputStream(classFile)) {
|
||||
handler.accept(input);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
try (JarFile jar = new JarFile(root)) {
|
||||
ZipEntry entry = jar.getEntry(classFilePath);
|
||||
if (entry != null) {
|
||||
try (InputStream input = jar.getInputStream(entry)) {
|
||||
handler.accept(input);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean correctlyAnnotated(InputStream classFile) {
|
||||
return AutoConfigurationClass.of(classFile) != null;
|
||||
}
|
||||
|
||||
private void writeReport(Map<String, List<String>> allProblems, File outputFile) {
|
||||
outputFile.getParentFile().mkdirs();
|
||||
StringBuilder report = new StringBuilder();
|
||||
if (!allProblems.isEmpty()) {
|
||||
allProblems.forEach((fileName, problems) -> {
|
||||
report.append("Found problems in '%s':%n".formatted(fileName));
|
||||
problems.forEach((problem) -> report.append(" - %s%n".formatted(problem)));
|
||||
});
|
||||
}
|
||||
writeString(outputFile, report.toString());
|
||||
}
|
||||
|
||||
private void writeString(File file, String content) {
|
||||
try {
|
||||
Files.writeString(file.toPath(), content);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
record AutoConfigureImports(String annotationName, List<String> imports, String fileName) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,14 +17,17 @@
|
|||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
|
@ -35,9 +38,9 @@ import org.gradle.api.tasks.OutputFile;
|
|||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Task} used to document test slices.
|
||||
|
@ -46,16 +49,16 @@ import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestS
|
|||
*/
|
||||
public abstract class DocumentTestSlices extends DefaultTask {
|
||||
|
||||
private FileCollection testSliceMetadata;
|
||||
private FileCollection testSlices;
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileCollection getTestSlices() {
|
||||
return this.testSliceMetadata;
|
||||
return this.testSlices;
|
||||
}
|
||||
|
||||
public void setTestSlices(FileCollection testSlices) {
|
||||
this.testSliceMetadata = testSlices;
|
||||
this.testSlices = testSlices;
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
|
@ -63,42 +66,61 @@ public abstract class DocumentTestSlices extends DefaultTask {
|
|||
|
||||
@TaskAction
|
||||
void documentTestSlices() throws IOException {
|
||||
Map<String, List<TestSlice>> testSlices = readTestSlices();
|
||||
Set<TestSlice> testSlices = readTestSlices();
|
||||
writeTable(testSlices);
|
||||
}
|
||||
|
||||
private Map<String, List<TestSlice>> readTestSlices() {
|
||||
Map<String, List<TestSlice>> testSlices = new TreeMap<>();
|
||||
for (File metadataFile : this.testSliceMetadata) {
|
||||
JsonMapper mapper = JsonMapper.builder().build();
|
||||
TestSliceMetadata metadata = mapper.readValue(metadataFile, TestSliceMetadata.class);
|
||||
List<TestSlice> slices = new ArrayList<>(metadata.testSlices());
|
||||
Collections.sort(slices, (s1, s2) -> s1.annotation().compareTo(s2.annotation()));
|
||||
testSlices.put(metadata.module(), slices);
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<TestSlice> readTestSlices() throws IOException {
|
||||
Set<TestSlice> testSlices = new TreeSet<>();
|
||||
for (File metadataFile : this.testSlices) {
|
||||
Properties metadata = new Properties();
|
||||
try (Reader reader = new FileReader(metadataFile)) {
|
||||
metadata.load(reader);
|
||||
}
|
||||
for (String name : Collections.list((Enumeration<String>) metadata.propertyNames())) {
|
||||
testSlices.add(new TestSlice(name,
|
||||
new TreeSet<>(StringUtils.commaDelimitedListToSet(metadata.getProperty(name)))));
|
||||
}
|
||||
}
|
||||
return testSlices;
|
||||
}
|
||||
|
||||
private void writeTable(Map<String, List<TestSlice>> testSlicesByModule) throws IOException {
|
||||
private void writeTable(Set<TestSlice> testSlices) throws IOException {
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
||||
writer.println("[cols=\"d,d,a\"]");
|
||||
writer.println("[cols=\"d,a\"]");
|
||||
writer.println("|===");
|
||||
writer.println("|Module | Test slice | Imported auto-configuration");
|
||||
testSlicesByModule.forEach((module, testSlices) -> {
|
||||
testSlices.forEach((testSlice) -> {
|
||||
writer.println();
|
||||
writer.printf("| `%s`%n", module);
|
||||
writer.printf("| javadoc:%s[format=annotation]%n", testSlice.annotation());
|
||||
writer.println("| ");
|
||||
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations()) {
|
||||
writer.printf("`%s`%n", importedAutoConfiguration);
|
||||
}
|
||||
});
|
||||
});
|
||||
writer.println("| Test slice | Imported auto-configuration");
|
||||
for (TestSlice testSlice : testSlices) {
|
||||
writer.println();
|
||||
writer.printf("| `@%s`%n", testSlice.className);
|
||||
writer.println("| ");
|
||||
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations) {
|
||||
writer.printf("`%s`%n", importedAutoConfiguration);
|
||||
}
|
||||
}
|
||||
writer.println("|===");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestSlice implements Comparable<TestSlice> {
|
||||
|
||||
private final String className;
|
||||
|
||||
private final SortedSet<String> importedAutoConfigurations;
|
||||
|
||||
private TestSlice(String className, SortedSet<String> importedAutoConfigurations) {
|
||||
this.className = ClassUtils.getShortName(className);
|
||||
this.importedAutoConfigurations = importedAutoConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestSlice other) {
|
||||
return this.className.compareTo(other.className);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link Task} for generating metadata describing a project's test slices.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class GenerateTestSliceMetadata extends DefaultTask {
|
||||
|
||||
private final ObjectFactory objectFactory;
|
||||
|
||||
private FileCollection classpath;
|
||||
|
||||
private FileCollection importsFiles;
|
||||
|
||||
private FileCollection classesDirs;
|
||||
|
||||
@Inject
|
||||
public GenerateTestSliceMetadata(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
public void setSourceSet(SourceSet sourceSet) {
|
||||
this.classpath = sourceSet.getRuntimeClasspath();
|
||||
this.importsFiles = this.objectFactory.fileTree()
|
||||
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
||||
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
||||
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract RegularFileProperty getSpringFactories();
|
||||
|
||||
@Classpath
|
||||
FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
FileCollection getImportFiles() {
|
||||
return this.importsFiles;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
FileCollection getClassesDirs() {
|
||||
return this.classesDirs;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void generateTestSliceMetadata() throws IOException {
|
||||
TestSliceMetadata metadata = readTestSlices();
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
metadata.writeTo(outputFile);
|
||||
}
|
||||
|
||||
private TestSliceMetadata readTestSlices() throws IOException {
|
||||
List<TestSlice> testSlices = new ArrayList<>();
|
||||
try (URLClassLoader classLoader = new URLClassLoader(
|
||||
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().getOrNull());
|
||||
readImportsFiles(springFactories, this.importsFiles);
|
||||
for (File classesDir : this.classesDirs) {
|
||||
testSlices.addAll(readTestSlices(classesDir, metadataReaderFactory, springFactories));
|
||||
}
|
||||
}
|
||||
return new TestSliceMetadata(getProject().getName(), testSlices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given imports files and puts them in springFactories. The key is the file
|
||||
* name, the value is the file contents, split by line, delimited with a comma. This
|
||||
* is done to mimic the spring.factories structure.
|
||||
* @param springFactories spring.factories parsed as properties
|
||||
* @param importsFiles the imports files to read
|
||||
*/
|
||||
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
||||
for (File file : importsFiles.getFiles()) {
|
||||
try {
|
||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||
String fileNameWithoutExtension = file.getName()
|
||||
.substring(0, file.getName().length() - ".imports".length());
|
||||
springFactories.setProperty(fileNameWithoutExtension,
|
||||
StringUtils.collectionToCommaDelimitedString(lines));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> removeComments(List<String> lines) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
int commentIndex = line.indexOf('#');
|
||||
if (commentIndex > -1) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
result.add(line);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private URL toURL(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readSpringFactories(File file) throws IOException {
|
||||
Properties springFactories = new Properties();
|
||||
if (file.isFile()) {
|
||||
try (Reader in = new FileReader(file)) {
|
||||
springFactories.load(in);
|
||||
}
|
||||
}
|
||||
return springFactories;
|
||||
}
|
||||
|
||||
private List<TestSlice> readTestSlices(File classesDir, MetadataReaderFactory metadataReaderFactory,
|
||||
Properties springFactories) throws IOException {
|
||||
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
||||
return classes.filter((path) -> path.toString().endsWith("Test.class"))
|
||||
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
||||
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
||||
.map((metadataReader) -> readTestSlice(metadataReader, springFactories))
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
||||
try {
|
||||
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private TestSlice readTestSlice(MetadataReader metadataReader, Properties springFactories) {
|
||||
String annotationName = metadataReader.getClassMetadata().getClassName();
|
||||
List<String> importedAutoConfiguration = getImportedAutoConfiguration(springFactories,
|
||||
metadataReader.getAnnotationMetadata());
|
||||
return new TestSlice(annotationName, importedAutoConfiguration);
|
||||
}
|
||||
|
||||
private List<String> getImportedAutoConfiguration(Properties springFactories,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Stream<String> importers = findMetaImporters(annotationMetadata);
|
||||
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
||||
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
||||
}
|
||||
return importers
|
||||
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
||||
return annotationMetadata.getAnnotationTypes()
|
||||
.stream()
|
||||
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
||||
}
|
||||
|
||||
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
||||
return metadata.getMetaAnnotationTypes(annotationType)
|
||||
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define test auto-configuration. When the
|
||||
* {@link JavaPlugin} is applied it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Add checks to ensure AutoConfigure*.import files and related annotations are
|
||||
* correct</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestAutoConfigurationPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project target) {
|
||||
target.getPlugins().withType(JavaPlugin.class, (plugin) -> {
|
||||
target.getTasks().register("checkAutoConfigureImports", CheckAutoConfigureImports.class, (task) -> {
|
||||
SourceSet mainSourceSet = target.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSource(mainSourceSet.getResources());
|
||||
ConfigurableFileCollection classpath = target.files(mainSourceSet.getRuntimeClasspath(),
|
||||
target.getConfigurations().getByName(mainSourceSet.getRuntimeClasspathConfigurationName()));
|
||||
task.setClasspath(classpath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -17,30 +17,229 @@
|
|||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import tools.jackson.databind.SerializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Metadata describing a module's test slices.
|
||||
* A {@link Task} for generating metadata describing a project's test slices.
|
||||
*
|
||||
* @param module the module's name
|
||||
* @param testSlices the module's test slices
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
record TestSliceMetadata(String module, List<TestSlice> testSlices) {
|
||||
public abstract class TestSliceMetadata extends DefaultTask {
|
||||
|
||||
static TestSliceMetadata readFrom(File file) {
|
||||
return JsonMapper.builder().build().readValue(file, TestSliceMetadata.class);
|
||||
private final ObjectFactory objectFactory;
|
||||
|
||||
private FileCollection classpath;
|
||||
|
||||
private FileCollection importsFiles;
|
||||
|
||||
private FileCollection classesDirs;
|
||||
|
||||
@Inject
|
||||
public TestSliceMetadata(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
Configuration testSliceMetadata = getProject().getConfigurations().maybeCreate("testSliceMetadata");
|
||||
getProject().afterEvaluate((evaluated) -> evaluated.getArtifacts()
|
||||
.add(testSliceMetadata.getName(), getOutputFile(), (artifact) -> artifact.builtBy(this)));
|
||||
}
|
||||
|
||||
void writeTo(File file) {
|
||||
JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build().writeValue(file, this);
|
||||
public void setSourceSet(SourceSet sourceSet) {
|
||||
this.classpath = sourceSet.getRuntimeClasspath();
|
||||
this.importsFiles = this.objectFactory.fileTree()
|
||||
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
||||
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
||||
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
||||
}
|
||||
|
||||
record TestSlice(String annotation, List<String> importedAutoConfigurations) {
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract RegularFileProperty getSpringFactories();
|
||||
|
||||
@Classpath
|
||||
FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
FileCollection getImportFiles() {
|
||||
return this.importsFiles;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
FileCollection getClassesDirs() {
|
||||
return this.classesDirs;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void documentTestSlices() throws IOException {
|
||||
Properties testSlices = readTestSlices();
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
try (FileWriter writer = new FileWriter(outputFile)) {
|
||||
testSlices.store(writer, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readTestSlices() throws IOException {
|
||||
Properties testSlices = CollectionFactory.createSortedProperties(true);
|
||||
try (URLClassLoader classLoader = new URLClassLoader(
|
||||
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().get());
|
||||
readImportsFiles(springFactories, this.importsFiles);
|
||||
for (File classesDir : this.classesDirs) {
|
||||
addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
|
||||
}
|
||||
}
|
||||
return testSlices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given imports files and puts them in springFactories. The key is the file
|
||||
* name, the value is the file contents, split by line, delimited with a comma. This
|
||||
* is done to mimic the spring.factories structure.
|
||||
* @param springFactories spring.factories parsed as properties
|
||||
* @param importsFiles the imports files to read
|
||||
*/
|
||||
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
||||
for (File file : importsFiles.getFiles()) {
|
||||
try {
|
||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||
String fileNameWithoutExtension = file.getName()
|
||||
.substring(0, file.getName().length() - ".imports".length());
|
||||
springFactories.setProperty(fileNameWithoutExtension,
|
||||
StringUtils.collectionToCommaDelimitedString(lines));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> removeComments(List<String> lines) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
int commentIndex = line.indexOf('#');
|
||||
if (commentIndex > -1) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
result.add(line);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private URL toURL(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readSpringFactories(File file) throws IOException {
|
||||
Properties springFactories = new Properties();
|
||||
try (Reader in = new FileReader(file)) {
|
||||
springFactories.load(in);
|
||||
}
|
||||
return springFactories;
|
||||
}
|
||||
|
||||
private void addTestSlices(Properties testSlices, File classesDir, MetadataReaderFactory metadataReaderFactory,
|
||||
Properties springFactories) throws IOException {
|
||||
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
||||
classes.filter((path) -> path.toString().endsWith("Test.class"))
|
||||
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
||||
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
||||
.forEach((metadataReader) -> addTestSlice(testSlices, springFactories, metadataReader));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
||||
try {
|
||||
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTestSlice(Properties testSlices, Properties springFactories, MetadataReader metadataReader) {
|
||||
testSlices.setProperty(metadataReader.getClassMetadata().getClassName(),
|
||||
StringUtils.collectionToCommaDelimitedString(
|
||||
getImportedAutoConfiguration(springFactories, metadataReader.getAnnotationMetadata())));
|
||||
}
|
||||
|
||||
private SortedSet<String> getImportedAutoConfiguration(Properties springFactories,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Stream<String> importers = findMetaImporters(annotationMetadata);
|
||||
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
||||
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
||||
}
|
||||
return importers
|
||||
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
||||
return annotationMetadata.getAnnotationTypes()
|
||||
.stream()
|
||||
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
||||
}
|
||||
|
||||
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
||||
return metadata.getMetaAnnotationTypes(annotationType)
|
||||
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.attributes.Category;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.plugins.PluginContainer;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define one or more test slices. When applied, it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Applies the {@link TestAutoConfigurationPlugin}
|
||||
* </ul>
|
||||
* Additionally, when the {@link JavaPlugin} is applied it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Defines a task that produces metadata describing the test slices. The metadata is
|
||||
* made available as an artifact in the {@code testSliceMetadata} configuration
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestSlicePlugin implements Plugin<Project> {
|
||||
|
||||
private static final String TEST_SLICE_METADATA_CONFIGURATION_NAME = "testSliceMetadata";
|
||||
|
||||
@Override
|
||||
public void apply(Project target) {
|
||||
PluginContainer plugins = target.getPlugins();
|
||||
plugins.apply(TestAutoConfigurationPlugin.class);
|
||||
plugins.withType(JavaPlugin.class, (plugin) -> {
|
||||
TaskProvider<GenerateTestSliceMetadata> generateTestSliceMetadata = target.getTasks()
|
||||
.register("generateTestSliceMetadata", GenerateTestSliceMetadata.class, (task) -> {
|
||||
SourceSet mainSourceSet = target.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSourceSet(mainSourceSet);
|
||||
task.getOutputFile().set(target.getLayout().getBuildDirectory().file("test-slice-metadata.json"));
|
||||
});
|
||||
addMetadataArtifact(target, generateTestSliceMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
private void addMetadataArtifact(Project project, TaskProvider<GenerateTestSliceMetadata> task) {
|
||||
project.getConfigurations().consumable(TEST_SLICE_METADATA_CONFIGURATION_NAME, (configuration) -> {
|
||||
configuration.attributes((attributes) -> {
|
||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE,
|
||||
project.getObjects().named(Category.class, Category.DOCUMENTATION));
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE,
|
||||
project.getObjects().named(Usage.class, "test-slice-metadata"));
|
||||
});
|
||||
});
|
||||
project.getArtifacts().add(TEST_SLICE_METADATA_CONFIGURATION_NAME, task);
|
||||
}
|
||||
|
||||
}
|
|
@ -116,18 +116,6 @@ apiref-openjdk=https://docs.oracle.com/en/java/javase/17/docs/api
|
|||
# === Code Links ===
|
||||
|
||||
code-spring-boot=https://github.com/{github-repo}/tree/{github-ref}
|
||||
code-spring-boot-autoconfigure-src={code-spring-boot}/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
||||
code-spring-boot-batch-jdbc-src={code-spring-boot}/module/spring-boot-batch-jdbc/src/main/java/org/springframework/boot/batch/jdbc
|
||||
code-spring-boot-batch-src={code-spring-boot}/module/spring-boot-batch/src/main/java/org/springframework/boot/batch
|
||||
code-spring-boot-freemarker-src={code-spring-boot}/module/spring-boot-freemarker/src/main/java/org/springframework/boot/freemarker
|
||||
code-spring-boot-groovy-templates-src={code-spring-boot}/module/spring-boot-groovy-templates/src/main/java/org/springframework/boot/groovy/template
|
||||
code-spring-boot-hibernate-src={code-spring-boot}/module/spring-boot-hibernate/src/main/java/org/springframework/boot/hibernate
|
||||
code-spring-boot-integration-src={code-spring-boot}/module/spring-boot-integration/src/main/java/org/springframework/boot/integration
|
||||
code-spring-boot-jdbc-src={code-spring-boot}/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc
|
||||
code-spring-boot-jooq-src={code-spring-boot}/module/spring-boot-jooq/src/main/java/org/springframework/boot/jooq
|
||||
code-spring-boot-jpa-src={code-spring-boot}/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa
|
||||
code-spring-boot-autoconfigure-src={code-spring-boot}/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
||||
code-spring-boot-latest=https://github.com/{github-repo}/tree/main
|
||||
code-spring-boot-servlet-src={code-spring-boot}/module/spring-boot-servlet/src/main/java/org/springframework/boot/servlet
|
||||
code-spring-boot-thymeleaf-src={code-spring-boot}/module/spring-boot-thymeleaf/src/main/java/org/springframework/boot/thymeleaf
|
||||
code-spring-boot-webmvc-src={code-spring-boot}/module/spring-boot-webmvc/src/main/java/org/springframework/boot/webmvc
|
||||
|
||||
|
|
|
@ -77,9 +77,7 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("description 'Test project for manifest customization'");
|
||||
out.println("jar.archiveFileName = 'test.jar'");
|
||||
}
|
||||
|
@ -109,9 +107,7 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("description 'Test'");
|
||||
}
|
||||
runGradle("assemble");
|
||||
|
@ -140,9 +136,7 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("description 'Test'");
|
||||
}
|
||||
runGradle("assemble");
|
||||
|
|
|
@ -57,8 +57,6 @@ class ArchitectureCheckTests {
|
|||
|
||||
private static final String SPRING_CONTEXT = "org.springframework:spring-context:6.2.9";
|
||||
|
||||
private static final String JUNIT_JUPITER = "org.junit.jupiter:junit-jupiter:5.12.0";
|
||||
|
||||
private static final String SPRING_INTEGRATION_JMX = "org.springframework.integration:spring-integration-jmx:6.5.1";
|
||||
|
||||
private GradleBuild gradleBuild;
|
||||
|
@ -285,29 +283,6 @@ class ArchitectureCheckTests {
|
|||
build(this.gradleBuild.withNullMarked(true), Task.CHECK_ARCHITECTURE_TEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEnumSourceValueIsInferredShouldSucceedAndWriteEmptyReport() throws IOException {
|
||||
prepareTask(Task.CHECK_ARCHITECTURE_TEST, "junit/enumsource/inferredfromparametertype");
|
||||
build(this.gradleBuild.withDependencies(JUNIT_JUPITER), Task.CHECK_ARCHITECTURE_TEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEnumSourceValueIsNotTheSameAsTypeOfMethodsFirstParameterShouldSucceedAndWriteEmptyReport()
|
||||
throws IOException {
|
||||
prepareTask(Task.CHECK_ARCHITECTURE_TEST, "junit/enumsource/valuenecessary");
|
||||
build(this.gradleBuild.withDependencies(JUNIT_JUPITER), Task.CHECK_ARCHITECTURE_TEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEnumSourceValueIsSameAsTypeOfMethodsFirstParameterShouldFailAndWriteReport() throws IOException {
|
||||
prepareTask(Task.CHECK_ARCHITECTURE_TEST, "junit/enumsource/sameasparametertype");
|
||||
buildAndFail(this.gradleBuild.withDependencies(JUNIT_JUPITER), Task.CHECK_ARCHITECTURE_TEST,
|
||||
"method <org.springframework.boot.build.architecture.junit.enumsource.sameasparametertype"
|
||||
+ ".EnumSourceSameAsParameterType.exampleMethod(org.springframework.boot.build."
|
||||
+ "architecture.junit.enumsource.sameasparametertype.EnumSourceSameAsParameterType$Example)>",
|
||||
"should not have a value that is the same as the type of the method's first parameter");
|
||||
}
|
||||
|
||||
private void prepareTask(Task task, String... sourceDirectories) throws IOException {
|
||||
for (String sourceDirectory : sourceDirectories) {
|
||||
FileSystemUtils.copyRecursively(
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.architecture.junit.enumsource.inferredfromparametertype;
|
||||
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
class EnumSourceInferredFromParameterType {
|
||||
|
||||
@EnumSource
|
||||
void exampleMethod(Example example) {
|
||||
|
||||
}
|
||||
|
||||
enum Example {
|
||||
|
||||
ONE, TWO, THREE
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.architecture.junit.enumsource.sameasparametertype;
|
||||
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
class EnumSourceSameAsParameterType {
|
||||
|
||||
@EnumSource(Example.class)
|
||||
void exampleMethod(Example example) {
|
||||
|
||||
}
|
||||
|
||||
enum Example {
|
||||
|
||||
ONE, TWO, THREE
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.architecture.junit.enumsource.valuenecessary;
|
||||
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
class EnumSourceValueNecessary {
|
||||
|
||||
@EnumSource(Example.class)
|
||||
void exampleMethod(String thing, Example example) {
|
||||
|
||||
}
|
||||
|
||||
enum Example {
|
||||
|
||||
ONE, TWO, THREE
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -31,7 +31,6 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
import org.junit.jupiter.params.support.ParameterDeclarations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -245,8 +244,7 @@ class DependencyVersionUpgradeTests {
|
|||
static class InputProvider implements ArgumentsProvider {
|
||||
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ParameterDeclarations parameterDeclarations,
|
||||
ExtensionContext context) {
|
||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||
Method testMethod = context.getRequiredTestMethod();
|
||||
Stream<Arguments> artifactVersions = artifactVersions(testMethod)
|
||||
.map((artifactVersion) -> Arguments.of(VersionType.ARTIFACT_VERSION.parse(artifactVersion.current()),
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestSliceMetadata}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestSliceMetadataTests {
|
||||
|
||||
@TempDir
|
||||
private File temp;
|
||||
|
||||
@Test
|
||||
void roundtripJson() {
|
||||
TestSliceMetadata source = new TestSliceMetadata("example",
|
||||
List.of(new TestSlice("ExampleOneTest", List.of("com.example.OneAutoConfiguration")),
|
||||
new TestSlice("ExampleTwoTest", List.of("com.example.TwoAutoConfiguration"))));
|
||||
File metadataFile = new File(this.temp, "metadata.json");
|
||||
source.writeTo(metadataFile);
|
||||
TestSliceMetadata readBack = TestSliceMetadata.readFrom(metadataFile);
|
||||
assertThat(source).isEqualTo(readBack);
|
||||
}
|
||||
|
||||
}
|
|
@ -166,7 +166,6 @@ class TestFailuresPluginIntegrationTests {
|
|||
writer.println("dependencies {");
|
||||
writer.println(" testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'");
|
||||
writer.println(" testImplementation 'org.assertj:assertj-core:3.11.1'");
|
||||
writer.println(" testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.6.0'");
|
||||
writer.println("}");
|
||||
writer.println();
|
||||
writer.println("test {");
|
||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.stream.IntStream;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ import java.util.function.Function;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.jspecify.annotations.Nullable;
|
|||
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPushListener;
|
||||
|
@ -35,6 +34,7 @@ import org.springframework.boot.buildpack.platform.docker.transport.DockerEngine
|
|||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -28,10 +28,10 @@ import com.sun.jna.Platform;
|
|||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.Cache.Bind;
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
|
|
|
@ -34,12 +34,14 @@ import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.E
|
|||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
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.IOBiConsumer;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 4.0.0
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class ApiVersion {
|
||||
|
|
@ -14,20 +14,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A platform specification for a Docker image.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 4.0.0
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class ImagePlatform {
|
||||
|
|
@ -20,7 +20,7 @@ import java.util.stream.IntStream;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
|
|
@ -37,9 +37,9 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi
|
|||
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.DockerLog;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
|
@ -40,6 +39,7 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerReferenc
|
|||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
|||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
||||
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.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
|
@ -49,6 +48,7 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
|||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
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.IOConsumer;
|
||||
|
|
|
@ -25,10 +25,10 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
|
|
@ -45,12 +45,14 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.SystemApi;
|
|||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
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.Content;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -14,13 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -65,7 +64,7 @@ class ImagePlatformTests extends AbstractJsonTests {
|
|||
}
|
||||
|
||||
private Image getImage() throws IOException {
|
||||
return Image.of(getContent("type/image.json"));
|
||||
return Image.of(getContent("image.json"));
|
||||
}
|
||||
|
||||
}
|
|
@ -79,7 +79,4 @@
|
|||
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="InvocationContext\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<!-- https://github.com/apache/logging-log4j2/issues/2769#issuecomment-3049020222 -->
|
||||
<suppress files="SpringProfileArbiter\.java" checks="SpringMethodVisibility"/>
|
||||
<suppress files="StructuredLogLayout\.java" checks="SpringMethodVisibility"/>
|
||||
</suppressions>
|
||||
|
|
|
@ -6,10 +6,7 @@
|
|||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||
</module>
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks">
|
||||
<property name="avoidStaticImportExcludes"
|
||||
value="org.springframework.boot.autoconfigure.AutoConfigurationImportedCondition.importedAutoConfiguration" />
|
||||
</module>
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks" />
|
||||
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
|
||||
<property name="headerFile" value="${config_loc}/checkstyle-header.txt"/>
|
||||
</module>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<subpackage name="ansi">
|
||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||
</subpackage>
|
||||
<subpackage name="availability">
|
||||
<subpackage name="avilability">
|
||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||
</subpackage>
|
||||
<subpackage name="bootstrap">
|
||||
|
@ -91,6 +91,8 @@
|
|||
<disallow pkg="jakarta.servlet" />
|
||||
|
||||
<!-- Common -->
|
||||
<subpackage name="client">
|
||||
</subpackage>
|
||||
<subpackage name="context">
|
||||
<allow pkg="org.springframework.context" />
|
||||
<subpackage name="servlet">
|
||||
|
@ -109,10 +111,6 @@
|
|||
<allow pkg="org.springframework.boot.web.servlet" />
|
||||
</subpackage>
|
||||
</subpackage>
|
||||
<subpackage name="client">
|
||||
<allow pkg="jakarta.servlet" />
|
||||
<allow pkg="org.springframework.context" />
|
||||
</subpackage>
|
||||
<subpackage name="context">
|
||||
<allow pkg="org.springframework.context" />
|
||||
</subpackage>
|
||||
|
|
|
@ -19,7 +19,7 @@ style:
|
|||
active: true
|
||||
NewLineAtEndOfFile:
|
||||
active: true
|
||||
UnusedImport:
|
||||
UnusedImports:
|
||||
active: true
|
||||
WildcardImport:
|
||||
active: true
|
||||
|
|
|
@ -136,25 +136,20 @@ def dependenciesOf(String version) {
|
|||
modules += [
|
||||
"spring-boot-jersey"
|
||||
]
|
||||
}
|
||||
if (version.equals("4.0.0-M1")) {
|
||||
modules += [
|
||||
"spring-boot-metrics",
|
||||
"spring-boot-observation",
|
||||
"spring-boot-tracing"
|
||||
]
|
||||
}
|
||||
else {
|
||||
modules += [
|
||||
"spring-boot-micrometer-metrics",
|
||||
"spring-boot-micrometer-observation",
|
||||
"spring-boot-micrometer-tracing"
|
||||
]
|
||||
}
|
||||
if (version.equals("4.0.0-RC1")) {
|
||||
modules += [
|
||||
"spring-boot-batch-jdbc"
|
||||
]
|
||||
if (version.equals("4.0.0-M1")) {
|
||||
modules += [
|
||||
"spring-boot-metrics",
|
||||
"spring-boot-observation",
|
||||
"spring-boot-tracing"
|
||||
]
|
||||
}
|
||||
else {
|
||||
modules += [
|
||||
"spring-boot-micrometer-metrics",
|
||||
"spring-boot-micrometer-observation",
|
||||
"spring-boot-micrometer-tracing"
|
||||
]
|
||||
}
|
||||
}
|
||||
return modules
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue