parent
abe95fa8a7
commit
ce99db1902
@ -1,28 +0,0 @@
|
|||||||
bomr:
|
|
||||||
bom: spring-boot-project/spring-boot-dependencies/pom.xml
|
|
||||||
upgrade:
|
|
||||||
github:
|
|
||||||
organization: spring-projects
|
|
||||||
repository: spring-boot
|
|
||||||
issue-labels:
|
|
||||||
- 'type: dependency-upgrade'
|
|
||||||
policy: same-major-version
|
|
||||||
prohibited:
|
|
||||||
- project: derby
|
|
||||||
versions:
|
|
||||||
# 10.15 requires Java 9
|
|
||||||
- '[10.15,)'
|
|
||||||
verify:
|
|
||||||
ignored-dependencies:
|
|
||||||
# Avoid conflicting transitive requirements for
|
|
||||||
# io.grpc:grpc-core:jar:[1.0.1,1.0.1] (Jetty),
|
|
||||||
# io.grpc:grpc-core:jar:[1.14.0,1.14.0] (Micrometer's Azure Registry), and
|
|
||||||
# io.grpc:grpc-core:jar:[1.15.0,1.15.0] (Micrometer's Stackdriver Registry)
|
|
||||||
- 'io.micrometer:micrometer-registry-azure-monitor'
|
|
||||||
- 'org.eclipse.jetty.gcloud:jetty-gcloud-session-manager'
|
|
||||||
- 'org.eclipse.jetty:jetty-home'
|
|
||||||
repositories:
|
|
||||||
# Caffeine Simulator's dependencies
|
|
||||||
- 'https://maven.imagej.net/content/repositories/public/'
|
|
||||||
# Spring Data GemFire's GemFire dependencies
|
|
||||||
- 'https://repo.spring.io/gemstone-release-pivotal-cache'
|
|
@ -1 +0,0 @@
|
|||||||
-Xmx1536m
|
|
Binary file not shown.
@ -1,2 +0,0 @@
|
|||||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
|
|
||||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar
|
|
@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' apply false // https://youtrack.jetbrains.com/issue/KT-30276
|
||||||
|
}
|
||||||
|
|
||||||
|
description = 'Spring Boot Build'
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
group 'org.springframework.boot'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven { url 'https://repo.spring.io/milestone' }
|
||||||
|
maven { url 'https://repo.spring.io/snapshot' }
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.cacheChangingModulesFor 60, 'minutes'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
= BOM Plugin
|
||||||
|
|
||||||
|
Allows one to publish a BOM from Gradle.
|
||||||
|
Properties, dependencies, and other BOMs are applied to the `bom {}` extension.
|
||||||
|
|
||||||
|
This plugin applies the `java-platform` and `maven-publish` plugins to the Project it's applied to.
|
||||||
|
|
||||||
|
== Usage
|
||||||
|
|
||||||
|
[source,groovy,indent=0]
|
||||||
|
----
|
||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot.bom'
|
||||||
|
}
|
||||||
|
|
||||||
|
bom {
|
||||||
|
property 'logback.version', '1.2.3'
|
||||||
|
property 'junit-jupiter.version', '5.3.2'
|
||||||
|
|
||||||
|
dependency 'ch.qos.logback', 'logback-classic', '${logback.version}'
|
||||||
|
dependency 'ch.qos.logback', 'logback-core', '${logback.version}'
|
||||||
|
|
||||||
|
bomImport 'org.junit', 'junit-bom', '${junit-jupiter.version}'
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
@ -0,0 +1,84 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
id 'io.spring.javaformat' version '0.0.18-SNAPSHOT'
|
||||||
|
id 'checkstyle'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven { url 'https://repo.spring.io/release' }
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.0'
|
||||||
|
implementation 'commons-codec:commons-codec:1.13'
|
||||||
|
implementation 'org.apache.maven:maven-embedder:3.6.2'
|
||||||
|
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:2.4.0'
|
||||||
|
implementation 'org.springframework:spring-core:5.2.2.RELEASE'
|
||||||
|
implementation 'org.springframework:spring-web:5.2.2.RELEASE'
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.5'
|
||||||
|
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
|
||||||
|
testImplementation 'org.assertj:assertj-core:3.11.1'
|
||||||
|
testImplementation 'org.apache.logging.log4j:log4j-core:2.12.1'
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
def archive = configurations.checkstyle.filter { it.name.startsWith('spring-javaformat-checkstyle')}
|
||||||
|
config = resources.text.fromArchiveEntry(archive, 'io/spring/javaformat/checkstyle/checkstyle.xml')
|
||||||
|
toolVersion = 8.11
|
||||||
|
}
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
plugins {
|
||||||
|
autoConfigurationPlugin {
|
||||||
|
id = "org.springframework.boot.auto-configuration"
|
||||||
|
implementationClass = "org.springframework.boot.build.autoconfigure.AutoConfigurationPlugin"
|
||||||
|
}
|
||||||
|
bomPlugin {
|
||||||
|
id = "org.springframework.boot.bom"
|
||||||
|
implementationClass = "org.springframework.boot.build.bom.BomPlugin"
|
||||||
|
}
|
||||||
|
configurationPropertiesPlugin {
|
||||||
|
id = "org.springframework.boot.configuration-properties"
|
||||||
|
implementationClass = "org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin"
|
||||||
|
}
|
||||||
|
conventionsPlugin {
|
||||||
|
id = "org.springframework.boot.conventions"
|
||||||
|
implementationClass = "org.springframework.boot.build.ConventionsPlugin"
|
||||||
|
}
|
||||||
|
deployedPlugin {
|
||||||
|
id = "org.springframework.boot.deployed"
|
||||||
|
implementationClass = "org.springframework.boot.build.DeployedPlugin"
|
||||||
|
}
|
||||||
|
integrationTestPlugin {
|
||||||
|
id = "org.springframework.boot.integration-test"
|
||||||
|
implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
|
||||||
|
}
|
||||||
|
mavenPluginPlugin {
|
||||||
|
id = "org.springframework.boot.maven-plugin"
|
||||||
|
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
||||||
|
}
|
||||||
|
mavenRepositoryPlugin {
|
||||||
|
id = "org.springframework.boot.maven-repository"
|
||||||
|
implementationClass = "org.springframework.boot.build.MavenRepositoryPlugin"
|
||||||
|
}
|
||||||
|
optionalDependenciesPlugin {
|
||||||
|
id = "org.springframework.boot.optional-dependencies"
|
||||||
|
implementationClass = "org.springframework.boot.build.optional.OptionalDependenciesPlugin"
|
||||||
|
}
|
||||||
|
starterPlugin {
|
||||||
|
id = "org.springframework.boot.starter"
|
||||||
|
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven { url 'https://repo.spring.io/snapshot' }
|
||||||
|
}
|
||||||
|
resolutionStrategy {
|
||||||
|
eachPlugin {
|
||||||
|
if (requested.id.id == 'io.spring.javaformat') {
|
||||||
|
useModule "io.spring.javaformat:spring-javaformat-gradle-plugin:${requested.version}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: new File(settingsDir, '../gradle/build-cache-settings.gradle')
|
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask;
|
||||||
|
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension;
|
||||||
|
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||||
|
import org.asciidoctor.gradle.jvm.AsciidoctorTask;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.Sync;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When
|
||||||
|
* the plugin is applied:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>All warnings are made fatal.
|
||||||
|
* <li>A task is created to resolve and unzip our documentation resources (CSS and
|
||||||
|
* Javascript).
|
||||||
|
* <li>For each {@link AsciidoctorTask} (HTML only):
|
||||||
|
* <ul>
|
||||||
|
* <li>A task is created to sync the documentation resources to its output directory.
|
||||||
|
* <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured.
|
||||||
|
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax
|
||||||
|
* highlighting, CSS styling, docinfo, etc.
|
||||||
|
* </ul>
|
||||||
|
* <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF):
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable
|
||||||
|
* warnings for references to missing attributes, the GitHub tag, the Artifactory repo for
|
||||||
|
* the current version, etc.
|
||||||
|
* <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()}
|
||||||
|
* is enabled.
|
||||||
|
* </ul>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class AsciidoctorConventions {
|
||||||
|
|
||||||
|
void apply(Project project) {
|
||||||
|
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
|
||||||
|
configureDocResourcesRepository(project);
|
||||||
|
makeAllWarningsFatal(project);
|
||||||
|
UnzipDocumentationResources unzipResources = createUnzipDocumentationResourcesTask(project);
|
||||||
|
project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> {
|
||||||
|
configureCommonAttributes(project, asciidoctorTask);
|
||||||
|
configureOptions(asciidoctorTask);
|
||||||
|
asciidoctorTask.baseDirFollowsSourceDir();
|
||||||
|
Sync syncSource = createSyncDocumentationSourceTask(project, asciidoctorTask);
|
||||||
|
if (asciidoctorTask instanceof AsciidoctorTask) {
|
||||||
|
configureHtmlOnlyAttributes(project, asciidoctorTask);
|
||||||
|
syncSource.from(unzipResources, (resources) -> resources.into("asciidoc"));
|
||||||
|
asciidoctorTask.doFirst(new Action<Task>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Task task) {
|
||||||
|
project.copy((spec) -> {
|
||||||
|
spec.from(asciidoctorTask.getSourceDir());
|
||||||
|
spec.into(asciidoctorTask.getOutputDir());
|
||||||
|
spec.include("css/**", "js/**");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureDocResourcesRepository(Project project) {
|
||||||
|
project.getRepositories().maven((mavenRepo) -> {
|
||||||
|
mavenRepo.setUrl(URI.create("https://repo.spring.io/release"));
|
||||||
|
mavenRepo.mavenContent((mavenContent) -> mavenContent.includeGroup("io.spring.docresources"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeAllWarningsFatal(Project project) {
|
||||||
|
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnzipDocumentationResources createUnzipDocumentationResourcesTask(Project project) {
|
||||||
|
Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources");
|
||||||
|
documentationResources.getDependencies()
|
||||||
|
.add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.1.3.RELEASE"));
|
||||||
|
UnzipDocumentationResources unzipResources = project.getTasks().create("unzipDocumentationResources",
|
||||||
|
UnzipDocumentationResources.class);
|
||||||
|
unzipResources.setResources(documentationResources);
|
||||||
|
unzipResources.setOutputDir(new File(project.getBuildDir(), "docs/resources"));
|
||||||
|
return unzipResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sync createSyncDocumentationSourceTask(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||||
|
Sync syncDocumentationSource = project.getTasks()
|
||||||
|
.create("syncDocumentationSourceFor" + StringUtils.capitalize(asciidoctorTask.getName()), Sync.class);
|
||||||
|
File syncedSource = new File(project.getBuildDir(), "docs/src/" + asciidoctorTask.getName());
|
||||||
|
syncDocumentationSource.setDestinationDir(syncedSource);
|
||||||
|
syncDocumentationSource.from("src/docs/");
|
||||||
|
asciidoctorTask.dependsOn(syncDocumentationSource);
|
||||||
|
asciidoctorTask.setSourceDir(project.relativePath(new File(syncedSource, "asciidoc/")));
|
||||||
|
return syncDocumentationSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {
|
||||||
|
asciidoctorTask.options(Collections.singletonMap("doctype", "book"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||||
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
|
attributes.put("highlightjsdir", "js/highlight");
|
||||||
|
attributes.put("highlightjs-theme", "github");
|
||||||
|
attributes.put("linkcss", true);
|
||||||
|
attributes.put("icons", "font");
|
||||||
|
attributes.put("stylesheet", "css/spring.css");
|
||||||
|
asciidoctorTask.attributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||||
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
|
attributes.put("attribute-missing", "warn");
|
||||||
|
attributes.put("github-tag", determineGitHubTag(project));
|
||||||
|
attributes.put("spring-boot-artifactory-repo", determineArtifactoryRepo(project));
|
||||||
|
attributes.put("version", "{gradle-project-version}");
|
||||||
|
asciidoctorTask.attributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineArtifactoryRepo(Project project) {
|
||||||
|
String version = project.getVersion().toString();
|
||||||
|
String type = version.substring(version.lastIndexOf('.'));
|
||||||
|
if (type.equals("RELEASE")) {
|
||||||
|
return "release";
|
||||||
|
}
|
||||||
|
if (type.startsWith("M") || type.startsWith("RC")) {
|
||||||
|
return "milestone";
|
||||||
|
}
|
||||||
|
return "snapshot";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineGitHubTag(Project project) {
|
||||||
|
String version = "v" + project.getVersion();
|
||||||
|
return (version.endsWith("-SNAPSHOT")) ? "master" : version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} for unzipping the documentation resources.
|
||||||
|
*/
|
||||||
|
public static class UnzipDocumentationResources extends DefaultTask {
|
||||||
|
|
||||||
|
private FileCollection resources;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getResources() {
|
||||||
|
return this.resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResources(FileCollection resources) {
|
||||||
|
this.resources = resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void syncDocumentationResources() {
|
||||||
|
getProject().sync((copySpec) -> {
|
||||||
|
copySpec.into(this.outputDir);
|
||||||
|
for (File resource : this.resources) {
|
||||||
|
copySpec.from(getProject().zipTree(resource));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
|
||||||
|
import org.apache.maven.artifact.repository.MavenArtifactRepository;
|
||||||
|
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.DependencySet;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.plugins.quality.CheckstyleExtension;
|
||||||
|
import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||||
|
import org.gradle.api.publish.PublishingExtension;
|
||||||
|
import org.gradle.api.publish.maven.MavenPom;
|
||||||
|
import org.gradle.api.publish.maven.MavenPomDeveloperSpec;
|
||||||
|
import org.gradle.api.publish.maven.MavenPomIssueManagement;
|
||||||
|
import org.gradle.api.publish.maven.MavenPomLicenseSpec;
|
||||||
|
import org.gradle.api.publish.maven.MavenPomOrganization;
|
||||||
|
import org.gradle.api.publish.maven.MavenPomScm;
|
||||||
|
import org.gradle.api.publish.maven.MavenPublication;
|
||||||
|
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||||
|
import org.gradle.api.tasks.bundling.Jar;
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile;
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||||
|
import org.gradle.api.tasks.testing.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin to apply conventions to projects that are part of Spring Boot's build.
|
||||||
|
* Conventions are applied in response to various plugins being applied.
|
||||||
|
*
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* When the {@link JavaPlugin Java plugin} is applied:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code sourceCompatibility} is set to {@code 1.8}
|
||||||
|
* <li>Spring Java Format and Checkstyle plugins are applied
|
||||||
|
* <li>{@link Test} tasks are configured to use JUnit Platform and use a max heap of 1024M
|
||||||
|
* <li>{@link JavaCompile} tasks are configured to use UTF-8 encoding
|
||||||
|
* <li>{@link Javadoc} tasks are configured to use UTF-8 encoding
|
||||||
|
* <li>{@link Jar} tasks are configured to have the following manifest entries:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code Automatic-Module-Name}
|
||||||
|
* <li>{@code Build-Jdk-Spec}
|
||||||
|
* <li>{@code Built-By}
|
||||||
|
* <li>{@code Implementation-Title}
|
||||||
|
* <li>{@code Implementation-Version}
|
||||||
|
* </ul>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* When the {@link MavenPublishPlugin Maven Publish plugin} is applied:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>If the {@code deploymentRepository} property has been set, a
|
||||||
|
* {@link MavenArtifactRepository Maven artifact repository} is configured to publish to
|
||||||
|
* it.
|
||||||
|
* <li>The poms of all {@link MavenPublication Maven publications} are customized to meet
|
||||||
|
* Maven Central's requirements.
|
||||||
|
* <li>If the {@link JavaPlugin Java plugin} has also been applied, creation of Javadoc
|
||||||
|
* and source jars is enabled.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* When the {@link AsciidoctorJPlugin} is applied, the conventions in
|
||||||
|
* {@link AsciidoctorConventions} are applied.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ConventionsPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
applyJavaConventions(project);
|
||||||
|
applyAsciidoctorConventions(project);
|
||||||
|
applyMavenPublishingConventions(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyJavaConventions(Project project) {
|
||||||
|
project.getPlugins().withType(JavaPlugin.class, (java) -> {
|
||||||
|
configureSpringJavaFormat(project);
|
||||||
|
project.setProperty("sourceCompatibility", "1.8");
|
||||||
|
project.getTasks().withType(JavaCompile.class, (compile) -> compile.getOptions().setEncoding("UTF-8"));
|
||||||
|
project.getTasks().withType(Javadoc.class,
|
||||||
|
(javadoc) -> javadoc.getOptions().source("1.8").encoding("UTF-8"));
|
||||||
|
project.getTasks().withType(Test.class, (test) -> {
|
||||||
|
test.useJUnitPlatform();
|
||||||
|
test.setMaxHeapSize("1024M");
|
||||||
|
});
|
||||||
|
project.getTasks().withType(Jar.class, (jar) -> {
|
||||||
|
project.afterEvaluate((evaluated) -> {
|
||||||
|
jar.manifest((manifest) -> {
|
||||||
|
Map<String, Object> attributes = new TreeMap<>();
|
||||||
|
attributes.put("Automatic-Module-Name", project.getName().replace("-", "."));
|
||||||
|
attributes.put("Build-Jdk-Spec", project.property("sourceCompatibility"));
|
||||||
|
attributes.put("Built-By", "Spring");
|
||||||
|
attributes.put("Implementation-Title", project.getDescription());
|
||||||
|
attributes.put("Implementation-Version", project.getVersion());
|
||||||
|
manifest.attributes(attributes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureSpringJavaFormat(Project project) {
|
||||||
|
project.getPlugins().apply(SpringJavaFormatPlugin.class);
|
||||||
|
project.getPlugins().apply(CheckstylePlugin.class);
|
||||||
|
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||||
|
checkstyle.setToolVersion("8.22");
|
||||||
|
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||||
|
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||||
|
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||||
|
checkstyleDependencies
|
||||||
|
.add(project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
|
||||||
|
checkstyleDependencies
|
||||||
|
.add(project.getDependencies().create("io.spring.nohttp:nohttp-checkstyle:0.0.3.RELEASE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyAsciidoctorConventions(Project project) {
|
||||||
|
new AsciidoctorConventions().apply(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyMavenPublishingConventions(Project project) {
|
||||||
|
project.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> {
|
||||||
|
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||||
|
if (project.hasProperty("deploymentRepository")) {
|
||||||
|
publishing.getRepositories().maven((mavenRepository) -> {
|
||||||
|
mavenRepository.setUrl(project.property("deploymentRepository"));
|
||||||
|
mavenRepository.setName("deployment");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
publishing.getPublications().withType(MavenPublication.class)
|
||||||
|
.all((mavenPublication) -> customizePom(mavenPublication.getPom(), project));
|
||||||
|
project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {
|
||||||
|
JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||||
|
extension.withJavadocJar();
|
||||||
|
extension.withSourcesJar();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizePom(MavenPom pom, Project project) {
|
||||||
|
pom.getUrl().set("https://projects.spring.io/spring-boot/#");
|
||||||
|
pom.getDescription().set(project.provider(project::getDescription));
|
||||||
|
pom.organization(this::customizeOrganization);
|
||||||
|
pom.licenses(this::customizeLicences);
|
||||||
|
pom.developers(this::customizeDevelopers);
|
||||||
|
pom.scm(this::customizeScm);
|
||||||
|
pom.issueManagement(this::customizeIssueManagement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeOrganization(MavenPomOrganization organization) {
|
||||||
|
organization.getName().set("Pivotal Software, Inc.");
|
||||||
|
organization.getUrl().set("https://spring.io");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeLicences(MavenPomLicenseSpec licences) {
|
||||||
|
licences.license((licence) -> {
|
||||||
|
licence.getName().set("Apache License, Version 2.0");
|
||||||
|
licence.getUrl().set("http://www.apache.org/licenses/LICENSE-2.0");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeDevelopers(MavenPomDeveloperSpec developers) {
|
||||||
|
developers.developer((developer) -> {
|
||||||
|
developer.getName().set("Pivotal");
|
||||||
|
developer.getEmail().set("info@pivotal.io");
|
||||||
|
developer.getOrganization().set("Pivotal Software, Inc.");
|
||||||
|
developer.getOrganizationUrl().set("https://www.spring.io");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeScm(MavenPomScm scm) {
|
||||||
|
scm.getConnection().set("scm:git:git://github.com/spring-projects/spring-boot.git");
|
||||||
|
scm.getDeveloperConnection().set("scm:git:ssh://git@github.com/spring-projects/spring-boot.git");
|
||||||
|
scm.getUrl().set("https://github.com/spring-projects/spring-boot");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeIssueManagement(MavenPomIssueManagement issueManagement) {
|
||||||
|
issueManagement.getSystem().set("GitHub");
|
||||||
|
issueManagement.getUrl().set("https://github.com/spring-projects/spring-boot/issues");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.publish.PublishingExtension;
|
||||||
|
import org.gradle.api.publish.maven.MavenPublication;
|
||||||
|
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin applied to a project that should be deployed.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DeployedPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the task that generates the deployed pom file.
|
||||||
|
*/
|
||||||
|
public static final String GENERATE_POM_TASK_NAME = "generatePomFileForMavenPublication";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply(MavenPublishPlugin.class);
|
||||||
|
project.getPlugins().apply(MavenRepositoryPlugin.class);
|
||||||
|
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||||
|
MavenPublication mavenPublication = publishing.getPublications().create("maven", MavenPublication.class);
|
||||||
|
project.getPlugins().withType(JavaPlugin.class)
|
||||||
|
.all((javaPlugin) -> project.getComponents().matching((component) -> component.getName().equals("java"))
|
||||||
|
.all((javaComponent) -> mavenPublication.from(javaComponent)));
|
||||||
|
project.getPlugins().withType(JavaPlatformPlugin.class)
|
||||||
|
.all((javaPlugin) -> project.getComponents()
|
||||||
|
.matching((component) -> component.getName().equals("javaPlatform"))
|
||||||
|
.all((javaComponent) -> mavenPublication.from(javaComponent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.DependencySet;
|
||||||
|
import org.gradle.api.artifacts.ProjectDependency;
|
||||||
|
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.publish.PublishingExtension;
|
||||||
|
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin to make a project's {@code deployment} publication available as a Maven
|
||||||
|
* repository. The repository can be consumed by depending upon the project using the
|
||||||
|
* {@code mavenRepository} configuration.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class MavenRepositoryPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@code mavenRepository} configuration.
|
||||||
|
*/
|
||||||
|
public static final String MAVEN_REPOSITORY_CONFIGURATION_NAME = "mavenRepository";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the task that publishes to the project repository.
|
||||||
|
*/
|
||||||
|
public static final String PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME = "publishMavenPublicationToProjectRepository";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply(MavenPublishPlugin.class);
|
||||||
|
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||||
|
File repositoryLocation = new File(project.getBuildDir(), "maven-repository");
|
||||||
|
publishing.getRepositories().maven((mavenRepository) -> {
|
||||||
|
mavenRepository.setName("project");
|
||||||
|
mavenRepository.setUrl(repositoryLocation.toURI());
|
||||||
|
});
|
||||||
|
project.getTasks().matching((task) -> task.getName().equals(PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME))
|
||||||
|
.all((task) -> setUpProjectRepository(project, task, repositoryLocation));
|
||||||
|
project.getTasks().matching((task) -> task.getName().equals("publishPluginMavenPublicationToProjectRepository"))
|
||||||
|
.all((task) -> setUpProjectRepository(project, task, repositoryLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpProjectRepository(Project project, Task publishTask, File repositoryLocation) {
|
||||||
|
publishTask.doFirst(new CleanAction(repositoryLocation));
|
||||||
|
Configuration projectRepository = project.getConfigurations().create(MAVEN_REPOSITORY_CONFIGURATION_NAME);
|
||||||
|
project.getArtifacts().add(projectRepository.getName(), repositoryLocation,
|
||||||
|
(artifact) -> artifact.builtBy(publishTask));
|
||||||
|
DependencySet target = projectRepository.getDependencies();
|
||||||
|
project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> addMavenRepositoryDependencies(project,
|
||||||
|
JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, target));
|
||||||
|
project.getPlugins().withType(JavaLibraryPlugin.class)
|
||||||
|
.all((javaLibraryPlugin) -> addMavenRepositoryDependencies(project, JavaPlugin.API_CONFIGURATION_NAME,
|
||||||
|
target));
|
||||||
|
project.getPlugins().withType(JavaPlatformPlugin.class)
|
||||||
|
.all((javaPlugin) -> addMavenRepositoryDependencies(project, JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
||||||
|
target));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMavenRepositoryDependencies(Project project, String sourceConfigurationName, DependencySet target) {
|
||||||
|
project.getConfigurations().getByName(sourceConfigurationName).getDependencies()
|
||||||
|
.withType(ProjectDependency.class).all((dependency) -> {
|
||||||
|
Map<String, String> dependencyDescriptor = new HashMap<>();
|
||||||
|
dependencyDescriptor.put("path", dependency.getDependencyProject().getPath());
|
||||||
|
dependencyDescriptor.put("configuration", MAVEN_REPOSITORY_CONFIGURATION_NAME);
|
||||||
|
target.add(project.getDependencies().project(dependencyDescriptor));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CleanAction implements Action<Task> {
|
||||||
|
|
||||||
|
private final File location;
|
||||||
|
|
||||||
|
private CleanAction(File location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Task task) {
|
||||||
|
task.getProject().delete(this.location);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} for generating metadata describing a project's auto-configuration
|
||||||
|
* classes.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class AutoConfigurationMetadata extends AbstractTask {
|
||||||
|
|
||||||
|
private SourceSet sourceSet;
|
||||||
|
|
||||||
|
private File outputFile;
|
||||||
|
|
||||||
|
public AutoConfigurationMetadata() {
|
||||||
|
getInputs().file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||||
|
"META-INF/spring.factories"));
|
||||||
|
dependsOn((Callable<String>) () -> this.sourceSet.getProcessResourcesTaskName());
|
||||||
|
getProject().getConfigurations()
|
||||||
|
.maybeCreate(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceSet(SourceSet sourceSet) {
|
||||||
|
this.sourceSet = sourceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getOutputFile() {
|
||||||
|
return this.outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFile(File outputFile) {
|
||||||
|
this.outputFile = outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentAutoConfiguration() throws IOException {
|
||||||
|
Properties autoConfiguration = readAutoConfiguration();
|
||||||
|
getOutputFile().getParentFile().mkdirs();
|
||||||
|
try (FileWriter writer = new FileWriter(getOutputFile())) {
|
||||||
|
autoConfiguration.store(writer, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties readAutoConfiguration() throws IOException {
|
||||||
|
Properties autoConfiguration = new Properties();
|
||||||
|
Properties springFactories = readSpringFactories(
|
||||||
|
new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||||
|
autoConfiguration.setProperty("autoConfigurationClassNames",
|
||||||
|
springFactories.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration"));
|
||||||
|
autoConfiguration.setProperty("module", getProject().getName());
|
||||||
|
return autoConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties readSpringFactories(File file) throws IOException {
|
||||||
|
Properties springFactories = new Properties();
|
||||||
|
try (Reader in = new FileReader(file)) {
|
||||||
|
springFactories.load(in);
|
||||||
|
}
|
||||||
|
return springFactories;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.DeployedPlugin;
|
||||||
|
import org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Plugin} for projects that define auto-configuration. When applied, the plugin
|
||||||
|
* applies the {@link DeployedPlugin}. Additionally, it reacts to the presence of the
|
||||||
|
* {@link JavaPlugin} by:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Applying the {@link ConfigurationPropertiesPlugin}.
|
||||||
|
* <li>Adding a dependency on the auto-configuration annotation processor.
|
||||||
|
* <li>Defining a task that produces metadata describing the auto-configuration. The
|
||||||
|
* metadata is made available as an artifact in the
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class AutoConfigurationPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@link Configuration} that holds the auto-configuration metadata
|
||||||
|
* artifact.
|
||||||
|
*/
|
||||||
|
public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply(DeployedPlugin.class);
|
||||||
|
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||||
|
project.getPlugins().apply(ConfigurationPropertiesPlugin.class);
|
||||||
|
Configuration annotationProcessors = project.getConfigurations()
|
||||||
|
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||||
|
annotationProcessors.getDependencies()
|
||||||
|
.add(project.getDependencies().project(Collections.singletonMap("path",
|
||||||
|
":spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor")));
|
||||||
|
annotationProcessors.getDependencies()
|
||||||
|
.add(project.getDependencies().project(Collections.singletonMap("path",
|
||||||
|
":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
|
||||||
|
project.getTasks().create("autoConfigurationMetadata", AutoConfigurationMetadata.class, (task) -> {
|
||||||
|
task.setSourceSet(project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
|
||||||
|
.getByName(SourceSet.MAIN_SOURCE_SET_NAME));
|
||||||
|
task.setOutputFile(new File(project.getBuildDir(), "auto-configuration-metadata.properties"));
|
||||||
|
project.getArtifacts().add(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME,
|
||||||
|
project.provider((Callable<File>) task::getOutputFile), (artifact) -> artifact.builtBy(task));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} used to document auto-configuration classes.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentAutoConfigurationClasses extends AbstractTask {
|
||||||
|
|
||||||
|
private FileCollection autoConfiguration;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getAutoConfiguration() {
|
||||||
|
return this.autoConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoConfiguration(FileCollection autoConfiguration) {
|
||||||
|
this.autoConfiguration = autoConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentAutoConfigurationClasses() throws IOException {
|
||||||
|
for (File metadataFile : this.autoConfiguration) {
|
||||||
|
Properties metadata = new Properties();
|
||||||
|
try (Reader reader = new FileReader(metadataFile)) {
|
||||||
|
metadata.load(reader);
|
||||||
|
}
|
||||||
|
AutoConfiguration autoConfiguration = new AutoConfiguration(metadata.getProperty("module"), new TreeSet<>(
|
||||||
|
StringUtils.commaDelimitedListToSet(metadata.getProperty("autoConfigurationClassNames"))));
|
||||||
|
writeTable(autoConfiguration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTable(AutoConfiguration autoConfigurationClasses) throws IOException {
|
||||||
|
this.outputDir.mkdirs();
|
||||||
|
try (PrintWriter writer = new PrintWriter(
|
||||||
|
new FileWriter(new File(this.outputDir, autoConfigurationClasses.module + ".adoc")))) {
|
||||||
|
writer.println("[cols=\"4,1\"]");
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println("| Configuration Class | Links");
|
||||||
|
|
||||||
|
for (AutoConfigurationClass autoConfigurationClass : autoConfigurationClasses.classes) {
|
||||||
|
writer.println();
|
||||||
|
writer.printf("| {spring-boot-code}/spring-boot-project/%s/src/main/java/%s.java[`%s`]%n",
|
||||||
|
autoConfigurationClasses.module, autoConfigurationClass.path, autoConfigurationClass.name);
|
||||||
|
writer.printf("| {spring-boot-api}/%s.html[javadoc]%n", autoConfigurationClass.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AutoConfiguration {
|
||||||
|
|
||||||
|
private final String module;
|
||||||
|
|
||||||
|
private final SortedSet<AutoConfigurationClass> classes;
|
||||||
|
|
||||||
|
private AutoConfiguration(String module, Set<String> classNames) {
|
||||||
|
this.module = module;
|
||||||
|
this.classes = classNames.stream().map((className) -> {
|
||||||
|
String path = className.replace('.', '/');
|
||||||
|
String name = className.substring(className.lastIndexOf('.') + 1);
|
||||||
|
return new AutoConfigurationClass(name, path);
|
||||||
|
}).collect(Collectors.toCollection(TreeSet::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AutoConfigurationClass implements Comparable<AutoConfigurationClass> {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
private AutoConfigurationClass(String name, String path) {
|
||||||
|
this.name = name;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(AutoConfigurationClass other) {
|
||||||
|
return this.name.compareTo(other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,297 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
import groovy.lang.GroovyObjectSupport;
|
||||||
|
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
|
||||||
|
import org.apache.maven.artifact.versioning.VersionRange;
|
||||||
|
import org.gradle.api.InvalidUserCodeException;
|
||||||
|
import org.gradle.api.InvalidUserDataException;
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||||
|
import org.gradle.util.ConfigureUtil;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.Library.Exclusion;
|
||||||
|
import org.springframework.boot.build.bom.Library.Group;
|
||||||
|
import org.springframework.boot.build.bom.Library.Module;
|
||||||
|
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DSL extensions for {@link BomPlugin}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class BomExtension {
|
||||||
|
|
||||||
|
private final Map<String, DependencyVersion> properties = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, String> artifactVersionProperties = new HashMap<>();
|
||||||
|
|
||||||
|
private final List<Library> libraries = new ArrayList<Library>();
|
||||||
|
|
||||||
|
private final DependencyHandler dependencyHandler;
|
||||||
|
|
||||||
|
private final UpgradeHandler upgradeHandler = new UpgradeHandler();
|
||||||
|
|
||||||
|
public BomExtension(DependencyHandler dependencyHandler) {
|
||||||
|
this.dependencyHandler = dependencyHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Library> getLibraries() {
|
||||||
|
return this.libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upgrade(Closure<?> closure) {
|
||||||
|
ConfigureUtil.configure(closure, this.upgradeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Upgrade getUpgrade() {
|
||||||
|
return new Upgrade(this.upgradeHandler.upgradePolicy, new GitHub(this.upgradeHandler.gitHub.organization,
|
||||||
|
this.upgradeHandler.gitHub.repository, this.upgradeHandler.gitHub.issueLabels));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void library(String name, String version, Closure<?> closure) {
|
||||||
|
LibraryHandler libraryHandler = new LibraryHandler();
|
||||||
|
ConfigureUtil.configure(closure, libraryHandler);
|
||||||
|
addLibrary(new Library(name, DependencyVersion.parse(version), libraryHandler.groups,
|
||||||
|
libraryHandler.prohibitedVersions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createDependencyNotation(String groupId, String artifactId, DependencyVersion version) {
|
||||||
|
return groupId + ":" + artifactId + ":" + version;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, DependencyVersion> getProperties() {
|
||||||
|
return this.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getArtifactVersionProperty(String groupId, String artifactId) {
|
||||||
|
String coordinates = groupId + ":" + artifactId;
|
||||||
|
return this.artifactVersionProperties.get(coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putArtifactVersionProperty(String groupId, String artifactId, String versionProperty) {
|
||||||
|
String coordinates = groupId + ":" + artifactId;
|
||||||
|
String existing = this.artifactVersionProperties.putIfAbsent(coordinates, versionProperty);
|
||||||
|
if (existing != null) {
|
||||||
|
throw new InvalidUserDataException("Cannot put version property for '" + coordinates
|
||||||
|
+ "'. Version property '" + existing + "' has already been stored.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLibrary(Library library) {
|
||||||
|
this.libraries.add(library);
|
||||||
|
this.properties.put(library.getVersionProperty(), library.getVersion());
|
||||||
|
for (Group group : library.getGroups()) {
|
||||||
|
for (Module module : group.getModules()) {
|
||||||
|
this.putArtifactVersionProperty(group.getId(), module.getName(), library.getVersionProperty());
|
||||||
|
this.dependencyHandler.getConstraints().add("api",
|
||||||
|
createDependencyNotation(group.getId(), module.getName(), library.getVersion()));
|
||||||
|
}
|
||||||
|
for (String bomImport : group.getBoms()) {
|
||||||
|
this.putArtifactVersionProperty(group.getId(), bomImport, library.getVersionProperty());
|
||||||
|
this.dependencyHandler.add("api", this.dependencyHandler
|
||||||
|
.enforcedPlatform(createDependencyNotation(group.getId(), bomImport, library.getVersion())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LibraryHandler {
|
||||||
|
|
||||||
|
private final List<Group> groups = new ArrayList<>();
|
||||||
|
|
||||||
|
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
|
||||||
|
|
||||||
|
public void group(String id, Closure<?> closure) {
|
||||||
|
GroupHandler groupHandler = new GroupHandler(id);
|
||||||
|
ConfigureUtil.configure(closure, groupHandler);
|
||||||
|
this.groups
|
||||||
|
.add(new Group(groupHandler.id, groupHandler.modules, groupHandler.plugins, groupHandler.imports));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prohibit(String range, Closure<?> closure) {
|
||||||
|
ProhibitedVersionHandler prohibitedVersionHandler = new ProhibitedVersionHandler();
|
||||||
|
ConfigureUtil.configure(closure, prohibitedVersionHandler);
|
||||||
|
try {
|
||||||
|
this.prohibitedVersions.add(new ProhibitedVersion(VersionRange.createFromVersionSpec(range),
|
||||||
|
prohibitedVersionHandler.reason));
|
||||||
|
}
|
||||||
|
catch (InvalidVersionSpecificationException ex) {
|
||||||
|
throw new InvalidUserCodeException("Invalid version range", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProhibitedVersionHandler {
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public void because(String because) {
|
||||||
|
this.reason = because;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GroupHandler extends GroovyObjectSupport {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private List<Module> modules = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<String> imports = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<String> plugins = new ArrayList<>();
|
||||||
|
|
||||||
|
public GroupHandler(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModules(List<Object> modules) {
|
||||||
|
this.modules = modules.stream()
|
||||||
|
.map((input) -> (input instanceof Module) ? (Module) input : new Module((String) input))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImports(List<String> imports) {
|
||||||
|
this.imports = imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlugins(List<String> plugins) {
|
||||||
|
this.plugins = plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object methodMissing(String name, Object args) {
|
||||||
|
if (args instanceof Object[] && ((Object[]) args).length == 1) {
|
||||||
|
Object arg = ((Object[]) args)[0];
|
||||||
|
if (arg instanceof Closure) {
|
||||||
|
ExclusionHandler exclusionHandler = new ExclusionHandler();
|
||||||
|
ConfigureUtil.configure((Closure<?>) arg, exclusionHandler);
|
||||||
|
return new Module(name, exclusionHandler.exclusions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InvalidUserDataException("Invalid exclusion configuration for module '" + name + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExclusionHandler {
|
||||||
|
|
||||||
|
private final List<Exclusion> exclusions = new ArrayList<>();
|
||||||
|
|
||||||
|
public void exclude(Map<String, String> exclusion) {
|
||||||
|
this.exclusions.add(new Exclusion(exclusion.get("group"), exclusion.get("module")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpgradeHandler {
|
||||||
|
|
||||||
|
private UpgradePolicy upgradePolicy;
|
||||||
|
|
||||||
|
private final GitHubHandler gitHub = new GitHubHandler();
|
||||||
|
|
||||||
|
public void setPolicy(UpgradePolicy upgradePolicy) {
|
||||||
|
this.upgradePolicy = upgradePolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void gitHub(Closure<?> closure) {
|
||||||
|
ConfigureUtil.configure(closure, this.gitHub);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Upgrade {
|
||||||
|
|
||||||
|
private final UpgradePolicy upgradePolicy;
|
||||||
|
|
||||||
|
private final GitHub gitHub;
|
||||||
|
|
||||||
|
private Upgrade(UpgradePolicy upgradePolicy, GitHub gitHub) {
|
||||||
|
this.upgradePolicy = upgradePolicy;
|
||||||
|
this.gitHub = gitHub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpgradePolicy getPolicy() {
|
||||||
|
return this.upgradePolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHub getGitHub() {
|
||||||
|
return this.gitHub;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GitHubHandler {
|
||||||
|
|
||||||
|
private String organization = "spring-projects";
|
||||||
|
|
||||||
|
private String repository = "spring-boot";
|
||||||
|
|
||||||
|
private List<String> issueLabels;
|
||||||
|
|
||||||
|
public void setOrganization(String organization) {
|
||||||
|
this.organization = organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepository(String repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIssueLabels(List<String> issueLabels) {
|
||||||
|
this.issueLabels = issueLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class GitHub {
|
||||||
|
|
||||||
|
private String organization = "spring-projects";
|
||||||
|
|
||||||
|
private String repository = "spring-boot";
|
||||||
|
|
||||||
|
private List<String> issueLabels;
|
||||||
|
|
||||||
|
private GitHub(String organization, String repository, List<String> issueLabels) {
|
||||||
|
this.organization = organization;
|
||||||
|
this.repository = repository;
|
||||||
|
this.issueLabels = issueLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrganization() {
|
||||||
|
return this.organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getIssueLabels() {
|
||||||
|
return this.issueLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import javax.xml.xpath.XPath;
|
||||||
|
import javax.xml.xpath.XPathConstants;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
|
import groovy.util.Node;
|
||||||
|
import groovy.xml.QName;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.plugins.JavaPlatformExtension;
|
||||||
|
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||||
|
import org.gradle.api.plugins.PluginContainer;
|
||||||
|
import org.gradle.api.publish.PublishingExtension;
|
||||||
|
import org.gradle.api.publish.maven.MavenPom;
|
||||||
|
import org.gradle.api.publish.maven.MavenPublication;
|
||||||
|
import org.gradle.api.publish.maven.tasks.GenerateMavenPom;
|
||||||
|
import org.gradle.api.tasks.Sync;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.DeployedPlugin;
|
||||||
|
import org.springframework.boot.build.MavenRepositoryPlugin;
|
||||||
|
import org.springframework.boot.build.bom.Library.Group;
|
||||||
|
import org.springframework.boot.build.bom.bomr.UpgradeBom;
|
||||||
|
import org.springframework.boot.build.mavenplugin.MavenExec;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Plugin} for defining a bom. Dependencies are added as constraints in the
|
||||||
|
* {@code api} configuration. Imported boms are added as enforced platforms in the
|
||||||
|
* {@code api} configuration.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class BomPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
PluginContainer plugins = project.getPlugins();
|
||||||
|
plugins.apply(DeployedPlugin.class);
|
||||||
|
plugins.apply(MavenRepositoryPlugin.class);
|
||||||
|
plugins.apply(JavaPlatformPlugin.class);
|
||||||
|
JavaPlatformExtension javaPlatform = project.getExtensions().getByType(JavaPlatformExtension.class);
|
||||||
|
javaPlatform.allowDependencies();
|
||||||
|
BomExtension bom = project.getExtensions().create("bom", BomExtension.class, project.getDependencies());
|
||||||
|
project.getTasks().create("bomrCheck", CheckBom.class, bom);
|
||||||
|
project.getTasks().create("bomrUpgrade", UpgradeBom.class, bom);
|
||||||
|
new PublishingCustomizer(project, bom).customize();
|
||||||
|
Configuration effectiveBomConfiguration = project.getConfigurations().create("effectiveBom");
|
||||||
|
project.getTasks().matching((task) -> task.getName().equals(DeployedPlugin.GENERATE_POM_TASK_NAME))
|
||||||
|
.all((task) -> {
|
||||||
|
Sync syncBom = project.getTasks().create("syncBom", Sync.class);
|
||||||
|
syncBom.dependsOn(task);
|
||||||
|
File generatedBomDir = new File(project.getBuildDir(), "generated/bom");
|
||||||
|
syncBom.setDestinationDir(generatedBomDir);
|
||||||
|
syncBom.from(((GenerateMavenPom) task).getDestination(), (pom) -> pom.rename((name) -> "pom.xml"));
|
||||||
|
try {
|
||||||
|
String settingsXmlContent = FileCopyUtils
|
||||||
|
.copyToString(new InputStreamReader(
|
||||||
|
getClass().getClassLoader().getResourceAsStream("effective-bom-settings.xml"),
|
||||||
|
StandardCharsets.UTF_8))
|
||||||
|
.replace("localRepositoryPath",
|
||||||
|
new File(project.getBuildDir(), "local-m2-repository").getAbsolutePath());
|
||||||
|
syncBom.from(project.getResources().getText().fromString(settingsXmlContent),
|
||||||
|
(settingsXml) -> settingsXml.rename((name) -> "settings.xml"));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new GradleException("Failed to prepare settings.xml", ex);
|
||||||
|
}
|
||||||
|
MavenExec generateEffectiveBom = project.getTasks().create("generateEffectiveBom", MavenExec.class);
|
||||||
|
generateEffectiveBom.setProjectDir(generatedBomDir);
|
||||||
|
File effectiveBom = new File(project.getBuildDir(),
|
||||||
|
"generated/effective-bom/" + project.getName() + "-effective-bom.xml");
|
||||||
|
generateEffectiveBom.args("--settings", "settings.xml", "help:effective-pom",
|
||||||
|
"-Doutput=" + effectiveBom);
|
||||||
|
generateEffectiveBom.dependsOn(syncBom);
|
||||||
|
generateEffectiveBom.getOutputs().file(effectiveBom);
|
||||||
|
generateEffectiveBom.doLast(new StripUnrepeatableOutputAction(effectiveBom));
|
||||||
|
project.getArtifacts().add(effectiveBomConfiguration.getName(), effectiveBom,
|
||||||
|
(artifact) -> artifact.builtBy(generateEffectiveBom));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PublishingCustomizer {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
private final BomExtension bom;
|
||||||
|
|
||||||
|
private PublishingCustomizer(Project project, BomExtension bom) {
|
||||||
|
this.project = project;
|
||||||
|
this.bom = bom;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customize() {
|
||||||
|
PublishingExtension publishing = this.project.getExtensions().getByType(PublishingExtension.class);
|
||||||
|
publishing.getPublications().withType(MavenPublication.class).all(this::configurePublication);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurePublication(MavenPublication publication) {
|
||||||
|
publication.pom(this::customizePom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void customizePom(MavenPom pom) {
|
||||||
|
pom.withXml((xml) -> {
|
||||||
|
Node projectNode = xml.asNode();
|
||||||
|
Node properties = new Node(null, "properties");
|
||||||
|
this.bom.getProperties().forEach(properties::appendNode);
|
||||||
|
Node dependencyManagement = findChild(projectNode, "dependencyManagement");
|
||||||
|
if (dependencyManagement != null) {
|
||||||
|
addPropertiesBeforeDependencyManagement(projectNode, properties);
|
||||||
|
replaceVersionsWithVersionPropertyReferences(dependencyManagement);
|
||||||
|
addExclusionsToManagedDependencies(dependencyManagement);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
projectNode.children().add(properties);
|
||||||
|
}
|
||||||
|
addPluginManagement(projectNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void addPropertiesBeforeDependencyManagement(Node projectNode, Node properties) {
|
||||||
|
for (int i = 0; i < projectNode.children().size(); i++) {
|
||||||
|
if (isNodeWithName(projectNode.children().get(i), "dependencyManagement")) {
|
||||||
|
projectNode.children().add(i, properties);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceVersionsWithVersionPropertyReferences(Node dependencyManagement) {
|
||||||
|
Node dependencies = findChild(dependencyManagement, "dependencies");
|
||||||
|
if (dependencies != null) {
|
||||||
|
for (Node dependency : findChildren(dependencies, "dependency")) {
|
||||||
|
String groupId = findChild(dependency, "groupId").text();
|
||||||
|
String artifactId = findChild(dependency, "artifactId").text();
|
||||||
|
findChild(dependency, "version")
|
||||||
|
.setValue("${" + this.bom.getArtifactVersionProperty(groupId, artifactId) + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addExclusionsToManagedDependencies(Node dependencyManagement) {
|
||||||
|
Node dependencies = findChild(dependencyManagement, "dependencies");
|
||||||
|
if (dependencies != null) {
|
||||||
|
for (Node dependency : findChildren(dependencies, "dependency")) {
|
||||||
|
String groupId = findChild(dependency, "groupId").text();
|
||||||
|
String artifactId = findChild(dependency, "artifactId").text();
|
||||||
|
this.bom.getLibraries().stream().flatMap((library) -> library.getGroups().stream())
|
||||||
|
.filter((group) -> group.getId().equals(groupId))
|
||||||
|
.flatMap((group) -> group.getModules().stream())
|
||||||
|
.filter((module) -> module.getName().equals(artifactId))
|
||||||
|
.flatMap((module) -> module.getExclusions().stream()).forEach((exclusion) -> {
|
||||||
|
Node exclusions = findOrCreateNode(dependency, "exclusions");
|
||||||
|
Node node = new Node(exclusions, "exclusion");
|
||||||
|
node.appendNode("groupId", exclusion.getGroupId());
|
||||||
|
node.appendNode("artifactId", exclusion.getArtifactId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPluginManagement(Node projectNode) {
|
||||||
|
for (Library library : this.bom.getLibraries()) {
|
||||||
|
for (Group group : library.getGroups()) {
|
||||||
|
Node plugins = findOrCreateNode(projectNode, "build", "pluginManagement", "plugins");
|
||||||
|
for (String pluginName : group.getPlugins()) {
|
||||||
|
Node plugin = new Node(plugins, "plugin");
|
||||||
|
plugin.appendNode("groupId", group.getId());
|
||||||
|
plugin.appendNode("artifactId", pluginName);
|
||||||
|
plugin.appendNode("version", "${" + library.getVersionProperty() + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node findOrCreateNode(Node parent, String... path) {
|
||||||
|
Node current = parent;
|
||||||
|
for (String nodeName : path) {
|
||||||
|
Node child = findChild(current, nodeName);
|
||||||
|
if (child == null) {
|
||||||
|
child = new Node(current, nodeName);
|
||||||
|
}
|
||||||
|
current = child;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node findChild(Node parent, String name) {
|
||||||
|
for (Object child : parent.children()) {
|
||||||
|
if (child instanceof Node) {
|
||||||
|
Node node = (Node) child;
|
||||||
|
if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (name.equals(node.name())) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Node> findChildren(Node parent, String name) {
|
||||||
|
return (List<Node>) parent.children().stream().filter((child) -> isNodeWithName(child, name))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNodeWithName(Object candidate, String name) {
|
||||||
|
if (candidate instanceof Node) {
|
||||||
|
Node node = (Node) candidate;
|
||||||
|
if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name.equals(node.name())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class StripUnrepeatableOutputAction implements Action<Task> {
|
||||||
|
|
||||||
|
private final File effectiveBom;
|
||||||
|
|
||||||
|
private StripUnrepeatableOutputAction(File xmlFile) {
|
||||||
|
this.effectiveBom = xmlFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Task task) {
|
||||||
|
try {
|
||||||
|
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(this.effectiveBom);
|
||||||
|
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||||
|
NodeList comments = (NodeList) xpath.evaluate("//comment()", document, XPathConstants.NODESET);
|
||||||
|
for (int i = 0; i < comments.getLength(); i++) {
|
||||||
|
org.w3c.dom.Node comment = comments.item(i);
|
||||||
|
comment.getParentNode().removeChild(comment);
|
||||||
|
}
|
||||||
|
org.w3c.dom.Node build = (org.w3c.dom.Node) xpath.evaluate("/project/build", document,
|
||||||
|
XPathConstants.NODE);
|
||||||
|
build.getParentNode().removeChild(build);
|
||||||
|
org.w3c.dom.Node reporting = (org.w3c.dom.Node) xpath.evaluate("/project/reporting", document,
|
||||||
|
XPathConstants.NODE);
|
||||||
|
reporting.getParentNode().removeChild(reporting);
|
||||||
|
TransformerFactory.newInstance().newTransformer().transform(new DOMSource(document),
|
||||||
|
new StreamResult(this.effectiveBom));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new TaskExecutionException(task, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.InvalidUserDataException;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.Library.Group;
|
||||||
|
import org.springframework.boot.build.bom.Library.Module;
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the validity of a bom.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class CheckBom extends DefaultTask {
|
||||||
|
|
||||||
|
private final BomExtension bom;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CheckBom(BomExtension bom) {
|
||||||
|
this.bom = bom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void checkBom() {
|
||||||
|
for (Library library : this.bom.getLibraries()) {
|
||||||
|
for (Group group : library.getGroups()) {
|
||||||
|
for (Module module : group.getModules()) {
|
||||||
|
if (!module.getExclusions().isEmpty()) {
|
||||||
|
checkExclusions(group.getId(), module, library.getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkExclusions(String groupId, Module module, DependencyVersion version) {
|
||||||
|
Set<String> resolved = getProject().getConfigurations()
|
||||||
|
.detachedConfiguration(
|
||||||
|
getProject().getDependencies().create(groupId + ":" + module.getName() + ":" + version))
|
||||||
|
.getResolvedConfiguration().getResolvedArtifacts().stream()
|
||||||
|
.map((artifact) -> artifact.getModuleVersion().getId())
|
||||||
|
.map((id) -> id.getGroup() + ":" + id.getModule().getName()).collect(Collectors.toSet());
|
||||||
|
Set<String> exclusions = module.getExclusions().stream()
|
||||||
|
.map((exclusion) -> exclusion.getGroupId() + ":" + exclusion.getArtifactId())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<String> unused = new TreeSet<>();
|
||||||
|
for (String exclusion : exclusions) {
|
||||||
|
if (!resolved.contains(exclusion) && exclusion.endsWith(":*")) {
|
||||||
|
String group = exclusion.substring(0, exclusion.indexOf(':') + 1);
|
||||||
|
if (!resolved.stream().filter((candidate) -> candidate.startsWith(group)).findFirst().isPresent()) {
|
||||||
|
unused.add(exclusion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exclusions.removeAll(resolved);
|
||||||
|
if (!unused.isEmpty()) {
|
||||||
|
throw new InvalidUserDataException(
|
||||||
|
"Unnecessary exclusions on " + groupId + ":" + module.getName() + ": " + exclusions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.VersionRange;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of modules, Maven plugins, and Maven boms that are versioned and released
|
||||||
|
* together.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class Library {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final DependencyVersion version;
|
||||||
|
|
||||||
|
private final List<Group> groups;
|
||||||
|
|
||||||
|
private final String versionProperty;
|
||||||
|
|
||||||
|
private final List<ProhibitedVersion> prohibitedVersions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code Library} with the given {@code name}, {@code version}, and
|
||||||
|
* {@code groups}.
|
||||||
|
* @param name name of the library
|
||||||
|
* @param version version of the library
|
||||||
|
* @param groups groups in the library
|
||||||
|
* @param prohibitedVersions version of the library that are prohibited
|
||||||
|
*/
|
||||||
|
public Library(String name, DependencyVersion version, List<Group> groups,
|
||||||
|
List<ProhibitedVersion> prohibitedVersions) {
|
||||||
|
this.name = name;
|
||||||
|
this.version = version;
|
||||||
|
this.groups = groups;
|
||||||
|
this.versionProperty = name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version";
|
||||||
|
this.prohibitedVersions = prohibitedVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DependencyVersion getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Group> getGroups() {
|
||||||
|
return this.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionProperty() {
|
||||||
|
return this.versionProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProhibitedVersion> getProhibitedVersions() {
|
||||||
|
return this.prohibitedVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version or range of versions that are prohibited from being used in a bom.
|
||||||
|
*/
|
||||||
|
public static class ProhibitedVersion {
|
||||||
|
|
||||||
|
private final VersionRange range;
|
||||||
|
|
||||||
|
private final String reason;
|
||||||
|
|
||||||
|
public ProhibitedVersion(VersionRange range, String reason) {
|
||||||
|
this.range = range;
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionRange getRange() {
|
||||||
|
return this.range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return this.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of modules, Maven plugins, and Maven boms with the same group ID.
|
||||||
|
*/
|
||||||
|
public static class Group {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private final List<Module> modules;
|
||||||
|
|
||||||
|
private final List<String> plugins;
|
||||||
|
|
||||||
|
private final List<String> boms;
|
||||||
|
|
||||||
|
public Group(String id, List<Module> modules, List<String> plugins, List<String> boms) {
|
||||||
|
this.id = id;
|
||||||
|
this.modules = modules;
|
||||||
|
this.plugins = plugins;
|
||||||
|
this.boms = boms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Module> getModules() {
|
||||||
|
return this.modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPlugins() {
|
||||||
|
return this.plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getBoms() {
|
||||||
|
return this.boms;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A module in a group.
|
||||||
|
*/
|
||||||
|
public static class Module {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final List<Exclusion> exclusions;
|
||||||
|
|
||||||
|
public Module(String name) {
|
||||||
|
this(name, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Module(String name, List<Exclusion> exclusions) {
|
||||||
|
this.name = name;
|
||||||
|
this.exclusions = exclusions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Exclusion> getExclusions() {
|
||||||
|
return this.exclusions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exclusion of a dependency identified by its group ID and artifact ID.
|
||||||
|
*/
|
||||||
|
public static class Exclusion {
|
||||||
|
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
|
private final String artifactId;
|
||||||
|
|
||||||
|
public Exclusion(String groupId, String artifactId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.artifactId = artifactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArtifactId() {
|
||||||
|
return this.artifactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policies used to decide which versions are considered as possible upgrades.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public enum UpgradePolicy implements BiPredicate<DependencyVersion, DependencyVersion> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All versions more recent than the current version will be suggested as possible
|
||||||
|
* upgrades.
|
||||||
|
*/
|
||||||
|
ANY((candidate, current) -> current.compareTo(candidate) < 0),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New minor versions of the current major version will be suggested as possible
|
||||||
|
* upgrades. For example, if the current version is 1.2.3, all 1.x.y versions after
|
||||||
|
* 1.2.3 will be suggested. 2.x versions will not be offered.
|
||||||
|
*/
|
||||||
|
SAME_MAJOR_VERSION(DependencyVersion::isSameMajorAndNewerThan),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New patch versions of the current minor version will be offered as possible
|
||||||
|
* upgrades. For example, if the current version is 1.2.3, all 1.2.x versions after
|
||||||
|
* 1.2.3 will be suggested. 1.x versions will not be offered.
|
||||||
|
*/
|
||||||
|
SAME_MINOR_VERSION(DependencyVersion::isSameMinorAndNewerThan);
|
||||||
|
|
||||||
|
private BiPredicate<DependencyVersion, DependencyVersion> delegate;
|
||||||
|
|
||||||
|
UpgradePolicy(BiPredicate<DependencyVersion, DependencyVersion> delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(DependencyVersion candidate, DependencyVersion current) {
|
||||||
|
return this.delegate.test(candidate, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||||
|
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.Library;
|
||||||
|
import org.springframework.boot.build.bom.Library.Group;
|
||||||
|
import org.springframework.boot.build.bom.Library.Module;
|
||||||
|
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
|
||||||
|
import org.springframework.boot.build.bom.UpgradePolicy;
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive {@link UpgradeResolver} that uses command line input to choose the upgrades
|
||||||
|
* to apply.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public final class InteractiveUpgradeResolver implements UpgradeResolver {
|
||||||
|
|
||||||
|
private final VersionResolver versionResolver;
|
||||||
|
|
||||||
|
private final UpgradePolicy upgradePolicy;
|
||||||
|
|
||||||
|
private final UserInputHandler userInputHandler;
|
||||||
|
|
||||||
|
InteractiveUpgradeResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy,
|
||||||
|
UserInputHandler userInputHandler) {
|
||||||
|
this.versionResolver = versionResolver;
|
||||||
|
this.upgradePolicy = upgradePolicy;
|
||||||
|
this.userInputHandler = userInputHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Upgrade> resolveUpgrades(Collection<Library> libraries) {
|
||||||
|
return libraries.stream().map(this::resolveUpgrade).filter((upgrade) -> upgrade != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Upgrade resolveUpgrade(Library library) {
|
||||||
|
Map<String, SortedSet<DependencyVersion>> moduleVersions = new LinkedHashMap<>();
|
||||||
|
for (Group group : library.getGroups()) {
|
||||||
|
for (Module module : group.getModules()) {
|
||||||
|
moduleVersions.put(group.getId() + ":" + module.getName(),
|
||||||
|
getLaterVersionsForModule(group.getId(), module.getName(), library.getVersion()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<DependencyVersion> allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
|
||||||
|
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (allVersions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<VersionOption> versionOptions = allVersions.stream()
|
||||||
|
.map((version) -> new VersionOption(version, getMissingModules(moduleVersions, version)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
VersionOption current = new VersionOption(library.getVersion(), Collections.emptyList());
|
||||||
|
VersionOption selected = this.userInputHandler.selectOption(library.getName() + " " + library.getVersion(),
|
||||||
|
versionOptions, current);
|
||||||
|
return (selected.equals(current)) ? null : new Upgrade(library, selected.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
|
||||||
|
if (prohibitedVersions.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (ProhibitedVersion prohibitedVersion : prohibitedVersions) {
|
||||||
|
if (prohibitedVersion.getRange()
|
||||||
|
.containsVersion(new DefaultArtifactVersion(dependencyVersion.toString()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,
|
||||||
|
DependencyVersion version) {
|
||||||
|
List<String> missingModules = new ArrayList<>();
|
||||||
|
moduleVersions.forEach((name, versions) -> {
|
||||||
|
if (!versions.contains(version)) {
|
||||||
|
missingModules.add(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return missingModules;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedSet<DependencyVersion> getLaterVersionsForModule(String groupId, String artifactId,
|
||||||
|
DependencyVersion currentVersion) {
|
||||||
|
SortedSet<DependencyVersion> versions = this.versionResolver.resolveVersions(groupId, artifactId);
|
||||||
|
versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion));
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class VersionOption {
|
||||||
|
|
||||||
|
private final DependencyVersion version;
|
||||||
|
|
||||||
|
private final List<String> missingModules;
|
||||||
|
|
||||||
|
private VersionOption(DependencyVersion version, List<String> missingModules) {
|
||||||
|
this.version = version;
|
||||||
|
this.missingModules = missingModules;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (this.missingModules.isEmpty()) {
|
||||||
|
return this.version.toString();
|
||||||
|
}
|
||||||
|
return this.version + " (some modules are missing: "
|
||||||
|
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.xpath.XPathConstants;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link VersionResolver} that examines {@code maven-metadata.xml} to determine the
|
||||||
|
* available versions.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class MavenMetadataVersionResolver implements VersionResolver {
|
||||||
|
|
||||||
|
private final RestTemplate rest;
|
||||||
|
|
||||||
|
private final List<String> repositoryUrls;
|
||||||
|
|
||||||
|
MavenMetadataVersionResolver(List<String> repositoryUrls) {
|
||||||
|
this(new RestTemplate(Arrays.asList(new StringHttpMessageConverter())), repositoryUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
MavenMetadataVersionResolver(RestTemplate restTemplate, List<String> repositoryUrls) {
|
||||||
|
this.rest = restTemplate;
|
||||||
|
this.repositoryUrls = repositoryUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedSet<DependencyVersion> resolveVersions(String groupId, String artifactId) {
|
||||||
|
Set<String> versions = new HashSet<String>();
|
||||||
|
for (String repositoryUrl : this.repositoryUrls) {
|
||||||
|
versions.addAll(resolveVersions(groupId, artifactId, repositoryUrl));
|
||||||
|
}
|
||||||
|
return new TreeSet<>(versions.stream().map(DependencyVersion::parse).collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> resolveVersions(String groupId, String artifactId, String repositoryUrl) {
|
||||||
|
Set<String> versions = new HashSet<String>();
|
||||||
|
String url = repositoryUrl + "/" + groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml";
|
||||||
|
try {
|
||||||
|
String metadata = this.rest.getForObject(url, String.class);
|
||||||
|
Document metadataDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
.parse(new InputSource(new StringReader(metadata)));
|
||||||
|
NodeList versionNodes = (NodeList) XPathFactory.newInstance().newXPath()
|
||||||
|
.evaluate("/metadata/versioning/versions/version", metadataDocument, XPathConstants.NODESET);
|
||||||
|
for (int i = 0; i < versionNodes.getLength(); i++) {
|
||||||
|
versions.add(versionNodes.item(i).getTextContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpClientErrorException ex) {
|
||||||
|
if (ex.getStatusCode() != HttpStatus.NOT_FOUND) {
|
||||||
|
System.err.println("Failed to download maven-metadata.xml for " + groupId + ":" + artifactId + " from "
|
||||||
|
+ url + ": " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
System.err.println("Failed to resolve versions for module " + groupId + ":" + artifactId + " in repository "
|
||||||
|
+ repositoryUrl + ": " + ex.getMessage());
|
||||||
|
}
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.Library;
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An upgrade to change a {@link Library} to use a new version}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class Upgrade {
|
||||||
|
|
||||||
|
private final Library library;
|
||||||
|
|
||||||
|
private final DependencyVersion version;
|
||||||
|
|
||||||
|
Upgrade(Library library, DependencyVersion version) {
|
||||||
|
this.library = library;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
Library getLibrary() {
|
||||||
|
return this.library;
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyVersion getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.InvalidUserDataException;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
import org.gradle.api.tasks.options.Option;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.BomExtension;
|
||||||
|
import org.springframework.boot.build.bom.bomr.github.GitHub;
|
||||||
|
import org.springframework.boot.build.bom.bomr.github.GitHubRepository;
|
||||||
|
import org.springframework.boot.build.bom.bomr.github.Milestone;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} to upgrade the libraries managed by a bom.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class UpgradeBom extends DefaultTask {
|
||||||
|
|
||||||
|
private final BomExtension bom;
|
||||||
|
|
||||||
|
private String milestone;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public UpgradeBom(BomExtension bom) {
|
||||||
|
this.bom = bom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(option = "milestone", description = "Milestone to which dependency upgrade issues should be assigned")
|
||||||
|
public void setMilestone(String milestone) {
|
||||||
|
this.milestone = milestone;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void upgradeDependencies() {
|
||||||
|
GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(),
|
||||||
|
this.bom.getUpgrade().getGitHub().getRepository());
|
||||||
|
List<String> availableLabels = repository.getLabels();
|
||||||
|
List<String> issueLabels = this.bom.getUpgrade().getGitHub().getIssueLabels();
|
||||||
|
if (!availableLabels.containsAll(issueLabels)) {
|
||||||
|
List<String> unknownLabels = new ArrayList<>(issueLabels);
|
||||||
|
unknownLabels.removeAll(availableLabels);
|
||||||
|
throw new InvalidUserDataException(
|
||||||
|
"Unknown label(s): " + StringUtils.collectionToCommaDelimitedString(unknownLabels));
|
||||||
|
}
|
||||||
|
Milestone milestone = determineMilestone(repository);
|
||||||
|
List<Upgrade> upgrades = new InteractiveUpgradeResolver(
|
||||||
|
new MavenMetadataVersionResolver(Arrays.asList("https://repo1.maven.org/maven2/")),
|
||||||
|
this.bom.getUpgrade().getPolicy(), getServices().get(UserInputHandler.class))
|
||||||
|
.resolveUpgrades(this.bom.getLibraries());
|
||||||
|
for (Upgrade upgrade : upgrades) {
|
||||||
|
String title = "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion();
|
||||||
|
System.out.println(title);
|
||||||
|
try {
|
||||||
|
Path buildFile = getProject().getBuildFile().toPath();
|
||||||
|
applyChanges(upgrade, buildFile);
|
||||||
|
int issueNumber = repository.openIssue(title, issueLabels, milestone);
|
||||||
|
if (new ProcessBuilder().command("git", "add", buildFile.toFile().getAbsolutePath()).start()
|
||||||
|
.waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("git add failed");
|
||||||
|
}
|
||||||
|
if (new ProcessBuilder().command("git", "commit", "-m", title + "\n\nCloses gh-" + issueNumber).start()
|
||||||
|
.waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("git commit failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new TaskExecutionException(this, ex);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GitHub createGitHub() {
|
||||||
|
Properties bomrProperties = new Properties();
|
||||||
|
try (Reader reader = new FileReader(new File(System.getProperty("user.home"), ".bomr.properties"))) {
|
||||||
|
bomrProperties.load(reader);
|
||||||
|
String username = bomrProperties.getProperty("bomr.github.username");
|
||||||
|
String password = bomrProperties.getProperty("bomr.github.password");
|
||||||
|
return GitHub.withCredentials(username, password);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new InvalidUserDataException("Failed to load .bomr.properties from user home", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyChanges(Upgrade upgrade, Path buildFile) throws IOException {
|
||||||
|
String contents = new String(Files.readAllBytes(buildFile), StandardCharsets.UTF_8);
|
||||||
|
String modified = contents.replace(
|
||||||
|
"library('" + upgrade.getLibrary().getName() + "', '" + upgrade.getLibrary().getVersion() + "')",
|
||||||
|
"library('" + upgrade.getLibrary().getName() + "', '" + upgrade.getVersion() + "')");
|
||||||
|
Files.write(buildFile, modified.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Milestone determineMilestone(GitHubRepository repository) {
|
||||||
|
if (this.milestone == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Milestone> milestones = repository.getMilestones();
|
||||||
|
Optional<Milestone> matchingMilestone = milestones.stream()
|
||||||
|
.filter((milestone) -> milestone.getName().equals(this.milestone)).findFirst();
|
||||||
|
if (!matchingMilestone.isPresent()) {
|
||||||
|
throw new InvalidUserDataException("Unknown milestone: " + this.milestone);
|
||||||
|
}
|
||||||
|
return matchingMilestone.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.Library;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves upgrades for the libraries in a bom.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
interface UpgradeResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the upgrades to be applied to the given {@code libraries}.
|
||||||
|
* @param libraries the libraries
|
||||||
|
* @return the upgrades
|
||||||
|
*/
|
||||||
|
List<Upgrade> resolveUpgrades(Collection<Library> libraries);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the available versions for a module.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
interface VersionResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the available versions for the module identified by the given
|
||||||
|
* {@code groupId} and {@code artifactId}.
|
||||||
|
* @param groupId module's group ID
|
||||||
|
* @param artifactId module's artifact ID
|
||||||
|
* @return the available versions
|
||||||
|
*/
|
||||||
|
SortedSet<DependencyVersion> resolveVersions(String groupId, String artifactId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal API for interacting with GitHub.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public interface GitHub {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link GitHubRepository} with the given {@code name} in the given
|
||||||
|
* {@code organization}.
|
||||||
|
* @param organization the organization
|
||||||
|
* @param name the name of the repository
|
||||||
|
* @return the repository
|
||||||
|
*/
|
||||||
|
GitHubRepository getRepository(String organization, String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code GitHub} that will authenticate with given {@code username} and
|
||||||
|
* {@code password}.
|
||||||
|
* @param username username for authentication
|
||||||
|
* @param password password for authentication
|
||||||
|
* @return the new {@code GitHub} instance
|
||||||
|
*/
|
||||||
|
static GitHub withCredentials(String username, String password) {
|
||||||
|
return new StandardGitHub(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.github;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal API for interacting with a GitHub repository.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public interface GitHubRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a new issue with the given title. The given {@code labels} will be applied to
|
||||||
|
* the issue and it will be assigned to the given {@code milestone}.
|
||||||
|
* @param title the title of the issue
|
||||||
|
* @param labels the labels to apply to the issue
|
||||||
|
* @param milestone the milestone to assign the issue to
|
||||||
|
* @return the number of the new issue
|
||||||
|
*/
|
||||||
|
int openIssue(String title, List<String> labels, Milestone milestone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the labels in the repository.
|
||||||
|
* @return the labels
|
||||||
|
*/
|
||||||
|
List<String> getLabels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the milestones in the repository.
|
||||||
|
* @return the milestones
|
||||||
|
*/
|
||||||
|
List<Milestone> getMilestones();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A milestone in a {@link GitHubRepository GitHub repository}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class Milestone {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final int number;
|
||||||
|
|
||||||
|
Milestone(String name, int number) {
|
||||||
|
this.name = name;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the milestone.
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of the milestone.
|
||||||
|
* @return the number
|
||||||
|
*/
|
||||||
|
public int getNumber() {
|
||||||
|
return this.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name + " (" + this.number + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||||
|
import org.springframework.web.util.UriTemplateHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard implementation of {@link GitHub}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class StandardGitHub implements GitHub {
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
StandardGitHub(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GitHubRepository getRepository(String organization, String name) {
|
||||||
|
RestTemplate restTemplate = new RestTemplate(
|
||||||
|
Arrays.asList(new MappingJackson2HttpMessageConverter(new ObjectMapper())));
|
||||||
|
restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
|
||||||
|
throws IOException {
|
||||||
|
request.getHeaders().add("User-Agent", StandardGitHub.this.username);
|
||||||
|
request.getHeaders().add("Authorization", "Basic " + Base64.getEncoder().encodeToString(
|
||||||
|
(StandardGitHub.this.username + ":" + StandardGitHub.this.password).getBytes()));
|
||||||
|
request.getHeaders().add("Accept", MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
return execution.execute(request, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory(
|
||||||
|
"https://api.github.com/repos/" + organization + "/" + name + "/");
|
||||||
|
restTemplate.setUriTemplateHandler(uriTemplateHandler);
|
||||||
|
return new StandardGitHubRepository(restTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.github;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard implementation of {@link GitHubRepository}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class StandardGitHubRepository implements GitHubRepository {
|
||||||
|
|
||||||
|
private final RestTemplate rest;
|
||||||
|
|
||||||
|
StandardGitHubRepository(RestTemplate restTemplate) {
|
||||||
|
this.rest = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public int openIssue(String title, List<String> labels, Milestone milestone) {
|
||||||
|
Map<String, Object> body = new HashMap<>();
|
||||||
|
body.put("title", title);
|
||||||
|
if (milestone != null) {
|
||||||
|
body.put("milestone", milestone.getNumber());
|
||||||
|
}
|
||||||
|
if (!labels.isEmpty()) {
|
||||||
|
body.put("labels", labels);
|
||||||
|
}
|
||||||
|
ResponseEntity<Map> response = this.rest.postForEntity("issues", body, Map.class);
|
||||||
|
return (Integer) response.getBody().get("number");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getLabels() {
|
||||||
|
return get("labels?per_page=100", (label) -> (String) label.get("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Milestone> getMilestones() {
|
||||||
|
return get("milestones?per_page=100",
|
||||||
|
(milestone) -> new Milestone((String) milestone.get("title"), (Integer) milestone.get("number")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
private <T> List<T> get(String name, Function<Map<String, Object>, T> mapper) {
|
||||||
|
ResponseEntity<List> response = this.rest.getForEntity(name, List.class);
|
||||||
|
List<Map<String, Object>> body = response.getBody();
|
||||||
|
return body.stream().map(mapper).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for {@link DependencyVersion} implementations.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
abstract class AbstractDependencyVersion implements DependencyVersion {
|
||||||
|
|
||||||
|
private final ComparableVersion comparableVersion;
|
||||||
|
|
||||||
|
protected AbstractDependencyVersion(ComparableVersion comparableVersion) {
|
||||||
|
this.comparableVersion = comparableVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DependencyVersion other) {
|
||||||
|
ComparableVersion otherComparable = (other instanceof AbstractDependencyVersion)
|
||||||
|
? ((AbstractDependencyVersion) other).comparableVersion : new ComparableVersion(other.toString());
|
||||||
|
return this.comparableVersion.compareTo(otherComparable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AbstractDependencyVersion other = (AbstractDependencyVersion) obj;
|
||||||
|
if (!this.comparableVersion.equals(other.comparableVersion)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.comparableVersion.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.comparableVersion.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DependencyVersion} backed by an {@link ArtifactVersion}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class ArtifactVersionDependencyVersion extends AbstractDependencyVersion {
|
||||||
|
|
||||||
|
private final ArtifactVersion artifactVersion;
|
||||||
|
|
||||||
|
protected ArtifactVersionDependencyVersion(ArtifactVersion artifactVersion) {
|
||||||
|
super(new ComparableVersion(artifactVersion.toString()));
|
||||||
|
this.artifactVersion = artifactVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ArtifactVersionDependencyVersion(ArtifactVersion artifactVersion, ComparableVersion comparableVersion) {
|
||||||
|
super(comparableVersion);
|
||||||
|
this.artifactVersion = artifactVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNewerThan(DependencyVersion other) {
|
||||||
|
if (other instanceof ReleaseTrainDependencyVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return compareTo(other) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMajorAndNewerThan(DependencyVersion other) {
|
||||||
|
if (other instanceof ReleaseTrainDependencyVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return extractArtifactVersionDependencyVersion(other).map(this::isSameMajorAndNewerThan).orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSameMajorAndNewerThan(ArtifactVersionDependencyVersion other) {
|
||||||
|
return this.artifactVersion.getMajorVersion() == other.artifactVersion.getMajorVersion() && isNewerThan(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMinorAndNewerThan(DependencyVersion other) {
|
||||||
|
if (other instanceof ReleaseTrainDependencyVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return extractArtifactVersionDependencyVersion(other).map(this::isSameMinorAndNewerThan).orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSameMinorAndNewerThan(ArtifactVersionDependencyVersion other) {
|
||||||
|
return this.artifactVersion.getMajorVersion() == other.artifactVersion.getMajorVersion()
|
||||||
|
&& this.artifactVersion.getMinorVersion() == other.artifactVersion.getMinorVersion()
|
||||||
|
&& isNewerThan(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.artifactVersion.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ArtifactVersionDependencyVersion> extractArtifactVersionDependencyVersion(
|
||||||
|
DependencyVersion other) {
|
||||||
|
ArtifactVersionDependencyVersion artifactVersion = null;
|
||||||
|
if (other instanceof ArtifactVersionDependencyVersion) {
|
||||||
|
artifactVersion = (ArtifactVersionDependencyVersion) other;
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(artifactVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ArtifactVersionDependencyVersion parse(String version) {
|
||||||
|
ArtifactVersion artifactVersion = new DefaultArtifactVersion(version);
|
||||||
|
if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(version)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ArtifactVersionDependencyVersion(artifactVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DependencyVersion} where the patch and qualifier are not separated.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class CombinedPatchAndQualifierDependencyVersion extends ArtifactVersionDependencyVersion {
|
||||||
|
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("([0-9]+\\.[0-9]+\\.[0-9]+)([A-Za-z][A-Za-z0-9]+)");
|
||||||
|
|
||||||
|
private final String original;
|
||||||
|
|
||||||
|
private CombinedPatchAndQualifierDependencyVersion(ArtifactVersion artifactVersion, String original) {
|
||||||
|
super(artifactVersion);
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.original;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CombinedPatchAndQualifierDependencyVersion parse(String version) {
|
||||||
|
Matcher matcher = PATTERN.matcher(version);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ArtifactVersion artifactVersion = new DefaultArtifactVersion(matcher.group(1) + "." + matcher.group(2));
|
||||||
|
if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(version)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new CombinedPatchAndQualifierDependencyVersion(artifactVersion, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of a dependency.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public interface DependencyVersion extends Comparable<DependencyVersion> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this version is newer than the given {@code other} version.
|
||||||
|
* @param other version to test
|
||||||
|
* @return {@code true} if this version is newer, otherwise {@code false}
|
||||||
|
*/
|
||||||
|
boolean isNewerThan(DependencyVersion other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this version has the same major versions as the {@code other}
|
||||||
|
* version while also being newer.
|
||||||
|
* @param other version to test
|
||||||
|
* @return {@code true} if this version has the same major and is newer, otherwise
|
||||||
|
* {@code false}
|
||||||
|
*/
|
||||||
|
boolean isSameMajorAndNewerThan(DependencyVersion other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this version has the same major and minor versions as the
|
||||||
|
* {@code other} version while also being newer.
|
||||||
|
* @param other version to test
|
||||||
|
* @return {@code true} if this version has the same major and minor and is newer,
|
||||||
|
* otherwise {@code false}
|
||||||
|
*/
|
||||||
|
boolean isSameMinorAndNewerThan(DependencyVersion other);
|
||||||
|
|
||||||
|
static DependencyVersion parse(String version) {
|
||||||
|
List<Function<String, DependencyVersion>> parsers = Arrays.asList(ArtifactVersionDependencyVersion::parse,
|
||||||
|
ReleaseTrainDependencyVersion::parse, NumericQualifierDependencyVersion::parse,
|
||||||
|
CombinedPatchAndQualifierDependencyVersion::parse, LeadingZeroesDependencyVersion::parse,
|
||||||
|
UnstructuredDependencyVersion::parse);
|
||||||
|
for (Function<String, DependencyVersion> parser : parsers) {
|
||||||
|
DependencyVersion result = parser.apply(version);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Version '" + version + "' could not be parsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DependencyVersion} that tolerates leading zeroes.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class LeadingZeroesDependencyVersion extends ArtifactVersionDependencyVersion {
|
||||||
|
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("0*([0-9]+)\\.0*([0-9]+)\\.0*([0-9]+)");
|
||||||
|
|
||||||
|
private final String original;
|
||||||
|
|
||||||
|
private LeadingZeroesDependencyVersion(ArtifactVersion artifactVersion, String original) {
|
||||||
|
super(artifactVersion);
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.original;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LeadingZeroesDependencyVersion parse(String input) {
|
||||||
|
Matcher matcher = PATTERN.matcher(input);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ArtifactVersion artifactVersion = new DefaultArtifactVersion(
|
||||||
|
matcher.group(1) + matcher.group(2) + matcher.group(3));
|
||||||
|
return new LeadingZeroesDependencyVersion(artifactVersion, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
|
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fallback {@link DependencyVersion} to handle versions with four components that
|
||||||
|
* cannot be handled by {@link ArtifactVersion} because the fourth component is numeric.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class NumericQualifierDependencyVersion extends ArtifactVersionDependencyVersion {
|
||||||
|
|
||||||
|
private final String original;
|
||||||
|
|
||||||
|
private NumericQualifierDependencyVersion(ArtifactVersion artifactVersion, String original) {
|
||||||
|
super(artifactVersion, new ComparableVersion(original));
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.original;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NumericQualifierDependencyVersion parse(String input) {
|
||||||
|
String[] components = input.split("\\.");
|
||||||
|
if (components.length == 4) {
|
||||||
|
ArtifactVersion artifactVersion = new DefaultArtifactVersion(
|
||||||
|
components[0] + "." + components[1] + "." + components[2]);
|
||||||
|
if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(input)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new NumericQualifierDependencyVersion(artifactVersion, input);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DependencyVersion} for a release train such as Spring Data.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class ReleaseTrainDependencyVersion implements DependencyVersion {
|
||||||
|
|
||||||
|
private static final Pattern VERSION_PATTERN = Pattern.compile("([A-Z][a-z]+)-([A-Z]+)([0-9]*)");
|
||||||
|
|
||||||
|
private final String releaseTrain;
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
private final int version;
|
||||||
|
|
||||||
|
private final String original;
|
||||||
|
|
||||||
|
private ReleaseTrainDependencyVersion(String releaseTrain, String type, int version, String original) {
|
||||||
|
this.releaseTrain = releaseTrain;
|
||||||
|
this.type = type;
|
||||||
|
this.version = version;
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DependencyVersion other) {
|
||||||
|
if (!(other instanceof ReleaseTrainDependencyVersion)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
|
||||||
|
int comparison = this.releaseTrain.compareTo(otherReleaseTrain.releaseTrain);
|
||||||
|
if (comparison != 0) {
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
comparison = this.type.compareTo(otherReleaseTrain.type);
|
||||||
|
if (comparison != 0) {
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
return Integer.compare(this.version, otherReleaseTrain.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNewerThan(DependencyVersion other) {
|
||||||
|
if (!(other instanceof ReleaseTrainDependencyVersion)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
|
||||||
|
return otherReleaseTrain.compareTo(this) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMajorAndNewerThan(DependencyVersion other) {
|
||||||
|
return isNewerThan(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMinorAndNewerThan(DependencyVersion other) {
|
||||||
|
if (!(other instanceof ReleaseTrainDependencyVersion)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
|
||||||
|
return otherReleaseTrain.releaseTrain.equals(this.releaseTrain) && isNewerThan(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ReleaseTrainDependencyVersion other = (ReleaseTrainDependencyVersion) obj;
|
||||||
|
if (!this.original.equals(other.original)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.original.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.original;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ReleaseTrainDependencyVersion parse(String input) {
|
||||||
|
Matcher matcher = VERSION_PATTERN.matcher(input);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ReleaseTrainDependencyVersion(matcher.group(1), matcher.group(2),
|
||||||
|
(StringUtils.hasLength(matcher.group(3))) ? Integer.parseInt(matcher.group(3)) : 0, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DependencyVersion} with no structure such that version comparisons are not
|
||||||
|
* possible.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class UnstructuredDependencyVersion extends AbstractDependencyVersion implements DependencyVersion {
|
||||||
|
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
private UnstructuredDependencyVersion(String version) {
|
||||||
|
super(new ComparableVersion(version));
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNewerThan(DependencyVersion other) {
|
||||||
|
return this.compareTo(other) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMajorAndNewerThan(DependencyVersion other) {
|
||||||
|
return this.compareTo(other) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameMinorAndNewerThan(DependencyVersion other) {
|
||||||
|
return this.compareTo(other) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UnstructuredDependencyVersion parse(String version) {
|
||||||
|
return new UnstructuredDependencyVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.classpath;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} for checking the classpath for conflicting classes and resources.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class CheckClasspathForConflicts extends DefaultTask {
|
||||||
|
|
||||||
|
private final List<Predicate<String>> ignores = new ArrayList<>();
|
||||||
|
|
||||||
|
private FileCollection classpath;
|
||||||
|
|
||||||
|
public void setClasspath(FileCollection classpath) {
|
||||||
|
this.classpath = classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
public FileCollection getClasspath() {
|
||||||
|
return this.classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void checkForConflicts() throws IOException {
|
||||||
|
ClasspathContents classpathContents = new ClasspathContents();
|
||||||
|
for (File file : this.classpath) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Path root = file.toPath();
|
||||||
|
Files.walk(root).filter((path) -> Files.isRegularFile(path))
|
||||||
|
.forEach((entry) -> classpathContents.add(root.relativize(entry).toString(), root.toString()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try (JarFile jar = new JarFile(file)) {
|
||||||
|
for (JarEntry entry : Collections.list(jar.entries())) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
classpathContents.add(entry.getName(), file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<String, List<String>> conflicts = classpathContents.getConflicts(this.ignores);
|
||||||
|
if (!conflicts.isEmpty()) {
|
||||||
|
StringBuilder message = new StringBuilder(String.format("Found classpath conflicts:%n"));
|
||||||
|
conflicts.forEach((entry, locations) -> {
|
||||||
|
message.append(String.format(" %s%n", entry));
|
||||||
|
locations.forEach((location) -> message.append(String.format(" %s%n", location)));
|
||||||
|
});
|
||||||
|
throw new GradleException(message.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ignore(Predicate<String> predicate) {
|
||||||
|
this.ignores.add(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClasspathContents {
|
||||||
|
|
||||||
|
private static final Set<String> IGNORED_NAMES = new HashSet<>(Arrays.asList("about.html", "changelog.txt",
|
||||||
|
"LICENSE", "license.txt", "module-info.class", "notice.txt", "readme.txt"));
|
||||||
|
|
||||||
|
private final Map<String, List<String>> classpathContents = new HashMap<>();
|
||||||
|
|
||||||
|
private void add(String name, String source) {
|
||||||
|
this.classpathContents.computeIfAbsent(name, (key) -> new ArrayList<>()).add(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<String>> getConflicts(List<Predicate<String>> ignores) {
|
||||||
|
return this.classpathContents.entrySet().stream().filter((entry) -> entry.getValue().size() > 1)
|
||||||
|
.filter((entry) -> canConflict(entry.getKey(), ignores))
|
||||||
|
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v1, TreeMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canConflict(String name, List<Predicate<String>> ignores) {
|
||||||
|
if (name.startsWith("META-INF/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String ignoredName : IGNORED_NAMES) {
|
||||||
|
if (name.equals(ignoredName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Predicate<String> ignore : ignores) {
|
||||||
|
if (ignore.test(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.classpath;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} for checking the classpath for prohibited dependencies.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class CheckClasspathForProhibitedDependencies extends DefaultTask {
|
||||||
|
|
||||||
|
private Configuration classpath;
|
||||||
|
|
||||||
|
public CheckClasspathForProhibitedDependencies() {
|
||||||
|
getOutputs().upToDateWhen((task) -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClasspath(Configuration classpath) {
|
||||||
|
this.classpath = classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
public FileCollection getClasspath() {
|
||||||
|
return this.classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void checkForProhibitedDependencies() throws IOException {
|
||||||
|
TreeSet<String> prohibited = this.classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
|
||||||
|
.map((artifact) -> artifact.getModuleVersion().getId()).filter(this::prohibited)
|
||||||
|
.map((id) -> id.getGroup() + ":" + id.getName()).collect(Collectors.toCollection(TreeSet::new));
|
||||||
|
if (!prohibited.isEmpty()) {
|
||||||
|
StringBuilder message = new StringBuilder(String.format("Found prohibited dependencies:%n"));
|
||||||
|
for (String dependency : prohibited) {
|
||||||
|
message.append(String.format(" %s%n", dependency));
|
||||||
|
}
|
||||||
|
throw new GradleException(message.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean prohibited(ModuleVersionIdentifier id) {
|
||||||
|
String group = id.getGroup();
|
||||||
|
if (group.equals("javax.batch")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (group.startsWith("javax")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (group.equals("commons-logging")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.cli;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.file.RegularFile;
|
||||||
|
import org.gradle.api.provider.Provider;
|
||||||
|
import org.gradle.api.tasks.InputFile;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for generating a package manager definition file such as a Scoop manifest or
|
||||||
|
* a Homebrew formula.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public abstract class AbstractPackageManagerDefinitionTask extends DefaultTask {
|
||||||
|
|
||||||
|
private Provider<RegularFile> archive;
|
||||||
|
|
||||||
|
private File template;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
public AbstractPackageManagerDefinitionTask() {
|
||||||
|
getInputs().property("version", getProject().provider(getProject()::getVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public RegularFile getArchive() {
|
||||||
|
return this.archive.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArchive(Provider<RegularFile> archive) {
|
||||||
|
this.archive = archive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public File getTemplate() {
|
||||||
|
return this.template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplate(File template) {
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createDescriptor(Map<String, Object> additionalProperties) {
|
||||||
|
getProject().copy((copy) -> {
|
||||||
|
copy.from(this.template);
|
||||||
|
copy.into(this.outputDir);
|
||||||
|
Map<String, Object> properties = new HashMap<>(additionalProperties);
|
||||||
|
properties.put("hash", sha256(this.archive.get().getAsFile()));
|
||||||
|
properties.put("repo", determineArtifactoryRepo(getProject()));
|
||||||
|
properties.put("project", getProject());
|
||||||
|
copy.expand(properties);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sha256(File file) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
return new DigestUtils(digest).digestAsHex(file);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new TaskExecutionException(this, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineArtifactoryRepo(Project project) {
|
||||||
|
String version = project.getVersion().toString();
|
||||||
|
String type = version.substring(version.lastIndexOf('.'));
|
||||||
|
if (type.equals("RELEASE")) {
|
||||||
|
return "release";
|
||||||
|
}
|
||||||
|
if (type.startsWith("M") || type.startsWith("RC")) {
|
||||||
|
return "milestone";
|
||||||
|
}
|
||||||
|
return "snapshot";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.cli;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@Task} for creating a Homebrew formula manifest.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class HomebrewFormula extends AbstractPackageManagerDefinitionTask {
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void createFormula() {
|
||||||
|
createDescriptor(Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.cli;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@Task} for creating a Scoop manifest.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ScoopManifest extends AbstractPackageManagerDefinitionTask {
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void createManifest() {
|
||||||
|
String version = getProject().getVersion().toString();
|
||||||
|
createDescriptor(Collections.singletonMap("scoopVersion", version.substring(0, version.lastIndexOf('.'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.constraints;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.api.provider.SetProperty;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.constraints.ExtractVersionConstraints.ConstrainedVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task for documenting a platform's constrained versions.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentConstrainedVersions extends DefaultTask {
|
||||||
|
|
||||||
|
private final SetProperty<ConstrainedVersion> constrainedVersions;
|
||||||
|
|
||||||
|
private File outputFile;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DocumentConstrainedVersions(ObjectFactory objectFactory) {
|
||||||
|
this.constrainedVersions = objectFactory.setProperty(ConstrainedVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public SetProperty<ConstrainedVersion> getConstrainedVersions() {
|
||||||
|
return this.constrainedVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getOutputFile() {
|
||||||
|
return this.outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFile(File outputFile) {
|
||||||
|
this.outputFile = outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void documentConstrainedVersions() throws IOException {
|
||||||
|
this.outputFile.getParentFile().mkdirs();
|
||||||
|
try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile))) {
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println("| Group ID | Artifact ID | Version");
|
||||||
|
for (ConstrainedVersion constrainedVersion : this.constrainedVersions.get()) {
|
||||||
|
writer.println();
|
||||||
|
writer.printf("| `%s`%n", constrainedVersion.getGroup());
|
||||||
|
writer.printf("| `%s`%n", constrainedVersion.getArtifact());
|
||||||
|
writer.printf("| `%s`%n", constrainedVersion.getVersion());
|
||||||
|
}
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.constraints;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.ComponentMetadataDetails;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.DependencyConstraint;
|
||||||
|
import org.gradle.api.artifacts.DependencyConstraintMetadata;
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.Internal;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.platform.base.Platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} to extract constraints from a {@link Platform}. The platform's own
|
||||||
|
* constraints and those in any boms upon which it depends are extracted.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ExtractVersionConstraints extends AbstractTask {
|
||||||
|
|
||||||
|
private final Configuration configuration;
|
||||||
|
|
||||||
|
private final Map<String, String> versionConstraints = new TreeMap<>();
|
||||||
|
|
||||||
|
private final Set<ConstrainedVersion> constrainedVersions = new TreeSet<>();
|
||||||
|
|
||||||
|
private final List<String> projectPaths = new ArrayList<String>();
|
||||||
|
|
||||||
|
public ExtractVersionConstraints() {
|
||||||
|
DependencyHandler dependencies = getProject().getDependencies();
|
||||||
|
this.configuration = getProject().getConfigurations().create(getName());
|
||||||
|
dependencies.getComponents().all(this::processMetadataDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enforcedPlatform(String projectPath) {
|
||||||
|
this.configuration.getDependencies().add(getProject().getDependencies().enforcedPlatform(
|
||||||
|
getProject().getDependencies().project(Collections.singletonMap("path", projectPath))));
|
||||||
|
this.projectPaths.add(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public Map<String, String> getVersionConstraints() {
|
||||||
|
return Collections.unmodifiableMap(this.versionConstraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public Set<ConstrainedVersion> getConstrainedVersions() {
|
||||||
|
return this.constrainedVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void extractVersionConstraints() {
|
||||||
|
this.configuration.resolve();
|
||||||
|
for (String projectPath : this.projectPaths) {
|
||||||
|
for (DependencyConstraint constraint : getProject().project(projectPath).getConfigurations()
|
||||||
|
.getByName("apiElements").getAllDependencyConstraints()) {
|
||||||
|
this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(),
|
||||||
|
constraint.getVersionConstraint().toString());
|
||||||
|
this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(),
|
||||||
|
constraint.getVersionConstraint().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMetadataDetails(ComponentMetadataDetails details) {
|
||||||
|
details.allVariants((variantMetadata) -> variantMetadata.withDependencyConstraints((dependencyConstraints) -> {
|
||||||
|
for (DependencyConstraintMetadata constraint : dependencyConstraints) {
|
||||||
|
this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(),
|
||||||
|
constraint.getVersionConstraint().toString());
|
||||||
|
this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(),
|
||||||
|
constraint.getVersionConstraint().toString()));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ConstrainedVersion implements Comparable<ConstrainedVersion>, Serializable {
|
||||||
|
|
||||||
|
private final String group;
|
||||||
|
|
||||||
|
private final String artifact;
|
||||||
|
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
private ConstrainedVersion(String group, String artifact, String version) {
|
||||||
|
this.group = group;
|
||||||
|
this.artifact = artifact;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroup() {
|
||||||
|
return this.group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArtifact() {
|
||||||
|
return this.artifact;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ConstrainedVersion other) {
|
||||||
|
int groupComparison = this.group.compareTo(other.group);
|
||||||
|
if (groupComparison != 0) {
|
||||||
|
return groupComparison;
|
||||||
|
}
|
||||||
|
return this.artifact.compareTo(other.artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple builder to help construct Asciidoc markup.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class AsciidocBuilder {
|
||||||
|
|
||||||
|
private final StringBuilder content;
|
||||||
|
|
||||||
|
AsciidocBuilder() {
|
||||||
|
this.content = new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
AsciidocBuilder appendKey(Object... items) {
|
||||||
|
for (Object item : items) {
|
||||||
|
appendln("`+", item, "+` +");
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsciidocBuilder newLine() {
|
||||||
|
return append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
AsciidocBuilder appendln(Object... items) {
|
||||||
|
return append(items).newLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
AsciidocBuilder append(Object... items) {
|
||||||
|
for (Object item : items) {
|
||||||
|
this.content.append(item);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table entry regrouping a list of configuration properties sharing the same description.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
class CompoundConfigurationTableEntry extends ConfigurationTableEntry {
|
||||||
|
|
||||||
|
private Set<String> configurationKeys;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
CompoundConfigurationTableEntry(String key, String description) {
|
||||||
|
this.key = key;
|
||||||
|
this.description = description;
|
||||||
|
this.configurationKeys = new TreeSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addConfigurationKeys(ConfigurationProperty... properties) {
|
||||||
|
Stream.of(properties).map(ConfigurationProperty::getName).forEach(this.configurationKeys::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(AsciidocBuilder builder) {
|
||||||
|
builder.append("|");
|
||||||
|
this.configurationKeys.forEach(builder::appendKey);
|
||||||
|
builder.newLine().appendln("|").appendln("|+++", this.description, "+++");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write Asciidoc documents with configuration properties listings.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class ConfigurationMetadataDocumentWriter {
|
||||||
|
|
||||||
|
public void writeDocument(Path outputDirectory, DocumentOptions options, FileCollection metadataFiles)
|
||||||
|
throws IOException {
|
||||||
|
assertValidOutputDirectory(outputDirectory);
|
||||||
|
if (!Files.exists(outputDirectory)) {
|
||||||
|
Files.createDirectory(outputDirectory);
|
||||||
|
}
|
||||||
|
List<ConfigurationTable> tables = createConfigTables(ConfigurationProperties.fromFiles(metadataFiles), options);
|
||||||
|
for (ConfigurationTable table : tables) {
|
||||||
|
writeConfigurationTable(table, outputDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertValidOutputDirectory(Path outputDirPath) {
|
||||||
|
if (outputDirPath == null) {
|
||||||
|
throw new IllegalArgumentException("output path should not be null");
|
||||||
|
}
|
||||||
|
if (Files.exists(outputDirPath) && !Files.isDirectory(outputDirPath)) {
|
||||||
|
throw new IllegalArgumentException("output path already exists and is not a directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ConfigurationTable> createConfigTables(Map<String, ConfigurationProperty> metadataProperties,
|
||||||
|
DocumentOptions options) {
|
||||||
|
List<ConfigurationTable> tables = new ArrayList<>();
|
||||||
|
List<String> unmappedKeys = metadataProperties.values().stream().filter((property) -> !property.isDeprecated())
|
||||||
|
.map(ConfigurationProperty::getName).collect(Collectors.toList());
|
||||||
|
Map<String, CompoundConfigurationTableEntry> overrides = getOverrides(metadataProperties, unmappedKeys,
|
||||||
|
options);
|
||||||
|
options.getMetadataSections().forEach((id, keyPrefixes) -> tables
|
||||||
|
.add(createConfigTable(metadataProperties, unmappedKeys, overrides, id, keyPrefixes)));
|
||||||
|
if (!unmappedKeys.isEmpty()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"The following keys were not written to the documentation: " + String.join(", ", unmappedKeys));
|
||||||
|
}
|
||||||
|
if (!overrides.isEmpty()) {
|
||||||
|
throw new IllegalStateException("The following keys were not written to the documentation: "
|
||||||
|
+ String.join(", ", overrides.keySet()));
|
||||||
|
}
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, CompoundConfigurationTableEntry> getOverrides(
|
||||||
|
Map<String, ConfigurationProperty> metadataProperties, List<String> unmappedKeys, DocumentOptions options) {
|
||||||
|
Map<String, CompoundConfigurationTableEntry> overrides = new HashMap<>();
|
||||||
|
options.getOverrides().forEach((keyPrefix, description) -> {
|
||||||
|
CompoundConfigurationTableEntry entry = new CompoundConfigurationTableEntry(keyPrefix, description);
|
||||||
|
List<String> matchingKeys = unmappedKeys.stream().filter((key) -> key.startsWith(keyPrefix))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (String matchingKey : matchingKeys) {
|
||||||
|
entry.addConfigurationKeys(metadataProperties.get(matchingKey));
|
||||||
|
}
|
||||||
|
overrides.put(keyPrefix, entry);
|
||||||
|
unmappedKeys.removeAll(matchingKeys);
|
||||||
|
});
|
||||||
|
return overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationTable createConfigTable(Map<String, ConfigurationProperty> metadataProperties,
|
||||||
|
List<String> unmappedKeys, Map<String, CompoundConfigurationTableEntry> overrides, String id,
|
||||||
|
List<String> keyPrefixes) {
|
||||||
|
ConfigurationTable table = new ConfigurationTable(id);
|
||||||
|
for (String keyPrefix : keyPrefixes) {
|
||||||
|
List<String> matchingOverrides = overrides.keySet().stream()
|
||||||
|
.filter((overrideKey) -> overrideKey.startsWith(keyPrefix)).collect(Collectors.toList());
|
||||||
|
matchingOverrides.forEach((match) -> table.addEntry(overrides.remove(match)));
|
||||||
|
}
|
||||||
|
List<String> matchingKeys = unmappedKeys.stream()
|
||||||
|
.filter((key) -> keyPrefixes.stream().anyMatch(key::startsWith)).collect(Collectors.toList());
|
||||||
|
for (String matchingKey : matchingKeys) {
|
||||||
|
ConfigurationProperty property = metadataProperties.get(matchingKey);
|
||||||
|
table.addEntry(new SingleConfigurationTableEntry(property));
|
||||||
|
}
|
||||||
|
unmappedKeys.removeAll(matchingKeys);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeConfigurationTable(ConfigurationTable table, Path outputDirectory) throws IOException {
|
||||||
|
Path outputFilePath = outputDirectory.resolve(table.getId() + ".adoc");
|
||||||
|
Files.deleteIfExists(outputFilePath);
|
||||||
|
Files.createFile(outputFilePath);
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(outputFilePath)) {
|
||||||
|
outputStream.write(table.toAsciidocTable().getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration properties read from one or more
|
||||||
|
* {@code META-INF/spring-configuration-metadata.json} files.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
final class ConfigurationProperties {
|
||||||
|
|
||||||
|
private static final Type MAP_TYPE = new MapTypeToken().getType();
|
||||||
|
|
||||||
|
private ConfigurationProperties() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static Map<String, ConfigurationProperty> fromFiles(FileCollection files) {
|
||||||
|
List<ConfigurationProperty> configurationProperties = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Gson gson = new GsonBuilder().create();
|
||||||
|
for (File file : files) {
|
||||||
|
try (Reader reader = new FileReader(file)) {
|
||||||
|
Map<String, Object> json = gson.fromJson(reader, MAP_TYPE);
|
||||||
|
List<Map<String, Object>> properties = (List<Map<String, Object>>) json.get("properties");
|
||||||
|
for (Map<String, Object> property : properties) {
|
||||||
|
String name = (String) property.get("name");
|
||||||
|
String type = (String) property.get("type");
|
||||||
|
Object defaultValue = property.get("defaultValue");
|
||||||
|
String description = (String) property.get("description");
|
||||||
|
boolean deprecated = property.containsKey("deprecated");
|
||||||
|
configurationProperties
|
||||||
|
.add(new ConfigurationProperty(name, type, defaultValue, description, deprecated));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurationProperties.stream()
|
||||||
|
.collect(Collectors.toMap(ConfigurationProperty::getName, Function.identity()));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException("Failed to load configuration metadata", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MapTypeToken extends TypeToken<Map<String, Object>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Plugin} for projects that define {@code @ConfigurationProperties}. When applied,
|
||||||
|
* the plugin reacts to the presence of the {@link JavaPlugin} by:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Adding a dependency on the configuration properties annotation processor.
|
||||||
|
* <li>Configure the additional metadata locations annotation processor compiler argument
|
||||||
|
* <li>Defining an artifact for the resulting configuration property metadata so that it
|
||||||
|
* can be consumed by downstream projects.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@link Configuration} that holds the configuration property metadata
|
||||||
|
* artifact.
|
||||||
|
*/
|
||||||
|
public static final String CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME = "configurationPropertiesMetadata";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||||
|
addConfigurationProcessorDependency(project);
|
||||||
|
configureAdditionalMetadataLocationsCompilerArgument(project);
|
||||||
|
addMetadataArtifact(project);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addConfigurationProcessorDependency(Project project) {
|
||||||
|
Configuration annotationProcessors = project.getConfigurations()
|
||||||
|
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||||
|
annotationProcessors.getDependencies().add(project.getDependencies().project(Collections.singletonMap("path",
|
||||||
|
":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMetadataArtifact(Project project) {
|
||||||
|
SourceSet mainSourceSet = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
|
||||||
|
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
project.getConfigurations().maybeCreate(CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME);
|
||||||
|
project.afterEvaluate((evaluatedProject) -> evaluatedProject.getArtifacts().add(
|
||||||
|
CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME,
|
||||||
|
evaluatedProject.provider((Callable<File>) () -> new File(mainSourceSet.getJava().getOutputDir(),
|
||||||
|
"META-INF/spring-configuration-metadata.json")),
|
||||||
|
(artifact) -> artifact
|
||||||
|
.builtBy(evaluatedProject.getTasks().getByName(mainSourceSet.getClassesTaskName()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureAdditionalMetadataLocationsCompilerArgument(Project project) {
|
||||||
|
JavaCompile compileJava = project.getTasks().withType(JavaCompile.class)
|
||||||
|
.getByName(JavaPlugin.COMPILE_JAVA_TASK_NAME);
|
||||||
|
SourceSet mainSourceSet = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
|
||||||
|
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
compileJava.getOptions().getCompilerArgs()
|
||||||
|
.add("-Aorg.springframework.boot.configurationprocessor.additionalMetadataLocations=" + StringUtils
|
||||||
|
.collectionToCommaDelimitedString(mainSourceSet.getResources().getSourceDirectories().getFiles()
|
||||||
|
.stream().map(project.getRootProject()::relativePath).collect(Collectors.toSet())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.context.properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration property.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ConfigurationProperty {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
private final Object defaultValue;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final boolean deprecated;
|
||||||
|
|
||||||
|
ConfigurationProperty(String name, String type) {
|
||||||
|
this(name, type, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationProperty(String name, String type, Object defaultValue, String description, boolean deprecated) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.description = description;
|
||||||
|
this.deprecated = deprecated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getDefaultValue() {
|
||||||
|
return this.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeprecated() {
|
||||||
|
return this.deprecated;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asciidoctor table listing configuration properties sharing to a common theme.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
class ConfigurationTable {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private final Set<ConfigurationTableEntry> entries;
|
||||||
|
|
||||||
|
ConfigurationTable(String id) {
|
||||||
|
this.id = id;
|
||||||
|
this.entries = new TreeSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEntry(ConfigurationTableEntry... entries) {
|
||||||
|
this.entries.addAll(Arrays.asList(entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
String toAsciidocTable() {
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
builder.appendln("[cols=\"1,1,2\", options=\"header\"]");
|
||||||
|
builder.appendln("|===");
|
||||||
|
builder.appendln("|Key|Default Value|Description");
|
||||||
|
builder.appendln();
|
||||||
|
this.entries.forEach((entry) -> {
|
||||||
|
entry.write(builder);
|
||||||
|
builder.appendln();
|
||||||
|
});
|
||||||
|
return builder.appendln("|===").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class for entries in {@link ConfigurationTable}.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
abstract class ConfigurationTableEntry implements Comparable<ConfigurationTableEntry> {
|
||||||
|
|
||||||
|
protected String key;
|
||||||
|
|
||||||
|
String getKey() {
|
||||||
|
return this.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void write(AsciidocBuilder builder);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConfigurationTableEntry other = (ConfigurationTableEntry) obj;
|
||||||
|
return this.key.equals(other.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.key.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ConfigurationTableEntry other) {
|
||||||
|
return this.key.compareTo(other.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.context.properties.DocumentOptions.Builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} used to document auto-configuration classes.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentConfigurationProperties extends AbstractTask {
|
||||||
|
|
||||||
|
private FileCollection configurationPropertyMetadata;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getConfigurationPropertyMetadata() {
|
||||||
|
return this.configurationPropertyMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigurationPropertyMetadata(FileCollection configurationPropertyMetadata) {
|
||||||
|
this.configurationPropertyMetadata = configurationPropertyMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentConfigurationProperties() throws IOException {
|
||||||
|
Builder builder = DocumentOptions.builder();
|
||||||
|
builder.addSection("core")
|
||||||
|
.withKeyPrefixes("debug", "trace", "logging", "spring.aop", "spring.application",
|
||||||
|
"spring.autoconfigure", "spring.banner", "spring.beaninfo", "spring.codec", "spring.config",
|
||||||
|
"spring.info", "spring.jmx", "spring.main", "spring.messages", "spring.pid", "spring.profiles",
|
||||||
|
"spring.quartz", "spring.reactor", "spring.task", "spring.mandatory-file-encoding", "info",
|
||||||
|
"spring.output.ansi.enabled")
|
||||||
|
.addSection("mail").withKeyPrefixes("spring.mail", "spring.sendgrid").addSection("cache")
|
||||||
|
.withKeyPrefixes("spring.cache").addSection("server").withKeyPrefixes("server").addSection("web")
|
||||||
|
.withKeyPrefixes("spring.hateoas", "spring.http", "spring.servlet", "spring.jersey", "spring.mvc",
|
||||||
|
"spring.resources", "spring.webflux")
|
||||||
|
.addSection("json").withKeyPrefixes("spring.jackson", "spring.gson").addSection("rsocket")
|
||||||
|
.withKeyPrefixes("spring.rsocket").addSection("templating")
|
||||||
|
.withKeyPrefixes("spring.freemarker", "spring.groovy", "spring.mustache", "spring.thymeleaf")
|
||||||
|
.addOverride("spring.groovy.template.configuration", "See GroovyMarkupConfigurer")
|
||||||
|
.addSection("security").withKeyPrefixes("spring.security", "spring.ldap", "spring.session")
|
||||||
|
.addSection("data-migration").withKeyPrefixes("spring.flyway", "spring.liquibase").addSection("data")
|
||||||
|
.withKeyPrefixes("spring.couchbase", "spring.elasticsearch", "spring.h2", "spring.influx",
|
||||||
|
"spring.mongodb", "spring.redis", "spring.dao", "spring.data", "spring.datasource",
|
||||||
|
"spring.jooq", "spring.jdbc", "spring.jpa")
|
||||||
|
.addOverride("spring.datasource.dbcp2", "Commons DBCP2 specific settings")
|
||||||
|
.addOverride("spring.datasource.tomcat", "Tomcat datasource specific settings")
|
||||||
|
.addOverride("spring.datasource.hikari", "Hikari specific settings").addSection("transaction")
|
||||||
|
.withKeyPrefixes("spring.jta", "spring.transaction").addSection("integration")
|
||||||
|
.withKeyPrefixes("spring.activemq", "spring.artemis", "spring.batch", "spring.integration",
|
||||||
|
"spring.jms", "spring.kafka", "spring.rabbitmq", "spring.hazelcast", "spring.webservices")
|
||||||
|
.addSection("actuator").withKeyPrefixes("management").addSection("devtools")
|
||||||
|
.withKeyPrefixes("spring.devtools").addSection("testing").withKeyPrefixes("spring.test");
|
||||||
|
DocumentOptions options = builder.build();
|
||||||
|
new ConfigurationMetadataDocumentWriter().writeDocument(this.outputDir.toPath(), options,
|
||||||
|
this.configurationPropertyMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for generating documentation for configuration properties.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public final class DocumentOptions {
|
||||||
|
|
||||||
|
private final Map<String, List<String>> metadataSections;
|
||||||
|
|
||||||
|
private final Map<String, String> overrides;
|
||||||
|
|
||||||
|
private DocumentOptions(Map<String, List<String>> metadataSections, Map<String, String> overrides) {
|
||||||
|
this.metadataSections = metadataSections;
|
||||||
|
this.overrides = overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> getMetadataSections() {
|
||||||
|
return this.metadataSections;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> getOverrides() {
|
||||||
|
return this.overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for DocumentOptions.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
Map<String, List<String>> metadataSections = new HashMap<>();
|
||||||
|
|
||||||
|
Map<String, String> overrides = new HashMap<>();
|
||||||
|
|
||||||
|
SectionSpec addSection(String name) {
|
||||||
|
return new SectionSpec(this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder addOverride(String keyPrefix, String description) {
|
||||||
|
this.overrides.put(keyPrefix, description);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentOptions build() {
|
||||||
|
return new DocumentOptions(this.metadataSections, this.overrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for a documentation section listing properties for a specific theme.
|
||||||
|
*/
|
||||||
|
public static class SectionSpec {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final Builder builder;
|
||||||
|
|
||||||
|
SectionSpec(Builder builder, String name) {
|
||||||
|
this.builder = builder;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder withKeyPrefixes(String... keyPrefixes) {
|
||||||
|
this.builder.metadataSections.put(this.name, Arrays.asList(keyPrefixes));
|
||||||
|
return this.builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table entry containing a single configuration property.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
class SingleConfigurationTableEntry extends ConfigurationTableEntry {
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final String defaultValue;
|
||||||
|
|
||||||
|
SingleConfigurationTableEntry(ConfigurationProperty property) {
|
||||||
|
this.key = property.getName();
|
||||||
|
if (property.getType() != null && property.getType().startsWith("java.util.Map")) {
|
||||||
|
this.key += ".*";
|
||||||
|
}
|
||||||
|
this.description = property.getDescription();
|
||||||
|
this.defaultValue = getDefaultValue(property.getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDefaultValue(Object defaultValue) {
|
||||||
|
if (defaultValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (defaultValue.getClass().isArray()) {
|
||||||
|
return Arrays.stream((Object[]) defaultValue).map(Object::toString)
|
||||||
|
.collect(Collectors.joining("," + System.lineSeparator()));
|
||||||
|
}
|
||||||
|
return defaultValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void write(AsciidocBuilder builder) {
|
||||||
|
builder.appendln("|`+", this.key, "+`");
|
||||||
|
writeDefaultValue(builder);
|
||||||
|
writeDescription(builder);
|
||||||
|
builder.appendln();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDefaultValue(AsciidocBuilder builder) {
|
||||||
|
String defaultValue = (this.defaultValue != null) ? this.defaultValue : "";
|
||||||
|
if (defaultValue.isEmpty()) {
|
||||||
|
builder.appendln("|");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
defaultValue = defaultValue.replace("\\", "\\\\").replace("|", "\\|");
|
||||||
|
builder.appendln("|`+", defaultValue, "+`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDescription(AsciidocBuilder builder) {
|
||||||
|
if (this.description == null || this.description.isEmpty()) {
|
||||||
|
builder.append("|");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String cleanedDescription = this.description.replace("|", "\\|");
|
||||||
|
builder.append("|+++", cleanedDescription, "+++");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.log4j2;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.InvalidUserDataException;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@Action} to post-process a {@code Log4j2Plugins.dat} and re-order its content so
|
||||||
|
* that it is reproducible.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ReproducibleLog4j2PluginsDatAction implements Action<JavaCompile> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JavaCompile javaCompile) {
|
||||||
|
File datFile = new File(javaCompile.getDestinationDir(),
|
||||||
|
"META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat");
|
||||||
|
try {
|
||||||
|
postProcess(datFile);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new TaskExecutionException(javaCompile, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void postProcess(File datFile) throws IOException {
|
||||||
|
if (!datFile.isFile()) {
|
||||||
|
throw new InvalidUserDataException(
|
||||||
|
"META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat not found");
|
||||||
|
}
|
||||||
|
Map<String, Map<String, Plugin>> categories = new TreeMap<>();
|
||||||
|
try (DataInputStream input = new DataInputStream(new FileInputStream(datFile))) {
|
||||||
|
int categoryCount = input.readInt();
|
||||||
|
for (int i = 0; i < categoryCount; i++) {
|
||||||
|
String categoryName = input.readUTF();
|
||||||
|
int pluginCount = input.readInt();
|
||||||
|
Map<String, Plugin> category = categories.computeIfAbsent(categoryName, (c) -> new TreeMap<>());
|
||||||
|
for (int j = 0; j < pluginCount; j++) {
|
||||||
|
Plugin plugin = new Plugin(input.readUTF(), input.readUTF(), input.readUTF(), input.readBoolean(),
|
||||||
|
input.readBoolean());
|
||||||
|
category.putIfAbsent(plugin.key, plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (DataOutputStream output = new DataOutputStream(new FileOutputStream(datFile))) {
|
||||||
|
output.writeInt(categories.size());
|
||||||
|
for (Entry<String, Map<String, Plugin>> category : categories.entrySet()) {
|
||||||
|
output.writeUTF(category.getKey());
|
||||||
|
output.writeInt(category.getValue().size());
|
||||||
|
for (Plugin plugin : category.getValue().values()) {
|
||||||
|
output.writeUTF(plugin.key);
|
||||||
|
output.writeUTF(plugin.className);
|
||||||
|
output.writeUTF(plugin.name);
|
||||||
|
output.writeBoolean(plugin.printable);
|
||||||
|
output.writeBoolean(plugin.defer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Plugin {
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final boolean printable;
|
||||||
|
|
||||||
|
private final boolean defer;
|
||||||
|
|
||||||
|
private Plugin(String key, String className, String name, boolean printable, boolean defer) {
|
||||||
|
this.key = key;
|
||||||
|
this.className = className;
|
||||||
|
this.name = name;
|
||||||
|
this.printable = printable;
|
||||||
|
this.defer = defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.tasks.InputFile;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.mavenplugin.PluginXmlParser.Mojo;
|
||||||
|
import org.springframework.boot.build.mavenplugin.PluginXmlParser.Parameter;
|
||||||
|
import org.springframework.boot.build.mavenplugin.PluginXmlParser.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} to document the plugin's goals.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentPluginGoals extends DefaultTask {
|
||||||
|
|
||||||
|
private final PluginXmlParser parser = new PluginXmlParser();
|
||||||
|
|
||||||
|
private File pluginXml;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public File getPluginXml() {
|
||||||
|
return this.pluginXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPluginXml(File pluginXml) {
|
||||||
|
this.pluginXml = pluginXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void documentPluginGoals() throws IOException {
|
||||||
|
Plugin plugin = this.parser.parse(this.pluginXml);
|
||||||
|
writeOverview(plugin);
|
||||||
|
for (Mojo mojo : plugin.getMojos()) {
|
||||||
|
documentMojo(plugin, mojo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeOverview(Plugin plugin) throws IOException {
|
||||||
|
try (PrintWriter writer = new PrintWriter(new FileWriter(new File(this.outputDir, "overview.adoc")))) {
|
||||||
|
writer.println("[cols=\"1,3\"]");
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println("| Goal | Description");
|
||||||
|
writer.println();
|
||||||
|
for (Mojo mojo : plugin.getMojos()) {
|
||||||
|
writer.printf("| <<goals-%s,%s:%s>>%n", mojo.getGoal(), plugin.getGoalPrefix(), mojo.getGoal());
|
||||||
|
writer.printf("| %s%n", mojo.getDescription());
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void documentMojo(Plugin plugin, Mojo mojo) throws IOException {
|
||||||
|
try (PrintWriter writer = new PrintWriter(new FileWriter(new File(this.outputDir, mojo.getGoal() + ".adoc")))) {
|
||||||
|
String sectionId = "goals-" + mojo.getGoal();
|
||||||
|
writer.println();
|
||||||
|
writer.println();
|
||||||
|
writer.printf("[[%s]]%n", sectionId);
|
||||||
|
writer.printf("== `%s:%s`%n", plugin.getGoalPrefix(), mojo.getGoal());
|
||||||
|
writer.printf("`%s:%s:%s`%n", plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion());
|
||||||
|
writer.println();
|
||||||
|
writer.println(mojo.getDescription());
|
||||||
|
List<Parameter> parameters = mojo.getParameters().stream().filter(Parameter::isEditable)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Parameter> requiredParameters = parameters.stream().filter(Parameter::isRequired)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
String parametersSectionId = sectionId + "-parameters";
|
||||||
|
String detailsSectionId = parametersSectionId + "-details";
|
||||||
|
if (!requiredParameters.isEmpty()) {
|
||||||
|
writer.println();
|
||||||
|
writer.println();
|
||||||
|
writer.printf("[[%s-required]]%n", parametersSectionId);
|
||||||
|
writer.println("=== Required parameters");
|
||||||
|
writeParametersTable(writer, mojo.getGoal(), requiredParameters);
|
||||||
|
}
|
||||||
|
List<Parameter> optionalParameters = parameters.stream().filter((parameter) -> !parameter.isRequired())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!optionalParameters.isEmpty()) {
|
||||||
|
writer.println();
|
||||||
|
writer.println();
|
||||||
|
writer.printf("[[%s-optional]]%n", parametersSectionId);
|
||||||
|
writer.println("=== Optional parameters");
|
||||||
|
writeParametersTable(writer, detailsSectionId, optionalParameters);
|
||||||
|
}
|
||||||
|
writer.println();
|
||||||
|
writer.println();
|
||||||
|
writer.printf("[[%s]]%n", detailsSectionId);
|
||||||
|
writer.println("=== Parameter details");
|
||||||
|
writeParameterDetails(writer, parameters, detailsSectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeParametersTable(PrintWriter writer, String detailsSectionId, List<Parameter> parameters) {
|
||||||
|
writer.println("[cols=\"3,2,3\"]");
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println("| Name | Type | Default");
|
||||||
|
writer.println();
|
||||||
|
for (Parameter parameter : parameters) {
|
||||||
|
String name = parameter.getName();
|
||||||
|
writer.printf("| <<%s-%s,%s>>%n", detailsSectionId, name, name);
|
||||||
|
String type = parameter.getType();
|
||||||
|
if (type.lastIndexOf('.') >= 0) {
|
||||||
|
type = type.substring(type.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
writer.printf("| `%s`%n", type);
|
||||||
|
String defaultValue = parameter.getDefaultValue();
|
||||||
|
if (defaultValue != null) {
|
||||||
|
writer.printf("| `%s`%n", defaultValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
writer.println("|");
|
||||||
|
}
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeParameterDetails(PrintWriter writer, List<Parameter> parameters, String sectionId) {
|
||||||
|
for (Parameter parameter : parameters) {
|
||||||
|
String name = parameter.getName();
|
||||||
|
writer.println();
|
||||||
|
writer.println();
|
||||||
|
writer.printf("[[%s-%s]]%n", sectionId, name);
|
||||||
|
writer.printf("==== `%s`%n", name);
|
||||||
|
writer.println(parameter.getDescription());
|
||||||
|
writer.println();
|
||||||
|
writer.println("[cols=\"10h,90\"]");
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println();
|
||||||
|
writeDetail(writer, "Name", name);
|
||||||
|
writeDetail(writer, "Type", parameter.getType());
|
||||||
|
writeOptionalDetail(writer, "Default value", parameter.getDefaultValue());
|
||||||
|
writeOptionalDetail(writer, "User property", parameter.getUserProperty());
|
||||||
|
writeOptionalDetail(writer, "Since", parameter.getSince());
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDetail(PrintWriter writer, String name, String value) {
|
||||||
|
writer.printf("| %s%n", name);
|
||||||
|
writer.printf("| `%s`%n", value);
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeOptionalDetail(PrintWriter writer, String name, String value) {
|
||||||
|
writer.printf("| %s%n", name);
|
||||||
|
if (value != null) {
|
||||||
|
writer.printf("| `%s`%n", value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
writer.println("|");
|
||||||
|
}
|
||||||
|
writer.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.tasks.Internal;
|
||||||
|
import org.gradle.api.tasks.JavaExec;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
import org.gradle.process.internal.ExecException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link JavaExec} {@link Task task} for running Maven.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class MavenExec extends JavaExec {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(MavenExec.class);
|
||||||
|
|
||||||
|
private File projectDir;
|
||||||
|
|
||||||
|
public MavenExec() throws IOException {
|
||||||
|
setClasspath(mavenConfiguration(getProject()));
|
||||||
|
args("--batch-mode");
|
||||||
|
setMain("org.apache.maven.cli.MavenCli");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProjectDir(File projectDir) {
|
||||||
|
this.projectDir = projectDir;
|
||||||
|
getInputs().file(new File(projectDir, "pom.xml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exec() {
|
||||||
|
workingDir(this.projectDir);
|
||||||
|
systemProperty("maven.multiModuleProjectDirectory", this.projectDir.getAbsolutePath());
|
||||||
|
try {
|
||||||
|
Path logFile = Files.createTempFile(getName(), ".log");
|
||||||
|
try {
|
||||||
|
args("--log-file", logFile.toFile().getAbsolutePath());
|
||||||
|
super.exec();
|
||||||
|
if (this.log.isInfoEnabled()) {
|
||||||
|
Files.readAllLines(logFile).forEach(this.log::info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ExecException ex) {
|
||||||
|
System.out.println("Exec exception! Dumping log");
|
||||||
|
Files.readAllLines(logFile).forEach(System.out::println);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new TaskExecutionException(this, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Configuration mavenConfiguration(Project project) {
|
||||||
|
Configuration existing = project.getConfigurations().findByName("maven");
|
||||||
|
if (existing != null) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
return project.getConfigurations().create("maven", (maven) -> {
|
||||||
|
maven.getDependencies().add(project.getDependencies().create("org.apache.maven:maven-embedder:3.6.2"));
|
||||||
|
maven.getDependencies().add(project.getDependencies().create("org.apache.maven:maven-compat:3.6.2"));
|
||||||
|
maven.getDependencies().add(project.getDependencies().create("org.slf4j:slf4j-simple:1.7.5"));
|
||||||
|
maven.getDependencies().add(
|
||||||
|
project.getDependencies().create("org.apache.maven.resolver:maven-resolver-connector-basic:1.4.1"));
|
||||||
|
maven.getDependencies().add(
|
||||||
|
project.getDependencies().create("org.apache.maven.resolver:maven-resolver-transport-http:1.4.1"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public File getProjectDir() {
|
||||||
|
return this.projectDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.spring.javaformat.formatter.FileFormatter;
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention;
|
||||||
|
import org.gradle.api.publish.PublishingExtension;
|
||||||
|
import org.gradle.api.publish.maven.MavenPublication;
|
||||||
|
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||||
|
import org.gradle.api.tasks.Copy;
|
||||||
|
import org.gradle.api.tasks.JavaExec;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.api.tasks.TaskExecutionException;
|
||||||
|
import org.gradle.api.tasks.bundling.Jar;
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||||
|
import org.gradle.external.javadoc.StandardJavadocDocletOptions;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.DeployedPlugin;
|
||||||
|
import org.springframework.boot.build.MavenRepositoryPlugin;
|
||||||
|
import org.springframework.boot.build.test.IntegrationTestPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin for building Spring Boot's Maven Plugin.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class MavenPluginPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().apply(JavaLibraryPlugin.class);
|
||||||
|
project.getPlugins().apply(MavenPublishPlugin.class);
|
||||||
|
project.getPlugins().apply(DeployedPlugin.class);
|
||||||
|
project.getPlugins().apply(MavenRepositoryPlugin.class);
|
||||||
|
project.getPlugins().apply(IntegrationTestPlugin.class);
|
||||||
|
Copy populateIntTestMavenRepository = project.getTasks().create("populateIntTestMavenRepository", Copy.class);
|
||||||
|
populateIntTestMavenRepository.setDestinationDir(project.getBuildDir());
|
||||||
|
populateIntTestMavenRepository.into("int-test-maven-repository", (copy) -> {
|
||||||
|
copy.from(project.getConfigurations().getByName(MavenRepositoryPlugin.MAVEN_REPOSITORY_CONFIGURATION_NAME));
|
||||||
|
copy.from(new File(project.getBuildDir(), "maven-repository"));
|
||||||
|
});
|
||||||
|
populateIntTestMavenRepository
|
||||||
|
.dependsOn(project.getTasks().getByName(MavenRepositoryPlugin.PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME));
|
||||||
|
configurePomPackaging(project);
|
||||||
|
MavenExec generateHelpMojo = configureMojoGenerationTasks(project);
|
||||||
|
MavenExec generatePluginDescriptor = configurePluginDescriptorGenerationTasks(project, generateHelpMojo);
|
||||||
|
DocumentPluginGoals documentPluginGoals = project.getTasks().create("documentPluginGoals",
|
||||||
|
DocumentPluginGoals.class);
|
||||||
|
documentPluginGoals.setPluginXml(generatePluginDescriptor.getOutputs().getFiles().getSingleFile());
|
||||||
|
documentPluginGoals.setOutputDir(new File(project.getBuildDir(), "docs/generated/goals/"));
|
||||||
|
documentPluginGoals.dependsOn(generatePluginDescriptor);
|
||||||
|
Jar jar = (Jar) project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME);
|
||||||
|
includeDescriptorInJar(jar, generatePluginDescriptor);
|
||||||
|
includeHelpMojoInJar(jar, generateHelpMojo);
|
||||||
|
PrepareMavenBinaries prepareMavenBinaries = project.getTasks().create("prepareMavenBinaries",
|
||||||
|
PrepareMavenBinaries.class);
|
||||||
|
prepareMavenBinaries.setOutputDir(new File(project.getBuildDir(), "maven-binaries"));
|
||||||
|
project.getTasks().getByName(IntegrationTestPlugin.INT_TEST_TASK_NAME).dependsOn(populateIntTestMavenRepository,
|
||||||
|
prepareMavenBinaries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configurePomPackaging(Project project) {
|
||||||
|
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||||
|
publishing.getPublications().withType(MavenPublication.class,
|
||||||
|
(mavenPublication) -> mavenPublication.pom((pom) -> pom.setPackaging("maven-plugin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MavenExec configureMojoGenerationTasks(Project project) {
|
||||||
|
File helpMojoDir = new File(project.getBuildDir(), "help-mojo");
|
||||||
|
Copy helpMojoInputs = createCopyHelpMojoInputs(project, helpMojoDir);
|
||||||
|
MavenExec generateHelpMojo = createGenerateHelpMojo(project, helpMojoDir);
|
||||||
|
generateHelpMojo.dependsOn(helpMojoInputs);
|
||||||
|
return generateHelpMojo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Copy createCopyHelpMojoInputs(Project project, File mavenDir) {
|
||||||
|
Copy mojoInputs = project.getTasks().create("copyHelpMojoInputs", Copy.class);
|
||||||
|
mojoInputs.setDestinationDir(mavenDir);
|
||||||
|
mojoInputs.from(new File(project.getProjectDir(), "src/maven/resources/pom.xml"),
|
||||||
|
(sync) -> sync.filter((input) -> input.replace("{{version}}", project.getVersion().toString())));
|
||||||
|
return mojoInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MavenExec createGenerateHelpMojo(Project project, File mavenDir) {
|
||||||
|
MavenExec generateHelpMojo = project.getTasks().create("generateHelpMojo", MavenExec.class);
|
||||||
|
generateHelpMojo.setProjectDir(mavenDir);
|
||||||
|
generateHelpMojo.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:helpmojo");
|
||||||
|
generateHelpMojo.getOutputs().dir(new File(mavenDir, "target/generated-sources/plugin"));
|
||||||
|
return generateHelpMojo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MavenExec configurePluginDescriptorGenerationTasks(Project project, MavenExec generateHelpMojo) {
|
||||||
|
File pluginDescriptorDir = new File(project.getBuildDir(), "plugin-descriptor");
|
||||||
|
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
|
||||||
|
SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
File generatedHelpMojoDir = new File(project.getBuildDir(), "generated/sources/helpMojo");
|
||||||
|
project.getTasks().withType(Javadoc.class,
|
||||||
|
(javadoc) -> ((StandardJavadocDocletOptions) javadoc.getOptions()).addMultilineStringsOption("tag")
|
||||||
|
.setValue(Arrays.asList("goal:X", "requiresProject:X", "threadSafe:X")));
|
||||||
|
FormatHelpMojoSource copyFormattedHelpMojoSource = project.getTasks().create("copyFormattedHelpMojoSource",
|
||||||
|
FormatHelpMojoSource.class);
|
||||||
|
copyFormattedHelpMojoSource.setGenerator(generateHelpMojo);
|
||||||
|
copyFormattedHelpMojoSource.setOutputDir(generatedHelpMojoDir);
|
||||||
|
mainSourceSet.getAllJava().srcDir(generatedHelpMojoDir);
|
||||||
|
project.getTasks().getByName(mainSourceSet.getCompileJavaTaskName()).dependsOn(copyFormattedHelpMojoSource);
|
||||||
|
Copy pluginDescriptorInputs = createCopyPluginDescriptorInputs(project, pluginDescriptorDir, mainSourceSet);
|
||||||
|
pluginDescriptorInputs.dependsOn(mainSourceSet.getClassesTaskName());
|
||||||
|
MavenExec generatePluginDescriptor = createGeneratePluginDescriptor(project, pluginDescriptorDir);
|
||||||
|
generatePluginDescriptor.dependsOn(pluginDescriptorInputs);
|
||||||
|
return generatePluginDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Copy createCopyPluginDescriptorInputs(Project project, File destination, SourceSet sourceSet) {
|
||||||
|
Copy pluginDescriptorInputs = project.getTasks().create("copyPluginDescriptorInputs", Copy.class);
|
||||||
|
pluginDescriptorInputs.setDestinationDir(destination);
|
||||||
|
pluginDescriptorInputs.from(new File(project.getProjectDir(), "src/maven/resources/pom.xml"),
|
||||||
|
(sync) -> sync.filter((input) -> input.replace("{{version}}", project.getVersion().toString())));
|
||||||
|
pluginDescriptorInputs.from(sourceSet.getOutput().getClassesDirs(), (sync) -> sync.into("target/classes"));
|
||||||
|
pluginDescriptorInputs.from(sourceSet.getAllJava().getSrcDirs(), (sync) -> sync.into("src/main/java"));
|
||||||
|
return pluginDescriptorInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MavenExec createGeneratePluginDescriptor(Project project, File mavenDir) {
|
||||||
|
MavenExec generatePluginDescriptor = project.getTasks().create("generatePluginDescriptor", MavenExec.class);
|
||||||
|
generatePluginDescriptor.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:descriptor");
|
||||||
|
generatePluginDescriptor.getOutputs().file(new File(mavenDir, "target/classes/META-INF/maven/plugin.xml"));
|
||||||
|
generatePluginDescriptor.getInputs().dir(new File(mavenDir, "target/classes/org"));
|
||||||
|
generatePluginDescriptor.setProjectDir(mavenDir);
|
||||||
|
return generatePluginDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void includeDescriptorInJar(Jar jar, JavaExec generatePluginDescriptor) {
|
||||||
|
jar.from(generatePluginDescriptor, (copy) -> copy.into("META-INF/maven/"));
|
||||||
|
jar.dependsOn(generatePluginDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void includeHelpMojoInJar(Jar jar, JavaExec generateHelpMojo) {
|
||||||
|
jar.from(generateHelpMojo);
|
||||||
|
jar.dependsOn(generateHelpMojo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FormatHelpMojoSource extends DefaultTask {
|
||||||
|
|
||||||
|
private Task generator;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
void setGenerator(Task generator) {
|
||||||
|
this.generator = generator;
|
||||||
|
getInputs().files(this.generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void syncAndFormat() {
|
||||||
|
FileFormatter fileFormatter = new FileFormatter();
|
||||||
|
for (File output : this.generator.getOutputs().getFiles()) {
|
||||||
|
fileFormatter.formatFiles(getProject().fileTree(output), StandardCharsets.UTF_8).forEach((fileEdit) -> {
|
||||||
|
Path relativePath = output.toPath().relativize(fileEdit.getFile().toPath());
|
||||||
|
Path outputLocation = this.outputDir.toPath().resolve(relativePath);
|
||||||
|
try {
|
||||||
|
Files.createDirectories(outputLocation.getParent());
|
||||||
|
Files.write(outputLocation, fileEdit.getFormattedContent().getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new TaskExecutionException(this, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.xpath.XPath;
|
||||||
|
import javax.xml.xpath.XPathConstants;
|
||||||
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parser for a Maven plugin's {@code plugin.xml} file.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class PluginXmlParser {
|
||||||
|
|
||||||
|
private final XPath xpath;
|
||||||
|
|
||||||
|
PluginXmlParser() {
|
||||||
|
this.xpath = XPathFactory.newInstance().newXPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin parse(File pluginXml) {
|
||||||
|
try {
|
||||||
|
Node root = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(pluginXml);
|
||||||
|
List<Mojo> mojos = parseMojos(root);
|
||||||
|
return new Plugin(textAt("//plugin/groupId", root), textAt("//plugin/artifactId", root),
|
||||||
|
textAt("//plugin/version", root), textAt("//plugin/goalPrefix", root), mojos);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String textAt(String path, Node source) throws XPathExpressionException {
|
||||||
|
String text = this.xpath.evaluate(path + "/text()", source);
|
||||||
|
return (text.length() == 0) ? null : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Mojo> parseMojos(Node plugin) throws XPathExpressionException {
|
||||||
|
List<Mojo> mojos = new ArrayList<Mojo>();
|
||||||
|
for (Node mojoNode : nodesAt("//plugin/mojos/mojo", plugin)) {
|
||||||
|
mojos.add(new Mojo(textAt("goal", mojoNode), format(textAt("description", mojoNode)),
|
||||||
|
parseParameters(mojoNode)));
|
||||||
|
}
|
||||||
|
return mojos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterable<Node> nodesAt(String path, Node source) throws XPathExpressionException {
|
||||||
|
return IterableNodeList.of((NodeList) this.xpath.evaluate(path, source, XPathConstants.NODESET));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Parameter> parseParameters(Node mojoNode) throws XPathExpressionException {
|
||||||
|
Map<String, String> defaultValues = new HashMap<>();
|
||||||
|
Map<String, String> userProperties = new HashMap<>();
|
||||||
|
for (Node parameterConfigurationNode : nodesAt("configuration/*", mojoNode)) {
|
||||||
|
String userProperty = parameterConfigurationNode.getTextContent();
|
||||||
|
if (userProperty != null && userProperty.length() > 0) {
|
||||||
|
userProperties.put(parameterConfigurationNode.getNodeName(),
|
||||||
|
userProperty.replace("${", "`").replace("}", "`"));
|
||||||
|
}
|
||||||
|
Node defaultValueAttribute = parameterConfigurationNode.getAttributes().getNamedItem("default-value");
|
||||||
|
if (defaultValueAttribute != null && defaultValueAttribute.getTextContent().length() > 0) {
|
||||||
|
defaultValues.put(parameterConfigurationNode.getNodeName(), defaultValueAttribute.getTextContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Parameter> parameters = new ArrayList<>();
|
||||||
|
for (Node parameterNode : nodesAt("parameters/parameter", mojoNode)) {
|
||||||
|
parameters.add(parseParameter(parameterNode, defaultValues, userProperties));
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parameter parseParameter(Node parameterNode, Map<String, String> defaultValues,
|
||||||
|
Map<String, String> userProperties) throws XPathExpressionException {
|
||||||
|
Parameter parameter = new Parameter(textAt("name", parameterNode), textAt("type", parameterNode),
|
||||||
|
booleanAt("required", parameterNode), booleanAt("editable", parameterNode),
|
||||||
|
format(textAt("description", parameterNode)), defaultValues.get(textAt("name", parameterNode)),
|
||||||
|
userProperties.get(textAt("name", parameterNode)), textAt("since", parameterNode));
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean booleanAt(String path, Node node) throws XPathExpressionException {
|
||||||
|
return Boolean.valueOf(textAt(path, node));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String format(String input) {
|
||||||
|
return input.replace("<code>", "`").replace("</code>", "`").replace("<", "<").replace(">", ">")
|
||||||
|
.replace("<br>", " ").replace("\n", " ").replace(""", "\"").replaceAll("\\{@code (.*?)\\}", "`$1`")
|
||||||
|
.replaceAll("\\{@link (.*?)\\}", "`$1`").replaceAll("\\{@literal (.*?)\\}", "`$1`")
|
||||||
|
.replaceAll("<a href=.\"(.*?)\".>(.*?)</a>", "\\$1[\\$2]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class IterableNodeList implements Iterable<Node> {
|
||||||
|
|
||||||
|
private final NodeList nodeList;
|
||||||
|
|
||||||
|
private IterableNodeList(NodeList nodeList) {
|
||||||
|
this.nodeList = nodeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Iterable<Node> of(NodeList nodeList) {
|
||||||
|
return new IterableNodeList(nodeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Node> iterator() {
|
||||||
|
|
||||||
|
return new Iterator<Node>() {
|
||||||
|
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return this.index < IterableNodeList.this.nodeList.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node next() {
|
||||||
|
return IterableNodeList.this.nodeList.item(this.index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Plugin {
|
||||||
|
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
|
private final String artifactId;
|
||||||
|
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
private final String goalPrefix;
|
||||||
|
|
||||||
|
private final List<Mojo> mojos;
|
||||||
|
|
||||||
|
private Plugin(String groupId, String artifactId, String version, String goalPrefix, List<Mojo> mojos) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.artifactId = artifactId;
|
||||||
|
this.version = version;
|
||||||
|
this.goalPrefix = goalPrefix;
|
||||||
|
this.mojos = mojos;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getArtifactId() {
|
||||||
|
return this.artifactId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getGoalPrefix() {
|
||||||
|
return this.goalPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Mojo> getMojos() {
|
||||||
|
return this.mojos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Mojo {
|
||||||
|
|
||||||
|
private final String goal;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final List<Parameter> parameters;
|
||||||
|
|
||||||
|
private Mojo(String goal, String description, List<Parameter> parameters) {
|
||||||
|
this.goal = goal;
|
||||||
|
this.description = description;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getGoal() {
|
||||||
|
return this.goal;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Parameter> getParameters() {
|
||||||
|
return this.parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Parameter {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
private final boolean required;
|
||||||
|
|
||||||
|
private final boolean editable;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final String defaultValue;
|
||||||
|
|
||||||
|
private final String userProperty;
|
||||||
|
|
||||||
|
private final String since;
|
||||||
|
|
||||||
|
private Parameter(String name, String type, boolean required, boolean editable, String description,
|
||||||
|
String defaultValue, String userProperty, String since) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.required = required;
|
||||||
|
this.editable = editable;
|
||||||
|
this.description = description;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.userProperty = userProperty;
|
||||||
|
this.since = since;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isRequired() {
|
||||||
|
return this.required;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEditable() {
|
||||||
|
return this.editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDefaultValue() {
|
||||||
|
return this.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUserProperty() {
|
||||||
|
return this.userProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSince() {
|
||||||
|
return this.since;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} to make Maven binaries available for integration testing.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class PrepareMavenBinaries extends DefaultTask {
|
||||||
|
|
||||||
|
private Set<String> versions = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public Set<String> getVersions() {
|
||||||
|
return this.versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void versions(String... versions) {
|
||||||
|
this.versions.addAll(Arrays.asList(versions));
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void prepareBinaries() {
|
||||||
|
for (String version : this.versions) {
|
||||||
|
Configuration configuration = getProject().getConfigurations().detachedConfiguration(
|
||||||
|
getProject().getDependencies().create("org.apache.maven:apache-maven:" + version + ":bin@zip"));
|
||||||
|
getProject().copy(
|
||||||
|
(copy) -> copy.into(this.outputDir).from(getProject().zipTree(configuration.getSingleFile())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.optional;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.attributes.Usage;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||||
|
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
|
||||||
|
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
|
||||||
|
* {@code optional} configuration. The {@code optional} configuration is part of the
|
||||||
|
* project's compile and runtime classpath's but does not affect the classpath of
|
||||||
|
* dependent projects.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class OptionalDependenciesPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@code optional} configuration.
|
||||||
|
*/
|
||||||
|
public static final String OPTIONAL_CONFIGURATION_NAME = "optional";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
Configuration optional = project.getConfigurations().create("optional");
|
||||||
|
optional.attributes((attributes) -> attributes.attribute(Usage.USAGE_ATTRIBUTE,
|
||||||
|
project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)));
|
||||||
|
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||||
|
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class)
|
||||||
|
.getSourceSets();
|
||||||
|
sourceSets.all((sourceSet) -> {
|
||||||
|
sourceSet.setCompileClasspath(sourceSet.getCompileClasspath().plus(optional));
|
||||||
|
sourceSet.setRuntimeClasspath(sourceSet.getRuntimeClasspath().plus(optional));
|
||||||
|
});
|
||||||
|
project.getTasks().withType(Javadoc.class)
|
||||||
|
.all((javadoc) -> javadoc.setClasspath(javadoc.getClasspath().plus(optional)));
|
||||||
|
});
|
||||||
|
project.getPlugins().withType(EclipsePlugin.class,
|
||||||
|
(eclipePlugin) -> project.getExtensions().getByType(EclipseModel.class)
|
||||||
|
.classpath((classpath) -> classpath.getPlusConfigurations().add(optional)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.starters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} to document all starter projects.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentStarters extends AbstractTask {
|
||||||
|
|
||||||
|
private final Configuration starters;
|
||||||
|
|
||||||
|
private File outputDir;
|
||||||
|
|
||||||
|
public DocumentStarters() {
|
||||||
|
this.starters = getProject().getConfigurations().create("starters");
|
||||||
|
getProject().getGradle().projectsEvaluated((gradle) -> {
|
||||||
|
gradle.allprojects((project) -> {
|
||||||
|
if (project.getPlugins().hasPlugin(StarterPlugin.class)) {
|
||||||
|
Map<String, String> dependency = new HashMap<>();
|
||||||
|
dependency.put("path", project.getPath());
|
||||||
|
dependency.put("configuration", "starterMetadata");
|
||||||
|
this.starters.getDependencies().add(project.getDependencies().project(dependency));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public File getOutputDir() {
|
||||||
|
return this.outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputDir(File outputDir) {
|
||||||
|
this.outputDir = outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getStarters() {
|
||||||
|
return this.starters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentStarters() {
|
||||||
|
Set<Starter> starters = this.starters.getFiles().stream().map(this::loadStarter)
|
||||||
|
.collect(Collectors.toCollection(TreeSet::new));
|
||||||
|
writeTable("application-starters", starters.stream().filter(Starter::isApplication));
|
||||||
|
writeTable("production-starters", starters.stream().filter(Starter::isProduction));
|
||||||
|
writeTable("technical-starters", starters.stream().filter(Starter::isTechnical));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Starter loadStarter(File metadata) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try (FileReader reader = new FileReader(metadata)) {
|
||||||
|
properties.load(reader);
|
||||||
|
return new Starter(properties.getProperty("name"), properties.getProperty("description"),
|
||||||
|
StringUtils.commaDelimitedListToSet(properties.getProperty("dependencies")));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTable(String name, Stream<Starter> starters) {
|
||||||
|
File output = new File(this.outputDir, name + ".adoc");
|
||||||
|
output.getParentFile().mkdirs();
|
||||||
|
try (PrintWriter writer = new PrintWriter(new FileWriter(output))) {
|
||||||
|
writer.println("|===");
|
||||||
|
writer.println("| Name | Description");
|
||||||
|
starters.forEach((starter) -> {
|
||||||
|
writer.println();
|
||||||
|
writer.printf("| [[%s]]`%s`%n", starter.name, starter.name);
|
||||||
|
writer.printf("| %s%n", postProcessDescription(starter.description));
|
||||||
|
});
|
||||||
|
writer.println("|===");
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String postProcessDescription(String description) {
|
||||||
|
return addStarterCrossLinks(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addStarterCrossLinks(String input) {
|
||||||
|
return input.replaceAll("(spring-boot-starter[A-Za-z-]*)", "<<$1,`$1`>>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Starter implements Comparable<Starter> {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final Set<String> dependencies;
|
||||||
|
|
||||||
|
private Starter(String name, String description, Set<String> dependencies) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isProduction() {
|
||||||
|
return this.name.equals("spring-boot-starter-actuator");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTechnical() {
|
||||||
|
return !Arrays.asList("spring-boot-starter", "spring-boot-starter-test").contains(this.name)
|
||||||
|
&& !isProduction() && !this.dependencies.contains("spring-boot-starter");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isApplication() {
|
||||||
|
return !isProduction() && !isTechnical();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Starter other) {
|
||||||
|
return this.name.compareTo(other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.starters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ResolvedArtifact;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} for generating metadata that describes a starter.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class StarterMetadata extends AbstractTask {
|
||||||
|
|
||||||
|
private Configuration dependencies;
|
||||||
|
|
||||||
|
private File destination;
|
||||||
|
|
||||||
|
public StarterMetadata() {
|
||||||
|
getInputs().property("name", (Callable<String>) () -> getProject().getName());
|
||||||
|
getInputs().property("description", (Callable<String>) () -> getProject().getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getDependencies() {
|
||||||
|
return this.dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDependencies(Configuration dependencies) {
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getDestination() {
|
||||||
|
return this.destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(File destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void generateMetadata() throws IOException {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("name", getProject().getName());
|
||||||
|
properties.setProperty("description", getProject().getDescription());
|
||||||
|
properties.setProperty("dependencies", String.join(",", this.dependencies.getResolvedConfiguration()
|
||||||
|
.getResolvedArtifacts().stream().map(ResolvedArtifact::getName).collect(Collectors.toSet())));
|
||||||
|
this.destination.getParentFile().mkdirs();
|
||||||
|
try (FileWriter writer = new FileWriter(this.destination)) {
|
||||||
|
properties.store(writer, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.starters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
|
import org.gradle.api.plugins.JavaBasePlugin;
|
||||||
|
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.PluginContainer;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.ConventionsPlugin;
|
||||||
|
import org.springframework.boot.build.DeployedPlugin;
|
||||||
|
import org.springframework.boot.build.classpath.CheckClasspathForConflicts;
|
||||||
|
import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Plugin} for a starter project.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class StarterPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
PluginContainer plugins = project.getPlugins();
|
||||||
|
plugins.apply(DeployedPlugin.class);
|
||||||
|
plugins.apply(JavaLibraryPlugin.class);
|
||||||
|
plugins.apply(ConventionsPlugin.class);
|
||||||
|
StarterMetadata starterMetadata = project.getTasks().create("starterMetadata", StarterMetadata.class);
|
||||||
|
ConfigurationContainer configurations = project.getConfigurations();
|
||||||
|
Configuration runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||||
|
starterMetadata.setDependencies(runtimeClasspath);
|
||||||
|
File destination = new File(project.getBuildDir(), "starter-metadata.properties");
|
||||||
|
starterMetadata.setDestination(destination);
|
||||||
|
configurations.create("starterMetadata");
|
||||||
|
project.getArtifacts().add("starterMetadata", project.provider(starterMetadata::getDestination),
|
||||||
|
(artifact) -> artifact.builtBy(starterMetadata));
|
||||||
|
createClasspathConflictsCheck(runtimeClasspath, project);
|
||||||
|
createProhibitedDependenciesCheck(runtimeClasspath, project);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createClasspathConflictsCheck(Configuration classpath, Project project) {
|
||||||
|
CheckClasspathForConflicts checkClasspathForConflicts = project.getTasks().create(
|
||||||
|
"check" + StringUtils.capitalize(classpath.getName() + "ForConflicts"),
|
||||||
|
CheckClasspathForConflicts.class);
|
||||||
|
checkClasspathForConflicts.setClasspath(classpath);
|
||||||
|
project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(checkClasspathForConflicts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProhibitedDependenciesCheck(Configuration classpath, Project project) {
|
||||||
|
CheckClasspathForProhibitedDependencies checkClasspathForProhibitedDependencies = project.getTasks().create(
|
||||||
|
"check" + StringUtils.capitalize(classpath.getName() + "ForProhibitedDependencies"),
|
||||||
|
CheckClasspathForProhibitedDependencies.class);
|
||||||
|
checkClasspathForProhibitedDependencies.setClasspath(classpath);
|
||||||
|
project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(checkClasspathForProhibitedDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
import org.gradle.api.tasks.testing.Test;
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||||
|
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
|
||||||
|
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@Plugin} to configure integration testing support in a {@link Project}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class IntegrationTestPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@code intTest} task.
|
||||||
|
*/
|
||||||
|
public static String INT_TEST_TASK_NAME = "intTest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the {@code intTest} source set.
|
||||||
|
*/
|
||||||
|
public static String INT_TEST_SOURCE_SET_NAME = "intTest";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> this.configureIntegrationTesting(project));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureIntegrationTesting(Project project) {
|
||||||
|
SourceSet intTestSourceSet = createSourceSet(project);
|
||||||
|
Test intTest = createTestTask(project, intTestSourceSet);
|
||||||
|
project.getTasks().getByName(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(intTest);
|
||||||
|
project.getPlugins().withType(EclipsePlugin.class, (eclipsePlugin) -> {
|
||||||
|
EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
|
||||||
|
eclipse.classpath((classpath) -> classpath.getPlusConfigurations().add(
|
||||||
|
project.getConfigurations().getByName(intTestSourceSet.getRuntimeClasspathConfigurationName())));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private SourceSet createSourceSet(Project project) {
|
||||||
|
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
|
||||||
|
SourceSet intTestSourceSet = sourceSets.create(INT_TEST_SOURCE_SET_NAME);
|
||||||
|
SourceSet main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
intTestSourceSet.setCompileClasspath(intTestSourceSet.getCompileClasspath().plus(main.getOutput()));
|
||||||
|
intTestSourceSet.setRuntimeClasspath(intTestSourceSet.getRuntimeClasspath().plus(main.getOutput()));
|
||||||
|
return intTestSourceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Test createTestTask(Project project, SourceSet intTestSourceSet) {
|
||||||
|
Test intTest = project.getTasks().create(INT_TEST_TASK_NAME, Test.class);
|
||||||
|
intTest.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||||
|
intTest.setDescription("Runs integration tests.");
|
||||||
|
intTest.setTestClassesDirs(intTestSourceSet.getOutput().getClassesDirs());
|
||||||
|
intTest.setClasspath(intTestSourceSet.getRuntimeClasspath());
|
||||||
|
intTest.shouldRunAfter(JavaPlugin.TEST_TASK_NAME);
|
||||||
|
return intTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} used to document test slices.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DocumentTestSlices extends AbstractTask {
|
||||||
|
|
||||||
|
private FileCollection testSlices;
|
||||||
|
|
||||||
|
private File outputFile;
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
public FileCollection getTestSlices() {
|
||||||
|
return this.testSlices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTestSlices(FileCollection testSlices) {
|
||||||
|
this.testSlices = testSlices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getOutputFile() {
|
||||||
|
return this.outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFile(File outputFile) {
|
||||||
|
this.outputFile = outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentTestSlices() throws IOException {
|
||||||
|
Set<TestSlice> testSlices = readTestSlices();
|
||||||
|
writeTable(testSlices);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(Set<TestSlice> testSlices) throws IOException {
|
||||||
|
this.outputFile.getParentFile().mkdirs();
|
||||||
|
try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile))) {
|
||||||
|
writer.println("[cols=\"d,a\"]");
|
||||||
|
writer.println("|===");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
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.Properties;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.internal.AbstractTask;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
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 class TestSliceMetadata extends AbstractTask {
|
||||||
|
|
||||||
|
private SourceSet sourceSet;
|
||||||
|
|
||||||
|
private File outputFile;
|
||||||
|
|
||||||
|
public TestSliceMetadata() {
|
||||||
|
getInputs().dir((Callable<File>) () -> this.sourceSet.getOutput().getResourcesDir());
|
||||||
|
getInputs().files((Callable<FileCollection>) () -> this.sourceSet.getOutput().getClassesDirs());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceSet(SourceSet sourceSet) {
|
||||||
|
this.sourceSet = sourceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public File getOutputFile() {
|
||||||
|
return this.outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFile(File outputFile) {
|
||||||
|
this.outputFile = outputFile;
|
||||||
|
Configuration testSliceMetadata = getProject().getConfigurations().maybeCreate("testSliceMetadata");
|
||||||
|
getProject().getArtifacts().add(testSliceMetadata.getName(),
|
||||||
|
getProject().provider((Callable<File>) this::getOutputFile), (artifact) -> artifact.builtBy(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void documentTestSlices() throws IOException {
|
||||||
|
Properties testSlices = readTestSlices();
|
||||||
|
getOutputFile().getParentFile().mkdirs();
|
||||||
|
try (FileWriter writer = new FileWriter(getOutputFile())) {
|
||||||
|
testSlices.store(writer, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties readTestSlices() throws IOException {
|
||||||
|
Properties testSlices = new Properties();
|
||||||
|
try (URLClassLoader classLoader = new URLClassLoader(
|
||||||
|
StreamSupport.stream(this.sourceSet.getRuntimeClasspath().spliterator(), false).map(this::toURL)
|
||||||
|
.toArray(URL[]::new))) {
|
||||||
|
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||||
|
Properties springFactories = readSpringFactories(
|
||||||
|
new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||||
|
for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) {
|
||||||
|
addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return testSlices;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<settings>
|
||||||
|
<localRepository>localRepositoryPath</localRepository>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>spring-repositories</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spring-snapshot</id>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-milestone</id>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
</settings>
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.assertj;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.xpath.XPath;
|
||||||
|
import javax.xml.xpath.XPathConstants;
|
||||||
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
|
import org.assertj.core.api.AbstractAssert;
|
||||||
|
import org.assertj.core.api.AssertProvider;
|
||||||
|
import org.assertj.core.api.StringAssert;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AssertJ {@link AssertProvider} for {@link Node} assertions.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class NodeAssert extends AbstractAssert<NodeAssert, Node> implements AssertProvider<NodeAssert> {
|
||||||
|
|
||||||
|
private static final DocumentBuilderFactory FACTORY = DocumentBuilderFactory.newInstance();
|
||||||
|
|
||||||
|
private final XPathFactory xpathFactory = XPathFactory.newInstance();
|
||||||
|
|
||||||
|
private final XPath xpath = this.xpathFactory.newXPath();
|
||||||
|
|
||||||
|
public NodeAssert(File xmlFile) {
|
||||||
|
this(read(xmlFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeAssert(Node actual) {
|
||||||
|
super(actual, NodeAssert.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document read(File xmlFile) {
|
||||||
|
try {
|
||||||
|
return FACTORY.newDocumentBuilder().parse(xmlFile);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeAssert nodeAtPath(String xpath) {
|
||||||
|
try {
|
||||||
|
return new NodeAssert((Node) this.xpath.evaluate(xpath, this.actual, XPathConstants.NODE));
|
||||||
|
}
|
||||||
|
catch (XPathExpressionException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringAssert textAtPath(String xpath) {
|
||||||
|
try {
|
||||||
|
return new StringAssert(
|
||||||
|
(String) this.xpath.evaluate(xpath + "/text()", this.actual, XPathConstants.STRING));
|
||||||
|
}
|
||||||
|
catch (XPathExpressionException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeAssert assertThat() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult;
|
||||||
|
import org.gradle.testkit.runner.GradleRunner;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.DeployedPlugin;
|
||||||
|
import org.springframework.boot.build.assertj.NodeAssert;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BomPlugin}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class BomPluginIntegrationTests {
|
||||||
|
|
||||||
|
private File projectDir;
|
||||||
|
|
||||||
|
private File buildFile;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup(@TempDir File projectDir) throws IOException {
|
||||||
|
this.projectDir = projectDir;
|
||||||
|
this.buildFile = new File(this.projectDir, "build.gradle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void libraryModulesAreIncludedInDependencyManagementOfGeneratedPom() throws IOException {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins {");
|
||||||
|
out.println(" id 'org.springframework.boot.bom'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("bom {");
|
||||||
|
out.println(" library('ActiveMQ', '5.15.10') {");
|
||||||
|
out.println(" group('org.apache.activemq') {");
|
||||||
|
out.println(" modules = [");
|
||||||
|
out.println(" 'activemq-amqp',");
|
||||||
|
out.println(" 'activemq-blueprint'");
|
||||||
|
out.println(" ]");
|
||||||
|
out.println(" }");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
generatePom((pom) -> {
|
||||||
|
assertThat(pom).textAtPath("//properties/activemq.version").isEqualTo("5.15.10");
|
||||||
|
NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[1]");
|
||||||
|
assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq");
|
||||||
|
assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-amqp");
|
||||||
|
assertThat(dependency).textAtPath("version").isEqualTo("${activemq.version}");
|
||||||
|
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
|
||||||
|
assertThat(dependency).textAtPath("type").isNullOrEmpty();
|
||||||
|
dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[2]");
|
||||||
|
assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq");
|
||||||
|
assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-blueprint");
|
||||||
|
assertThat(dependency).textAtPath("version").isEqualTo("${activemq.version}");
|
||||||
|
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
|
||||||
|
assertThat(dependency).textAtPath("type").isNullOrEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void libraryPluginsAreIncludedInPluginManagementOfGeneratedPom() throws IOException {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins {");
|
||||||
|
out.println(" id 'org.springframework.boot.bom'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("bom {");
|
||||||
|
out.println(" library('Flyway', '6.0.8') {");
|
||||||
|
out.println(" group('org.flywaydb') {");
|
||||||
|
out.println(" plugins = [");
|
||||||
|
out.println(" 'flyway-maven-plugin'");
|
||||||
|
out.println(" ]");
|
||||||
|
out.println(" }");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
generatePom((pom) -> {
|
||||||
|
assertThat(pom).textAtPath("//properties/flyway.version").isEqualTo("6.0.8");
|
||||||
|
NodeAssert plugin = pom.nodeAtPath("//pluginManagement/plugins/plugin");
|
||||||
|
assertThat(plugin).textAtPath("groupId").isEqualTo("org.flywaydb");
|
||||||
|
assertThat(plugin).textAtPath("artifactId").isEqualTo("flyway-maven-plugin");
|
||||||
|
assertThat(plugin).textAtPath("version").isEqualTo("${flyway.version}");
|
||||||
|
assertThat(plugin).textAtPath("scope").isNullOrEmpty();
|
||||||
|
assertThat(plugin).textAtPath("type").isNullOrEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void libraryImportsAreIncludedInDependencyManagementOfGeneratedPom() throws Exception {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins {");
|
||||||
|
out.println(" id 'org.springframework.boot.bom'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("bom {");
|
||||||
|
out.println(" library('Jackson Bom', '2.10.0') {");
|
||||||
|
out.println(" group('com.fasterxml.jackson') {");
|
||||||
|
out.println(" imports = [");
|
||||||
|
out.println(" 'jackson-bom'");
|
||||||
|
out.println(" ]");
|
||||||
|
out.println(" }");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
generatePom((pom) -> {
|
||||||
|
assertThat(pom).textAtPath("//properties/jackson-bom.version").isEqualTo("2.10.0");
|
||||||
|
NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency");
|
||||||
|
assertThat(dependency).textAtPath("groupId").isEqualTo("com.fasterxml.jackson");
|
||||||
|
assertThat(dependency).textAtPath("artifactId").isEqualTo("jackson-bom");
|
||||||
|
assertThat(dependency).textAtPath("version").isEqualTo("${jackson-bom.version}");
|
||||||
|
assertThat(dependency).textAtPath("scope").isEqualTo("import");
|
||||||
|
assertThat(dependency).textAtPath("type").isEqualTo("pom");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void moduleExclusionsAreIncludedInDependencyManagementOfGeneratedPom() throws IOException {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins {");
|
||||||
|
out.println(" id 'org.springframework.boot.bom'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("bom {");
|
||||||
|
out.println(" library('MySQL', '8.0.18') {");
|
||||||
|
out.println(" group('mysql') {");
|
||||||
|
out.println(" modules = [");
|
||||||
|
out.println(" 'mysql-connector-java' {");
|
||||||
|
out.println(" exclude group: 'com.google.protobuf', module: 'protobuf-java'");
|
||||||
|
out.println(" }");
|
||||||
|
out.println(" ]");
|
||||||
|
out.println(" }");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
generatePom((pom) -> {
|
||||||
|
assertThat(pom).textAtPath("//properties/mysql.version").isEqualTo("8.0.18");
|
||||||
|
NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency");
|
||||||
|
assertThat(dependency).textAtPath("groupId").isEqualTo("mysql");
|
||||||
|
assertThat(dependency).textAtPath("artifactId").isEqualTo("mysql-connector-java");
|
||||||
|
assertThat(dependency).textAtPath("version").isEqualTo("${mysql.version}");
|
||||||
|
assertThat(dependency).textAtPath("scope").isNullOrEmpty();
|
||||||
|
assertThat(dependency).textAtPath("type").isNullOrEmpty();
|
||||||
|
NodeAssert exclusion = dependency.nodeAtPath("exclusions/exclusion");
|
||||||
|
assertThat(exclusion).textAtPath("groupId").isEqualTo("com.google.protobuf");
|
||||||
|
assertThat(exclusion).textAtPath("artifactId").isEqualTo("protobuf-java");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private BuildResult runGradle(String... args) {
|
||||||
|
return GradleRunner.create().withDebug(true).withProjectDir(this.projectDir).withArguments(args)
|
||||||
|
.withPluginClasspath().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generatePom(Consumer<NodeAssert> consumer) {
|
||||||
|
runGradle(DeployedPlugin.GENERATE_POM_TASK_NAME, "-s");
|
||||||
|
File generatedPomXml = new File(this.projectDir, "build/publications/maven/pom-default.xml");
|
||||||
|
try (Reader reader = new FileReader(generatedPomXml)) {
|
||||||
|
System.out.println(FileCopyUtils.copyToString(reader));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
assertThat(generatedPomXml).isFile();
|
||||||
|
consumer.accept(new NodeAssert(generatedPomXml));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ArtifactVersionDependencyVersion}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ArtifactVersionDependencyVersionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenVersionIsNotAMavenVersionShouldReturnNull() {
|
||||||
|
assertThat(version("1.2.3.1")).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenVersionIsAMavenVersionShouldReturnAVersion() {
|
||||||
|
assertThat(version("1.2.3")).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenInputIsOlderMajorShouldReturnTrue() {
|
||||||
|
assertThat(version("2.1.2").isNewerThan(version("1.9.0"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenInputIsOlderMinorShouldReturnTrue() {
|
||||||
|
assertThat(version("2.1.2").isNewerThan(version("2.0.2"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenInputIsOlderPatchShouldReturnTrue() {
|
||||||
|
assertThat(version("2.1.2").isNewerThan(version("2.1.1"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenInputIsNewerMajorShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isNewerThan(version("3.2.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenMinorIsOlderShouldReturnTrue() {
|
||||||
|
assertThat(version("1.10.2").isSameMajorAndNewerThan(version("1.9.0"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenMajorIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("2.0.2").isSameMajorAndNewerThan(version("1.9.0"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenPatchIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("2.1.2").isSameMajorAndNewerThan(version("2.1.1"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenMinorIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMajorAndNewerThan(version("2.2.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenMajorIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMajorAndNewerThan(version("3.0.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenPatchIsOlderShouldReturnTrue() {
|
||||||
|
assertThat(version("1.10.2").isSameMinorAndNewerThan(version("1.10.1"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenMinorIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenVersionsAreTheSameShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.1.2"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenPatchIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.1.3"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenMinorIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenMajorIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("3.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArtifactVersionDependencyVersion version(String version) {
|
||||||
|
return ArtifactVersionDependencyVersion.parse(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DependencyVersion}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class DependencyVersionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenValidMavenVersionShouldReturnArtifactVersionDependencyVersion() {
|
||||||
|
assertThat(DependencyVersion.parse("1.2.3.Final")).isInstanceOf(ArtifactVersionDependencyVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenReleaseTrainShouldReturnReleaseTrainDependencyVersion() {
|
||||||
|
assertThat(DependencyVersion.parse("Ingalls-SR5")).isInstanceOf(ReleaseTrainDependencyVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenMavenLikeVersionWithNumericQualifieShouldReturnNumericQualifierDependencyVersion() {
|
||||||
|
assertThat(DependencyVersion.parse("1.2.3.4")).isInstanceOf(NumericQualifierDependencyVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenVersionWithLeadingZeroesShouldReturnLeadingZeroesDependencyVersion() {
|
||||||
|
assertThat(DependencyVersion.parse("1.4.01")).isInstanceOf(LeadingZeroesDependencyVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseWhenVersionWithCombinedPatchAndQualifierShouldReturnCombinedPatchAndQualifierDependencyVersion() {
|
||||||
|
assertThat(DependencyVersion.parse("4.0.0M4")).isInstanceOf(CombinedPatchAndQualifierDependencyVersion.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NumericQualifierDependencyVersion}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class NumericQualifierDependencyVersionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanOnVersionWithNumericQualifierWhenInputHasNoQualifierShouldReturnTrue() {
|
||||||
|
assertThat(version("2.9.9.20190806").isNewerThan(DependencyVersion.parse("2.9.9"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanOnVersionWithNumericQualifierWhenInputHasOlderQualifierShouldReturnTrue() {
|
||||||
|
assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190805"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanOnVersionWithNumericQualifierWhenInputHasNewerQualifierShouldReturnFalse() {
|
||||||
|
assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190807"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanOnVersionWithNumericQualifierWhenInputHasSameQualifierShouldReturnFalse() {
|
||||||
|
assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190806"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private NumericQualifierDependencyVersion version(String version) {
|
||||||
|
return NumericQualifierDependencyVersion.parse(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.bom.bomr.version;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ReleaseTrainDependencyVersion}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ReleaseTrainDependencyVersionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parsingOfANonReleaseTrainVersionReturnsNull() {
|
||||||
|
assertThat(version("5.1.4.RELEASE")).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parsingOfAReleaseTrainVersionReturnsVersion() {
|
||||||
|
assertThat(version("Lovelace-SR3")).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenReleaseTrainIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("Lovelace-RELEASE").isNewerThan(version("Kay-SR5"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenVersionIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("Kay-SR10").isNewerThan(version("Kay-SR5"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenVersionIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("Kay-RELEASE").isNewerThan(version("Kay-SR5"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isNewerThanWhenReleaseTrainIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("Ingalls-RELEASE").isNewerThan(version("Kay-SR5"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerWhenWhenReleaseTrainIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("Lovelace-RELEASE").isSameMajorAndNewerThan(version("Kay-SR5"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenReleaseTrainIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("Ingalls-RELEASE").isSameMajorAndNewerThan(version("Kay-SR5"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMajorAndNewerThanWhenVersionIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("Kay-SR6").isSameMajorAndNewerThan(version("Kay-SR5"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenReleaseTrainIsNewerShouldReturnFalse() {
|
||||||
|
assertThat(version("Lovelace-RELEASE").isSameMinorAndNewerThan(version("Kay-SR5"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenReleaseTrainIsTheSameAndVersionIsNewerShouldReturnTrue() {
|
||||||
|
assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR5"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenReleaseTrainAndVersionAreTheSameShouldReturnFalse() {
|
||||||
|
assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR6"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isSameMinorAndNewerThanWhenReleaseTrainIsTheSameAndVersionIsOlderShouldReturnFalse() {
|
||||||
|
assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR7"))).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReleaseTrainDependencyVersion version(String input) {
|
||||||
|
return ReleaseTrainDependencyVersion.parse(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link CompoundConfigurationTableEntry}.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
public class CompoundConfigurationTableEntryTests {
|
||||||
|
|
||||||
|
private static String NEWLINE = System.lineSeparator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void simpleProperty() {
|
||||||
|
ConfigurationProperty firstProp = new ConfigurationProperty("spring.test.first", "java.lang.String");
|
||||||
|
ConfigurationProperty secondProp = new ConfigurationProperty("spring.test.second", "java.lang.String");
|
||||||
|
ConfigurationProperty thirdProp = new ConfigurationProperty("spring.test.third", "java.lang.String");
|
||||||
|
CompoundConfigurationTableEntry entry = new CompoundConfigurationTableEntry("spring.test",
|
||||||
|
"This is a description.");
|
||||||
|
entry.addConfigurationKeys(firstProp, secondProp, thirdProp);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo(
|
||||||
|
"|`+spring.test.first+` +" + NEWLINE + "`+spring.test.second+` +" + NEWLINE + "`+spring.test.third+` +"
|
||||||
|
+ NEWLINE + NEWLINE + "|" + NEWLINE + "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ConfigurationTable}.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
public class ConfigurationTableTests {
|
||||||
|
|
||||||
|
private static String NEWLINE = System.lineSeparator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void simpleTable() {
|
||||||
|
ConfigurationTable table = new ConfigurationTable("test");
|
||||||
|
ConfigurationProperty first = new ConfigurationProperty("spring.test.prop", "java.lang.String", "something",
|
||||||
|
"This is a description.", false);
|
||||||
|
ConfigurationProperty second = new ConfigurationProperty("spring.test.other", "java.lang.String", "other value",
|
||||||
|
"This is another description.", false);
|
||||||
|
table.addEntry(new SingleConfigurationTableEntry(first));
|
||||||
|
table.addEntry(new SingleConfigurationTableEntry(second));
|
||||||
|
assertThat(table.toAsciidocTable()).isEqualTo("[cols=\"1,1,2\", options=\"header\"]" + NEWLINE + "|==="
|
||||||
|
+ NEWLINE + "|Key|Default Value|Description" + NEWLINE + NEWLINE + "|`+spring.test.other+`" + NEWLINE
|
||||||
|
+ "|`+other value+`" + NEWLINE + "|+++This is another description.+++" + NEWLINE + NEWLINE
|
||||||
|
+ "|`+spring.test.prop+`" + NEWLINE + "|`+something+`" + NEWLINE + "|+++This is a description.+++"
|
||||||
|
+ NEWLINE + NEWLINE + "|===" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.context.properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SingleConfigurationTableEntry}.
|
||||||
|
*
|
||||||
|
* @author Brian Clozel
|
||||||
|
*/
|
||||||
|
public class SingleConfigurationTableEntryTests {
|
||||||
|
|
||||||
|
private static String NEWLINE = System.lineSeparator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void simpleProperty() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", "something",
|
||||||
|
"This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo("|`+spring.test.prop+`" + NEWLINE + "|`+something+`" + NEWLINE
|
||||||
|
+ "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void noDefaultValue() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
|
||||||
|
"This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo(
|
||||||
|
"|`+spring.test.prop+`" + NEWLINE + "|" + NEWLINE + "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultValueWithPipes() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
|
||||||
|
"first|second", "This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo("|`+spring.test.prop+`" + NEWLINE + "|`+first\\|second+`" + NEWLINE
|
||||||
|
+ "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultValueWithBackslash() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
|
||||||
|
"first\\second", "This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo("|`+spring.test.prop+`" + NEWLINE + "|`+first\\\\second+`" + NEWLINE
|
||||||
|
+ "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void descriptionWithPipe() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
|
||||||
|
"This is a description with a | pipe.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo("|`+spring.test.prop+`" + NEWLINE + "|" + NEWLINE
|
||||||
|
+ "|+++This is a description with a \\| pipe.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mapProperty() {
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
|
||||||
|
"java.util.Map<java.lang.String,java.lang.String>", null, "This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo(
|
||||||
|
"|`+spring.test.prop.*+`" + NEWLINE + "|" + NEWLINE + "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listProperty() {
|
||||||
|
String[] defaultValue = new String[] { "first", "second", "third" };
|
||||||
|
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
|
||||||
|
"java.util.List<java.lang.String>", defaultValue, "This is a description.", false);
|
||||||
|
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
|
||||||
|
AsciidocBuilder builder = new AsciidocBuilder();
|
||||||
|
entry.write(builder);
|
||||||
|
assertThat(builder.toString()).isEqualTo("|`+spring.test.prop+`" + NEWLINE + "|`+first," + NEWLINE + "second,"
|
||||||
|
+ NEWLINE + "third+`" + NEWLINE + "|+++This is a description.+++" + NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.log4j2;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.processor.PluginCache;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ReproducibleLog4j2PluginsDatAction}
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ReproduciblePluginsDatActionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postProcessingOrdersCategoriesAndPlugins() throws IOException {
|
||||||
|
Path datFile = Files.createTempFile("Log4j2Plugins", "dat");
|
||||||
|
try {
|
||||||
|
write(datFile);
|
||||||
|
PluginCache cache = new PluginCache();
|
||||||
|
cache.loadCacheFiles(new Vector<>(Arrays.asList(datFile.toUri().toURL())).elements());
|
||||||
|
assertThat(cache.getAllCategories().keySet()).containsExactly("one", "two");
|
||||||
|
assertThat(cache.getCategory("one").keySet()).containsExactly("alpha", "bravo", "charlie");
|
||||||
|
assertThat(cache.getCategory("two").keySet()).containsExactly("delta", "echo", "foxtrot");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Files.delete(datFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(Path datFile) throws IOException {
|
||||||
|
PluginCache cache = new PluginCache();
|
||||||
|
createCategory(cache, "two", Arrays.asList("delta", "foxtrot", "echo"));
|
||||||
|
createCategory(cache, "one", Arrays.asList("bravo", "alpha", "charlie"));
|
||||||
|
try (OutputStream output = new FileOutputStream(datFile.toFile())) {
|
||||||
|
cache.writeCache(output);
|
||||||
|
new ReproducibleLog4j2PluginsDatAction().postProcess(datFile.toFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCategory(PluginCache cache, String categoryName, List<String> entryNames) {
|
||||||
|
Map<String, PluginEntry> category = cache.getCategory(categoryName);
|
||||||
|
for (String entryName : entryNames) {
|
||||||
|
PluginEntry entry = new PluginEntry();
|
||||||
|
entry.setKey(entryName);
|
||||||
|
entry.setClassName("com.example.Plugin");
|
||||||
|
entry.setName("name");
|
||||||
|
entry.setCategory(categoryName);
|
||||||
|
category.put(entryName, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.mavenplugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.mavenplugin.PluginXmlParser.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link PluginXmlParser}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class PluginXmlParserTests {
|
||||||
|
|
||||||
|
private final PluginXmlParser parser = new PluginXmlParser();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dunno() {
|
||||||
|
Plugin plugin = this.parser.parse(new File("src/test/resources/plugin.xml"));
|
||||||
|
System.out.println(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.optional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import org.gradle.testkit.runner.BuildResult;
|
||||||
|
import org.gradle.testkit.runner.GradleRunner;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link OptionalDependenciesPlugin}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class OptionalDependenciesPluginIntegrationTests {
|
||||||
|
|
||||||
|
private File projectDir;
|
||||||
|
|
||||||
|
private File buildFile;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup(@TempDir File projectDir) throws IOException {
|
||||||
|
this.projectDir = projectDir;
|
||||||
|
this.buildFile = new File(this.projectDir, "build.gradle");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void optionalConfigurationIsCreated() throws IOException {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins { id 'org.springframework.boot.optional-dependencies' }");
|
||||||
|
out.println("task printConfigurations {");
|
||||||
|
out.println(" doLast {");
|
||||||
|
out.println(" configurations.all { println it.name }");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
BuildResult buildResult = runGradle("printConfigurations");
|
||||||
|
assertThat(buildResult.getOutput()).contains("optional");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void optionalDependenciesAreAddedToMainSourceSetsCompileClasspath() throws IOException {
|
||||||
|
optionalDependenciesAreAddedToSourceSetClasspath("main", "compileClasspath");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void optionalDependenciesAreAddedToMainSourceSetsRuntimeClasspath() throws IOException {
|
||||||
|
optionalDependenciesAreAddedToSourceSetClasspath("main", "runtimeClasspath");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void optionalDependenciesAreAddedToTestSourceSetsCompileClasspath() throws IOException {
|
||||||
|
optionalDependenciesAreAddedToSourceSetClasspath("test", "compileClasspath");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void optionalDependenciesAreAddedToTestSourceSetsRuntimeClasspath() throws IOException {
|
||||||
|
optionalDependenciesAreAddedToSourceSetClasspath("test", "runtimeClasspath");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void optionalDependenciesAreAddedToSourceSetClasspath(String sourceSet, String classpath)
|
||||||
|
throws IOException {
|
||||||
|
try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
out.println("plugins {");
|
||||||
|
out.println(" id 'org.springframework.boot.optional-dependencies'");
|
||||||
|
out.println(" id 'java'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("repositories {");
|
||||||
|
out.println(" mavenCentral()");
|
||||||
|
out.println("}");
|
||||||
|
out.println("dependencies {");
|
||||||
|
out.println(" optional 'org.springframework:spring-jcl:5.1.2.RELEASE'");
|
||||||
|
out.println("}");
|
||||||
|
out.println("task printClasspath {");
|
||||||
|
out.println(" doLast {");
|
||||||
|
out.println(" println sourceSets." + sourceSet + "." + classpath + ".files");
|
||||||
|
out.println(" }");
|
||||||
|
out.println("}");
|
||||||
|
}
|
||||||
|
BuildResult buildResult = runGradle("printClasspath");
|
||||||
|
assertThat(buildResult.getOutput()).contains("spring-jcl");
|
||||||
|
}
|
||||||
|
|
||||||
|
private BuildResult runGradle(String... args) {
|
||||||
|
return GradleRunner.create().withProjectDir(this.projectDir).withArguments(args).withPluginClasspath().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,911 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- Generated by maven-plugin-tools 3.6 -->
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<name>Spring Boot Maven Plugin</name>
|
||||||
|
<description></description>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>2.2.0.GRADLE-SNAPSHOT</version>
|
||||||
|
<goalPrefix>spring-boot</goalPrefix>
|
||||||
|
<isolatedRealm>false</isolatedRealm>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<mojos>
|
||||||
|
<mojo>
|
||||||
|
<goal>build-info</goal>
|
||||||
|
<description>Generate a {@code build-info.properties} file based the content of the current
|
||||||
|
{@link MavenProject}.</description>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>true</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<implementation>org.springframework.boot.maven.BuildInfoMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<since>1.4.0</since>
|
||||||
|
<threadSafe>true</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>additionalProperties</name>
|
||||||
|
<type>java.util.Map</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Additional properties to store in the build-info.properties. Each entry is prefixed
|
||||||
|
by {@code build.} in the generated build-info.properties.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>outputFile</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The location of the generated build-info.properties.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>project</name>
|
||||||
|
<type>org.apache.maven.project.MavenProject</type>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven project.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>session</name>
|
||||||
|
<type>org.apache.maven.execution.MavenSession</type>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven session.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>time</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>2.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The value used for the {@code build.time} property in a form suitable for
|
||||||
|
{@link Instant#parse(CharSequence)}. Defaults to {@code session.request.startTime}.
|
||||||
|
To disable the {@code build.time} property entirely, use {@code 'off'}.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<outputFile implementation="java.io.File" default-value="${project.build.outputDirectory}/META-INF/build-info.properties"/>
|
||||||
|
<project implementation="org.apache.maven.project.MavenProject" default-value="${project}"/>
|
||||||
|
<session implementation="org.apache.maven.execution.MavenSession" default-value="${session}"/>
|
||||||
|
</configuration>
|
||||||
|
<requirements>
|
||||||
|
<requirement>
|
||||||
|
<role>org.sonatype.plexus.build.incremental.BuildContext</role>
|
||||||
|
<field-name>buildContext</field-name>
|
||||||
|
</requirement>
|
||||||
|
</requirements>
|
||||||
|
</mojo>
|
||||||
|
<mojo>
|
||||||
|
<goal>help</goal>
|
||||||
|
<description>Display help information on spring-boot-maven-plugin.<br>
|
||||||
|
Call <code>mvn spring-boot:help -Ddetail=true -Dgoal=&lt;goal-name&gt;</code> to display parameter details.</description>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>false</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<implementation>org.springframework.boot.maven.HelpMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<threadSafe>true</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>detail</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>If <code>true</code>, display all settable properties for each goal.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>goal</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The name of the goal for which to show help. If unspecified, all goals will be displayed.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>indentSize</name>
|
||||||
|
<type>int</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The number of spaces per indentation level, should be positive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>lineLength</name>
|
||||||
|
<type>int</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The maximum length of a display line, should be positive.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<detail implementation="boolean" default-value="false">${detail}</detail>
|
||||||
|
<goal implementation="java.lang.String">${goal}</goal>
|
||||||
|
<indentSize implementation="int" default-value="2">${indentSize}</indentSize>
|
||||||
|
<lineLength implementation="int" default-value="80">${lineLength}</lineLength>
|
||||||
|
</configuration>
|
||||||
|
</mojo>
|
||||||
|
<mojo>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
<description>Repackages existing JAR and WAR archives so that they can be executed from the command
|
||||||
|
line using {@literal java -jar}. With <code>layout=NONE</code> can also be used simply
|
||||||
|
to package a JAR with nested dependencies (and no main class, so not executable).</description>
|
||||||
|
<requiresDependencyResolution>compile+runtime</requiresDependencyResolution>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>true</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<phase>package</phase>
|
||||||
|
<implementation>org.springframework.boot.maven.RepackageMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<requiresDependencyCollection>compile+runtime</requiresDependencyCollection>
|
||||||
|
<threadSafe>true</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>attach</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.4.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Attach the repackaged archive to be installed and deployed.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>classifier</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Classifier to add to the repackaged archive. If not given, the main artifact will
|
||||||
|
be replaced by the repackaged archive. If given, the classifier will also be used
|
||||||
|
to determine the source archive to repackage: if an artifact with that classifier
|
||||||
|
already exists, it will be used as source and replaced. If no such artifact exists,
|
||||||
|
the main artifact will be used as source and the repackaged archive will be
|
||||||
|
attached as a supplemental artifact with that classifier. Attaching the artifact
|
||||||
|
allows to deploy it alongside to the original one, see <a href=
|
||||||
|
"https://maven.apache.org/plugins/maven-deploy-plugin/examples/deploying-with-classifiers.html"
|
||||||
|
>the Maven documentation for more details</a>.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>embeddedLaunchScript</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The embedded launch script to prepend to the front of the jar if it is fully
|
||||||
|
executable. If not specified the 'Spring Boot' default script will be used.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>embeddedLaunchScriptProperties</name>
|
||||||
|
<type>java.util.Properties</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Properties that should be expanded in the embedded launch script.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludeDevtools</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Exclude Spring Boot devtools from the repackaged archive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludeGroupIds</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Comma separated list of groupId names to exclude (exact match).</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to exclude. The {@link Exclude} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>executable</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Make a fully executable jar for *nix machines by prepending a launch script to the
|
||||||
|
jar.
|
||||||
|
<p>
|
||||||
|
Currently, some tools do not accept this format so you may not always be able to
|
||||||
|
use this technique. For example, {@code jar -xf} may silently fail to extract a jar
|
||||||
|
or war that has been made fully-executable. It is recommended that you only enable
|
||||||
|
this option if you intend to execute it directly, rather than running it with
|
||||||
|
{@code java -jar} or deploying it to a servlet container.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>finalName</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>Name of the generated archive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>includeSystemScope</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.4.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Include system scoped dependencies.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>includes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to include. The {@link Include} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>layout</name>
|
||||||
|
<type>org.springframework.boot.maven.RepackageMojo$LayoutType</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The type of archive (which corresponds to how the dependencies are laid out inside
|
||||||
|
it). Possible values are JAR, WAR, ZIP, DIR, NONE. Defaults to a guess based on the
|
||||||
|
archive type.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>layoutFactory</name>
|
||||||
|
<type>org.springframework.boot.loader.tools.LayoutFactory</type>
|
||||||
|
<since>1.5.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>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
|
||||||
|
parties.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>mainClass</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The name of the main class. If not specified the first compiled class found that
|
||||||
|
contains a 'main' method will be used.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>outputDirectory</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Directory containing the generated archive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>project</name>
|
||||||
|
<type>org.apache.maven.project.MavenProject</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven project.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>requiresUnpack</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>A list of the libraries that must be unpacked from fat jars in order to run.
|
||||||
|
Specify each library as a {@code <dependency>} with a {@code <groupId>} and a
|
||||||
|
{@code <artifactId>} and they will be unpacked at runtime.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>skip</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Skip the execution.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<attach implementation="boolean" default-value="true"/>
|
||||||
|
<excludeDevtools implementation="boolean" default-value="true">${spring-boot.repackage.excludeDevtools}</excludeDevtools>
|
||||||
|
<excludeGroupIds implementation="java.lang.String" default-value="">${spring-boot.excludeGroupIds}</excludeGroupIds>
|
||||||
|
<excludes implementation="java.util.List">${spring-boot.excludes}</excludes>
|
||||||
|
<executable implementation="boolean" default-value="false"/>
|
||||||
|
<finalName implementation="java.lang.String" default-value="${project.build.finalName}"/>
|
||||||
|
<includeSystemScope implementation="boolean" default-value="false"/>
|
||||||
|
<includes implementation="java.util.List">${spring-boot.includes}</includes>
|
||||||
|
<layout implementation="org.springframework.boot.maven.RepackageMojo$LayoutType">${spring-boot.repackage.layout}</layout>
|
||||||
|
<outputDirectory implementation="java.io.File" default-value="${project.build.directory}"/>
|
||||||
|
<project implementation="org.apache.maven.project.MavenProject" default-value="${project}"/>
|
||||||
|
<skip implementation="boolean" default-value="false">${spring-boot.repackage.skip}</skip>
|
||||||
|
</configuration>
|
||||||
|
<requirements>
|
||||||
|
<requirement>
|
||||||
|
<role>org.apache.maven.project.MavenProjectHelper</role>
|
||||||
|
<field-name>projectHelper</field-name>
|
||||||
|
</requirement>
|
||||||
|
</requirements>
|
||||||
|
</mojo>
|
||||||
|
<mojo>
|
||||||
|
<goal>run</goal>
|
||||||
|
<description>Run an executable archive application.</description>
|
||||||
|
<requiresDependencyResolution>test</requiresDependencyResolution>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>true</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<executePhase>test-compile</executePhase>
|
||||||
|
<implementation>org.springframework.boot.maven.RunMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<threadSafe>false</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>addResources</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Add maven resources to the classpath directly, this allows live in-place editing of
|
||||||
|
resources. Duplicate resources are removed from {@code target/classes} to prevent
|
||||||
|
them to appear twice if {@code ClassLoader.getResources()} is called. Please
|
||||||
|
consider adding {@code spring-boot-devtools} to your project instead as it provides
|
||||||
|
this feature and many more.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>agent</name>
|
||||||
|
<type>java.io.File[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<deprecated>since 2.2.0 in favor of {@code agents}</deprecated>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Path to agent jar. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>agents</name>
|
||||||
|
<type>java.io.File[]</type>
|
||||||
|
<since>2.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Path to agent jars. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>arguments</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Arguments that should be passed to the application. On command line use commas to
|
||||||
|
separate multiple arguments.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>classesDirectory</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Directory containing the classes and resource files that should be packaged into
|
||||||
|
the archive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>environmentVariables</name>
|
||||||
|
<type>java.util.Map</type>
|
||||||
|
<since>2.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>List of Environment variables that should be associated with the forked process
|
||||||
|
used to run the application. NOTE: a forked process is required to use this
|
||||||
|
feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludeGroupIds</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Comma separated list of groupId names to exclude (exact match).</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to exclude. The {@link Exclude} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>folders</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Additional folders besides the classes directory that should be added to the
|
||||||
|
classpath.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>fork</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to indicate if the run processes should be forked. Disabling forking will
|
||||||
|
disable some features such as an agent, custom JVM arguments, devtools or
|
||||||
|
specifying the working directory to use.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>includes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to include. The {@link Include} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jvmArguments</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>JVM arguments that should be associated with the forked process used to run the
|
||||||
|
application. On command line, make sure to wrap multiple values between quotes.
|
||||||
|
NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>mainClass</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The name of the main class. If not specified the first compiled class found that
|
||||||
|
contains a 'main' method will be used.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>noverify</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to say that the agent requires -noverify.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>optimizedLaunch</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>2.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Whether the JVM's launch should be optimized.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>profiles</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The spring profiles to activate. Convenience shortcut of specifying the
|
||||||
|
'spring.profiles.active' argument. On command line use commas to separate multiple
|
||||||
|
profiles.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>project</name>
|
||||||
|
<type>org.apache.maven.project.MavenProject</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven project.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>skip</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.3.2</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Skip the execution.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>systemPropertyVariables</name>
|
||||||
|
<type>java.util.Map</type>
|
||||||
|
<since>2.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>List of JVM system properties to pass to the process. NOTE: a forked process is
|
||||||
|
required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>useTestClasspath</name>
|
||||||
|
<type>java.lang.Boolean</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to include the test classpath when running.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>workingDirectory</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.5.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Current working directory to use for the application. If not specified, basedir
|
||||||
|
will be used. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<addResources implementation="boolean" default-value="false">${spring-boot.run.addResources}</addResources>
|
||||||
|
<agent implementation="java.io.File[]">${spring-boot.run.agent}</agent>
|
||||||
|
<agents implementation="java.io.File[]">${spring-boot.run.agents}</agents>
|
||||||
|
<arguments implementation="java.lang.String[]">${spring-boot.run.arguments}</arguments>
|
||||||
|
<classesDirectory implementation="java.io.File" default-value="${project.build.outputDirectory}"/>
|
||||||
|
<excludeGroupIds implementation="java.lang.String" default-value="">${spring-boot.excludeGroupIds}</excludeGroupIds>
|
||||||
|
<excludes implementation="java.util.List">${spring-boot.excludes}</excludes>
|
||||||
|
<folders implementation="java.lang.String[]">${spring-boot.run.folders}</folders>
|
||||||
|
<fork implementation="boolean" default-value="true">${spring-boot.run.fork}</fork>
|
||||||
|
<includes implementation="java.util.List">${spring-boot.includes}</includes>
|
||||||
|
<jvmArguments implementation="java.lang.String">${spring-boot.run.jvmArguments}</jvmArguments>
|
||||||
|
<mainClass implementation="java.lang.String">${spring-boot.run.main-class}</mainClass>
|
||||||
|
<noverify implementation="boolean">${spring-boot.run.noverify}</noverify>
|
||||||
|
<optimizedLaunch implementation="boolean" default-value="true">${spring-boot.run.optimizedLaunch}</optimizedLaunch>
|
||||||
|
<profiles implementation="java.lang.String[]">${spring-boot.run.profiles}</profiles>
|
||||||
|
<project implementation="org.apache.maven.project.MavenProject" default-value="${project}"/>
|
||||||
|
<skip implementation="boolean" default-value="false">${spring-boot.run.skip}</skip>
|
||||||
|
<useTestClasspath implementation="java.lang.Boolean" default-value="false">${spring-boot.run.useTestClasspath}</useTestClasspath>
|
||||||
|
<workingDirectory implementation="java.io.File">${spring-boot.run.workingDirectory}</workingDirectory>
|
||||||
|
</configuration>
|
||||||
|
</mojo>
|
||||||
|
<mojo>
|
||||||
|
<goal>start</goal>
|
||||||
|
<description>Start a spring application. Contrary to the {@code run} goal, this does not block and
|
||||||
|
allows other goal to operate on the application. This goal is typically used in
|
||||||
|
integration test scenario where the application is started before a test suite and
|
||||||
|
stopped after.</description>
|
||||||
|
<requiresDependencyResolution>test</requiresDependencyResolution>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>true</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<phase>pre-integration-test</phase>
|
||||||
|
<implementation>org.springframework.boot.maven.StartMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<threadSafe>false</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>addResources</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Add maven resources to the classpath directly, this allows live in-place editing of
|
||||||
|
resources. Duplicate resources are removed from {@code target/classes} to prevent
|
||||||
|
them to appear twice if {@code ClassLoader.getResources()} is called. Please
|
||||||
|
consider adding {@code spring-boot-devtools} to your project instead as it provides
|
||||||
|
this feature and many more.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>agent</name>
|
||||||
|
<type>java.io.File[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<deprecated>since 2.2.0 in favor of {@code agents}</deprecated>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Path to agent jar. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>agents</name>
|
||||||
|
<type>java.io.File[]</type>
|
||||||
|
<since>2.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Path to agent jars. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>arguments</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Arguments that should be passed to the application. On command line use commas to
|
||||||
|
separate multiple arguments.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>classesDirectory</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Directory containing the classes and resource files that should be packaged into
|
||||||
|
the archive.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>environmentVariables</name>
|
||||||
|
<type>java.util.Map</type>
|
||||||
|
<since>2.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>List of Environment variables that should be associated with the forked process
|
||||||
|
used to run the application. NOTE: a forked process is required to use this
|
||||||
|
feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludeGroupIds</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Comma separated list of groupId names to exclude (exact match).</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>excludes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to exclude. The {@link Exclude} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>folders</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Additional folders besides the classes directory that should be added to the
|
||||||
|
classpath.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>fork</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to indicate if the run processes should be forked. Disabling forking will
|
||||||
|
disable some features such as an agent, custom JVM arguments, devtools or
|
||||||
|
specifying the working directory to use.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>includes</name>
|
||||||
|
<type>java.util.List</type>
|
||||||
|
<since>1.2.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Collection of artifact definitions to include. The {@link Include} element defines
|
||||||
|
a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||||
|
{@code classifier} property.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jmxName</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The JMX name of the automatically deployed MBean managing the lifecycle of the
|
||||||
|
spring application.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jmxPort</name>
|
||||||
|
<type>int</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The port to use to expose the platform MBeanServer if the application is forked.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jvmArguments</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>JVM arguments that should be associated with the forked process used to run the
|
||||||
|
application. On command line, make sure to wrap multiple values between quotes.
|
||||||
|
NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>mainClass</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The name of the main class. If not specified the first compiled class found that
|
||||||
|
contains a 'main' method will be used.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>maxAttempts</name>
|
||||||
|
<type>int</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The maximum number of attempts to check if the spring application is ready.
|
||||||
|
Combined with the "wait" argument, this gives a global timeout value (30 sec by
|
||||||
|
default)</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>noverify</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to say that the agent requires -noverify.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>profiles</name>
|
||||||
|
<type>java.lang.String[]</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The spring profiles to activate. Convenience shortcut of specifying the
|
||||||
|
'spring.profiles.active' argument. On command line use commas to separate multiple
|
||||||
|
profiles.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>project</name>
|
||||||
|
<type>org.apache.maven.project.MavenProject</type>
|
||||||
|
<since>1.0.0</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven project.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>skip</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.3.2</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Skip the execution.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>systemPropertyVariables</name>
|
||||||
|
<type>java.util.Map</type>
|
||||||
|
<since>2.1.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>List of JVM system properties to pass to the process. NOTE: a forked process is
|
||||||
|
required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>useTestClasspath</name>
|
||||||
|
<type>java.lang.Boolean</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to include the test classpath when running.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>wait</name>
|
||||||
|
<type>long</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The number of milli-seconds to wait between each attempt to check if the spring
|
||||||
|
application is ready.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>workingDirectory</name>
|
||||||
|
<type>java.io.File</type>
|
||||||
|
<since>1.5.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Current working directory to use for the application. If not specified, basedir
|
||||||
|
will be used. NOTE: a forked process is required to use this feature.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<addResources implementation="boolean" default-value="false">${spring-boot.run.addResources}</addResources>
|
||||||
|
<agent implementation="java.io.File[]">${spring-boot.run.agent}</agent>
|
||||||
|
<agents implementation="java.io.File[]">${spring-boot.run.agents}</agents>
|
||||||
|
<arguments implementation="java.lang.String[]">${spring-boot.run.arguments}</arguments>
|
||||||
|
<classesDirectory implementation="java.io.File" default-value="${project.build.outputDirectory}"/>
|
||||||
|
<excludeGroupIds implementation="java.lang.String" default-value="">${spring-boot.excludeGroupIds}</excludeGroupIds>
|
||||||
|
<excludes implementation="java.util.List">${spring-boot.excludes}</excludes>
|
||||||
|
<folders implementation="java.lang.String[]">${spring-boot.run.folders}</folders>
|
||||||
|
<fork implementation="boolean" default-value="true">${spring-boot.run.fork}</fork>
|
||||||
|
<includes implementation="java.util.List">${spring-boot.includes}</includes>
|
||||||
|
<jvmArguments implementation="java.lang.String">${spring-boot.run.jvmArguments}</jvmArguments>
|
||||||
|
<mainClass implementation="java.lang.String">${spring-boot.run.main-class}</mainClass>
|
||||||
|
<noverify implementation="boolean">${spring-boot.run.noverify}</noverify>
|
||||||
|
<profiles implementation="java.lang.String[]">${spring-boot.run.profiles}</profiles>
|
||||||
|
<project implementation="org.apache.maven.project.MavenProject" default-value="${project}"/>
|
||||||
|
<skip implementation="boolean" default-value="false">${spring-boot.run.skip}</skip>
|
||||||
|
<useTestClasspath implementation="java.lang.Boolean" default-value="false">${spring-boot.run.useTestClasspath}</useTestClasspath>
|
||||||
|
<workingDirectory implementation="java.io.File">${spring-boot.run.workingDirectory}</workingDirectory>
|
||||||
|
</configuration>
|
||||||
|
</mojo>
|
||||||
|
<mojo>
|
||||||
|
<goal>stop</goal>
|
||||||
|
<description>Stop a spring application that has been started by the "start" goal. Typically invoked
|
||||||
|
once a test suite has completed.</description>
|
||||||
|
<requiresDirectInvocation>false</requiresDirectInvocation>
|
||||||
|
<requiresProject>true</requiresProject>
|
||||||
|
<requiresReports>false</requiresReports>
|
||||||
|
<aggregator>false</aggregator>
|
||||||
|
<requiresOnline>false</requiresOnline>
|
||||||
|
<inheritedByDefault>true</inheritedByDefault>
|
||||||
|
<phase>post-integration-test</phase>
|
||||||
|
<implementation>org.springframework.boot.maven.StopMojo</implementation>
|
||||||
|
<language>java</language>
|
||||||
|
<instantiationStrategy>per-lookup</instantiationStrategy>
|
||||||
|
<executionStrategy>once-per-session</executionStrategy>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<threadSafe>false</threadSafe>
|
||||||
|
<parameters>
|
||||||
|
<parameter>
|
||||||
|
<name>fork</name>
|
||||||
|
<type>java.lang.Boolean</type>
|
||||||
|
<since>1.3.0</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Flag to indicate if process to stop was forked. By default, the value is inherited
|
||||||
|
from the {@link MavenProject}. If it is set, it must match the value used to
|
||||||
|
{@link StartMojo start} the process.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jmxName</name>
|
||||||
|
<type>java.lang.String</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The JMX name of the automatically deployed MBean managing the lifecycle of the
|
||||||
|
application.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>jmxPort</name>
|
||||||
|
<type>int</type>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>The port to use to lookup the platform MBeanServer if the application has been
|
||||||
|
forked.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>project</name>
|
||||||
|
<type>org.apache.maven.project.MavenProject</type>
|
||||||
|
<since>1.4.1</since>
|
||||||
|
<required>true</required>
|
||||||
|
<editable>false</editable>
|
||||||
|
<description>The Maven project.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<name>skip</name>
|
||||||
|
<type>boolean</type>
|
||||||
|
<since>1.3.2</since>
|
||||||
|
<required>false</required>
|
||||||
|
<editable>true</editable>
|
||||||
|
<description>Skip the execution.</description>
|
||||||
|
</parameter>
|
||||||
|
</parameters>
|
||||||
|
<configuration>
|
||||||
|
<fork implementation="java.lang.Boolean">${spring-boot.stop.fork}</fork>
|
||||||
|
<project implementation="org.apache.maven.project.MavenProject" default-value="${project}"/>
|
||||||
|
<skip implementation="boolean" default-value="false">${spring-boot.stop.skip}</skip>
|
||||||
|
</configuration>
|
||||||
|
</mojo>
|
||||||
|
</mojos>
|
||||||
|
<dependencies/>
|
||||||
|
</plugin>
|
@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source $(dirname $0)/common.sh
|
|
||||||
repository=$(pwd)/distribution-repository
|
|
||||||
|
|
||||||
pushd git-repo > /dev/null
|
|
||||||
run_maven -f spring-boot-tests/spring-boot-deployment-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
|
|
||||||
popd > /dev/null
|
|
@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source $(dirname $0)/common.sh
|
|
||||||
repository=$(pwd)/distribution-repository
|
|
||||||
|
|
||||||
pushd git-repo > /dev/null
|
|
||||||
run_maven -f spring-boot-tests/spring-boot-integration-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
|
|
||||||
popd > /dev/null
|
|
@ -1,6 +1,4 @@
|
|||||||
SET "JAVA_HOME=C:\opt\jdk-8"
|
SET "JAVA_HOME=C:\opt\jdk-8"
|
||||||
SET PATH=%PATH%;C:\Program Files\Git\usr\bin
|
SET PATH=%PATH%;C:\Program Files\Git\usr\bin
|
||||||
cd git-repo
|
cd git-repo
|
||||||
|
.\gradlew --no-daemon --max-workers=4 build
|
||||||
echo ".\mvnw clean install" > build.log
|
|
||||||
.\mvnw clean install -U >> build.log 2>&1 || (sleep 1 && tail -n 3000 build.log && exit 1)
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source $(dirname $0)/common.sh
|
|
||||||
repository=$(pwd)/distribution-repository
|
|
||||||
|
|
||||||
pushd git-repo > /dev/null
|
|
||||||
run_maven -f spring-boot-tests/spring-boot-smoke-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
|
|
||||||
popd > /dev/null
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
platform: linux
|
|
||||||
inputs:
|
|
||||||
- name: git-repo
|
|
||||||
- name: distribution-repository
|
|
||||||
caches:
|
|
||||||
- path: maven
|
|
||||||
- path: gradle
|
|
||||||
run:
|
|
||||||
path: git-repo/ci/scripts/build-deployment-tests.sh
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
platform: linux
|
|
||||||
inputs:
|
|
||||||
- name: git-repo
|
|
||||||
- name: distribution-repository
|
|
||||||
caches:
|
|
||||||
- path: maven
|
|
||||||
- path: gradle
|
|
||||||
run:
|
|
||||||
path: git-repo/ci/scripts/build-integration-tests.sh
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
platform: linux
|
|
||||||
inputs:
|
|
||||||
- name: git-repo
|
|
||||||
- name: distribution-repository
|
|
||||||
caches:
|
|
||||||
- path: maven
|
|
||||||
- path: gradle
|
|
||||||
run:
|
|
||||||
path: git-repo/ci/scripts/build-smoke-tests.sh
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue