Compare commits
No commits in common. 'main' and '2.1.x' have entirely different histories.
@ -0,0 +1,40 @@
|
||||
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-minor-version
|
||||
prohibited:
|
||||
- project: dom4j
|
||||
versions:
|
||||
# Old versions that use yyyymmdd format
|
||||
- '[20040101,)'
|
||||
- project: glassfish-jaxb
|
||||
versions:
|
||||
# Switches to Jarkarta EE API dependencies resulting in duplicate classes
|
||||
- '[2.3.2,2.4)'
|
||||
- project: saaj-impl
|
||||
versions:
|
||||
# Switches to Jarkarta EE API dependencies resulting in duplicate classes
|
||||
- '[1.5.1,1.6)'
|
||||
- project: selenium-htmlunit
|
||||
versions:
|
||||
# Requires a new minor of Selenium which contains breaking API changes
|
||||
- '[2.33.1,2.34)'
|
||||
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:
|
||||
# Solr's Restlet dependencies
|
||||
- 'https://maven.restlet.com'
|
||||
# Caffeine Simulator's dependencies
|
||||
- 'https://maven.imagej.net/content/repositories/public/'
|
@ -1,7 +0,0 @@
|
||||
# .git-blame-ignore-revs
|
||||
# Reformat code following spring-javaformat upgrade
|
||||
df5898a1464112f185d295d585740de696934a12
|
||||
c4de86c244acdcff69ed0aecacd254399be79ce2
|
||||
b07269a018a4a9d4c029aba7dd8a15fa66df681c
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
name: Print JVM thread dumps
|
||||
description: Prints a thread dump for all running JVMs
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- run: |
|
||||
for java_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
|
||||
echo "------------------------ pid $java_pid ------------------------"
|
||||
cat /proc/$java_pid/cmdline | xargs -0 echo
|
||||
jcmd $java_pid Thread.print -l
|
||||
jcmd $java_pid GC.heap_info
|
||||
done
|
||||
exit 0
|
||||
shell: bash
|
@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
@ -1,43 +0,0 @@
|
||||
name: Build Pull Request
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build pull request
|
||||
runs-on: ubuntu22-8-32
|
||||
if: ${{ github.repository == 'spring-projects/spring-boot' }}
|
||||
steps:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --no-parallel --continue build
|
||||
|
||||
- name: Print JVM thread dumps when cancelled
|
||||
uses: ./.github/actions/print-jvm-thread-dumps
|
||||
if: cancelled()
|
||||
|
||||
- name: Upload build reports
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
@ -1,13 +0,0 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v1
|
@ -1,10 +0,0 @@
|
||||
.name
|
||||
*.xml
|
||||
/modules/
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -1,121 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="org.springframework" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="RIGHT_MARGIN" value="90" />
|
||||
<option name="ENABLE_JAVADOC_FORMATTING" value="false" />
|
||||
<GroovyCodeStyleSettings>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="org.springframework" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
</value>
|
||||
</option>
|
||||
</GroovyCodeStyleSettings>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="CLASS_NAMES_IN_JAVADOC" value="3" />
|
||||
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="500" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value />
|
||||
</option>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="org.springframework" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="ENABLE_JAVADOC_FORMATTING" value="false" />
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||
<option name="JD_KEEP_INVALID_TAGS" value="false" />
|
||||
<option name="JD_KEEP_EMPTY_LINES" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="false" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="20" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="20" />
|
||||
</JetCodeStyleSettings>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false" />
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||
<option name="BLANK_LINES_AROUND_FIELD" value="1" />
|
||||
<option name="BLANK_LINES_AROUND_FIELD_IN_INTERFACE" value="1" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
@ -1,6 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright 2012-&#36;today.year 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." />
|
||||
<option name="myName" value="java" />
|
||||
</copyright>
|
||||
</component>
|
@ -1,7 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings>
|
||||
<module2copyright>
|
||||
<element module="java" copyright="java" />
|
||||
</module2copyright>
|
||||
</settings>
|
||||
</component>
|
@ -1,17 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="LombokGetterMayBeUsed" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NullableProblems" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
|
||||
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
|
||||
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||
</profile>
|
||||
</component>
|
@ -1,3 +0,0 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="java" pattern="file:*.java&&!file:*package-info.java" />
|
||||
</component>
|
@ -0,0 +1,12 @@
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>com.gradle</groupId>
|
||||
<artifactId>gradle-enterprise-maven-extension</artifactId>
|
||||
<version>1.6.6</version>
|
||||
</extension>
|
||||
<extension>
|
||||
<groupId>io.spring.ge.conventions</groupId>
|
||||
<artifactId>gradle-enterprise-conventions-maven-extension</artifactId>
|
||||
<version>0.0.5</version>
|
||||
</extension>
|
||||
</extensions>
|
@ -0,0 +1,28 @@
|
||||
<gradleEnterprise
|
||||
xmlns="https://www.gradle.com/gradle-enterprise-maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://www.gradle.com/gradle-enterprise-maven https://www.gradle.com/schema/gradle-enterprise-maven.xsd">
|
||||
<server>
|
||||
<url>https://ge.spring.io</url>
|
||||
</server>
|
||||
<buildScan>
|
||||
<publishIfAuthenticated>true</publishIfAuthenticated>
|
||||
<obfuscation>
|
||||
<ipAddresses>#{{'0.0.0.0'}}</ipAddresses>
|
||||
</obfuscation>
|
||||
</buildScan>
|
||||
<buildCache>
|
||||
<local>
|
||||
<enabled>true</enabled>
|
||||
</local>
|
||||
<remote>
|
||||
<enabled>true</enabled>
|
||||
<storeEnabled><![CDATA[#{env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null && env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null}]]></storeEnabled>
|
||||
<server>
|
||||
<credentials>
|
||||
<username>${env.GRADLE_ENTERPRISE_CACHE_USERNAME}</username>
|
||||
<password>${env.GRADLE_ENTERPRISE_CACHE_PASSWORD}</password>
|
||||
</credentials>
|
||||
</server>
|
||||
</remote>
|
||||
</buildCache>
|
||||
</gradleEnterprise>
|
@ -0,0 +1 @@
|
||||
-Xmx2048m
|
Binary file not shown.
@ -0,0 +1,2 @@
|
||||
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
|
@ -1,3 +0,0 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.8.1-librca
|
@ -0,0 +1,160 @@
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>snapshot</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-ext</id>
|
||||
<url>https://repo.spring.io/ext-release-local/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<url>https://repository.jboss.org/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>rabbit-milestones</id>
|
||||
<name>Rabbit Milestones</name>
|
||||
<url>https://dl.bintray.com/rabbitmq/maven-milestones</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>milestone</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-ext</id>
|
||||
<url>https://repo.spring.io/ext-release-local/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<url>https://repository.jboss.org/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>rabbit-milestones</id>
|
||||
<name>Rabbit Milestones</name>
|
||||
<url>https://dl.bintray.com/rabbitmq/maven-milestones</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-ext</id>
|
||||
<url>https://repo.spring.io/ext-release-local/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<url>https://repository.jboss.org/nexus/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
<activeProfiles>
|
||||
<activeProfile>@profile@</activeProfile>
|
||||
</activeProfiles>
|
||||
<servers>
|
||||
<server>
|
||||
<id>central</id>
|
||||
<configuration>
|
||||
<timeout>120000</timeout>
|
||||
</configuration>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
@ -1,49 +0,0 @@
|
||||
plugins {
|
||||
id "base"
|
||||
id "org.jetbrains.kotlin.jvm" apply false // https://youtrack.jetbrains.com/issue/KT-30276
|
||||
id "io.spring.nohttp" version "0.0.11"
|
||||
}
|
||||
|
||||
description = "Spring Boot Build"
|
||||
|
||||
|
||||
|
||||
defaultTasks 'build'
|
||||
|
||||
nohttp {
|
||||
allowlistFile = project.file("src/nohttp/allowlist.lines")
|
||||
source.exclude "**/bin/**"
|
||||
source.exclude "**/build/**"
|
||||
source.exclude "**/out/**"
|
||||
source.exclude "**/target/**"
|
||||
source.exclude "**/.settings/**"
|
||||
source.exclude "**/.classpath"
|
||||
source.exclude "**/.project"
|
||||
source.exclude "spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/export.tar"
|
||||
}
|
||||
|
||||
check {
|
||||
dependsOn checkstyleNohttp
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group "org.springframework.boot"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
if (version.contains('-')) {
|
||||
maven { url "https://repo.spring.io/milestone" }
|
||||
}
|
||||
if (version.endsWith('-SNAPSHOT')) {
|
||||
maven { url "https://repo.spring.io/snapshot" }
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, "minutes"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("checkstyleNohttp").configure {
|
||||
maxHeapSize = "1536m"
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
plugins {
|
||||
id "java-gradle-plugin"
|
||||
id "io.spring.javaformat" version "${javaFormatVersion}"
|
||||
id "checkstyle"
|
||||
id "eclipse"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
|
||||
def versions = [:]
|
||||
new File(projectDir.parentFile, "gradle.properties").withInputStream {
|
||||
def properties = new Properties()
|
||||
properties.load(it)
|
||||
["assertj", "commonsCodec", "hamcrest", "jackson", "junitJupiter",
|
||||
"kotlin", "maven"].each {
|
||||
versions[it] = properties[it + "Version"]
|
||||
}
|
||||
}
|
||||
versions["springFramework"] = "6.0.12"
|
||||
ext.set("versions", versions)
|
||||
if (versions.springFramework.contains("-")) {
|
||||
repositories {
|
||||
maven { url "https://repo.spring.io/milestone" }
|
||||
maven { url "https://repo.spring.io/snapshot" }
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
|
||||
|
||||
implementation(platform("org.springframework:spring-framework-bom:${versions.springFramework}"))
|
||||
implementation("com.diffplug.gradle:goomph:3.37.2")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
|
||||
implementation("com.gradle:gradle-enterprise-gradle-plugin:3.12.1")
|
||||
implementation("com.tngtech.archunit:archunit:1.0.0")
|
||||
implementation("commons-codec:commons-codec:${versions.commonsCodec}")
|
||||
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||
implementation("org.apache.maven:maven-embedder:${versions.maven}")
|
||||
implementation("org.asciidoctor:asciidoctor-gradle-jvm:3.3.2")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${versions.kotlin}")
|
||||
implementation("org.springframework:spring-context")
|
||||
implementation("org.springframework:spring-core")
|
||||
implementation("org.springframework:spring-web")
|
||||
|
||||
testImplementation("org.assertj:assertj-core:${versions.assertj}")
|
||||
testImplementation("org.hamcrest:hamcrest:${versions.hamcrest}")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:${versions.junitJupiter}")
|
||||
testImplementation("org.springframework:spring-test")
|
||||
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.4"
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
annotationProcessorPlugin {
|
||||
id = "org.springframework.boot.annotation-processor"
|
||||
implementationClass = "org.springframework.boot.build.processors.AnnotationProcessorPlugin"
|
||||
}
|
||||
architecturePlugin {
|
||||
id = "org.springframework.boot.architecture"
|
||||
implementationClass = "org.springframework.boot.build.architecture.ArchitecturePlugin"
|
||||
}
|
||||
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"
|
||||
}
|
||||
systemTestPlugin {
|
||||
id = "org.springframework.boot.system-test"
|
||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||
}
|
||||
mavenPluginPlugin {
|
||||
id = "org.springframework.boot.maven-plugin"
|
||||
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
||||
}
|
||||
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"
|
||||
}
|
||||
processedAnnotationsPlugin {
|
||||
id = "org.springframework.boot.processed-annotations"
|
||||
implementationClass = "org.springframework.boot.build.processors.ProcessedAnnotationsPlugin"
|
||||
}
|
||||
starterPlugin {
|
||||
id = "org.springframework.boot.starter"
|
||||
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
||||
}
|
||||
testFailuresPlugin {
|
||||
id = "org.springframework.boot.test-failures"
|
||||
implementationClass = "org.springframework.boot.build.testing.TestFailuresPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
eclipse.classpath.file.whenMerged {
|
||||
def jreEntry = entries.find { it.path.contains("org.eclipse.jdt.launching.JRE_CONTAINER") }
|
||||
jreEntry.entryAttributes['module'] = 'true'
|
||||
jreEntry.entryAttributes['limit-modules'] = 'java.base'
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="com.puppycrawl.tools.checkstyle.Checker">
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks">
|
||||
<property name="excludes" value="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck" />
|
||||
</module>
|
||||
</module>
|
@ -1 +0,0 @@
|
||||
javaFormatVersion=0.0.38
|
@ -1,6 +0,0 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import java.io.File;
|
||||
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.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
|
||||
import org.springframework.boot.build.artifacts.ArtifactRelease;
|
||||
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>The version of AsciidoctorJ is upgraded to 2.4.3.
|
||||
* <li>An {@code asciidoctorExtensions} configuration is created.
|
||||
* <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>The {@code backend} is configured.
|
||||
* </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.
|
||||
* <li>{@code asciidoctorExtensions} is added to the task's configurations.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class AsciidoctorConventions {
|
||||
|
||||
private static final String ASCIIDOCTORJ_VERSION = "2.4.3";
|
||||
|
||||
private static final String EXTENSIONS_CONFIGURATION_NAME = "asciidoctorExtensions";
|
||||
|
||||
void apply(Project project) {
|
||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
|
||||
makeAllWarningsFatal(project);
|
||||
upgradeAsciidoctorJVersion(project);
|
||||
createAsciidoctorExtensionsConfiguration(project);
|
||||
project.getTasks()
|
||||
.withType(AbstractAsciidoctorTask.class,
|
||||
(asciidoctorTask) -> configureAsciidoctorTask(project, asciidoctorTask));
|
||||
});
|
||||
}
|
||||
|
||||
private void makeAllWarningsFatal(Project project) {
|
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*");
|
||||
}
|
||||
|
||||
private void upgradeAsciidoctorJVersion(Project project) {
|
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).setVersion(ASCIIDOCTORJ_VERSION);
|
||||
}
|
||||
|
||||
private void createAsciidoctorExtensionsConfiguration(Project project) {
|
||||
project.getConfigurations().create(EXTENSIONS_CONFIGURATION_NAME, (configuration) -> {
|
||||
project.getConfigurations()
|
||||
.matching((candidate) -> "dependencyManagement".equals(candidate.getName()))
|
||||
.all(configuration::extendsFrom);
|
||||
configuration.getDependencies()
|
||||
.add(project.getDependencies()
|
||||
.create("io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.5"));
|
||||
configuration.getDependencies()
|
||||
.add(project.getDependencies().create("org.asciidoctor:asciidoctorj-pdf:1.5.3"));
|
||||
});
|
||||
}
|
||||
|
||||
private void configureAsciidoctorTask(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
asciidoctorTask.configurations(EXTENSIONS_CONFIGURATION_NAME);
|
||||
configureCommonAttributes(project, asciidoctorTask);
|
||||
configureOptions(asciidoctorTask);
|
||||
configureForkOptions(asciidoctorTask);
|
||||
asciidoctorTask.baseDirFollowsSourceDir();
|
||||
createSyncDocumentationSourceTask(project, asciidoctorTask);
|
||||
if (asciidoctorTask instanceof AsciidoctorTask task) {
|
||||
boolean pdf = task.getName().toLowerCase().contains("pdf");
|
||||
String backend = (!pdf) ? "spring-html" : "spring-pdf";
|
||||
task.outputOptions((outputOptions) -> outputOptions.backends(backend));
|
||||
}
|
||||
}
|
||||
|
||||
private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
ArtifactRelease artifacts = ArtifactRelease.forProject(project);
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute-missing", "warn");
|
||||
attributes.put("github-tag", determineGitHubTag(project));
|
||||
attributes.put("artifact-release-type", artifacts.getType());
|
||||
attributes.put("artifact-download-repo", artifacts.getDownloadRepo());
|
||||
attributes.put("revnumber", null);
|
||||
asciidoctorTask.attributes(attributes);
|
||||
}
|
||||
|
||||
// See https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/597
|
||||
private void configureForkOptions(AbstractAsciidoctorTask asciidoctorTask) {
|
||||
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) {
|
||||
asciidoctorTask.forkOptions((options) -> options.jvmArgs("--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/java.io=ALL-UNNAMED"));
|
||||
}
|
||||
}
|
||||
|
||||
private String determineGitHubTag(Project project) {
|
||||
String version = "v" + project.getVersion();
|
||||
return (version.endsWith("-SNAPSHOT")) ? "main" : version;
|
||||
}
|
||||
|
||||
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {
|
||||
asciidoctorTask.options(Collections.singletonMap("doctype", "book"));
|
||||
}
|
||||
|
||||
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.getInputs()
|
||||
.dir(syncedSource)
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("synced source");
|
||||
asciidoctorTask.setSourceDir(project.relativePath(new File(syncedSource, "asciidoc/")));
|
||||
return syncDocumentationSource;
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
/**
|
||||
* Plugin to apply conventions to projects that are part of Spring Boot's build.
|
||||
* Conventions are applied in response to various plugins being applied.
|
||||
*
|
||||
* When the {@link JavaBasePlugin} is applied, the conventions in {@link JavaConventions}
|
||||
* are applied.
|
||||
*
|
||||
* When the {@link MavenPublishPlugin} is applied, the conventions in
|
||||
* {@link MavenPublishingConventions} are applied.
|
||||
*
|
||||
* When the {@link AsciidoctorJPlugin} is applied, the conventions in
|
||||
* {@link AsciidoctorConventions} are applied.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Christoph Dreis
|
||||
* @author Mike Smithson
|
||||
*/
|
||||
public class ConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
new JavaConventions().apply(project);
|
||||
new MavenPublishingConventions().apply(project);
|
||||
new AsciidoctorConventions().apply(project);
|
||||
new KotlinConventions().apply(project);
|
||||
new WarConventions().apply(project);
|
||||
new EclipseConventions().apply(project);
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@SuppressWarnings("deprecation")
|
||||
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.afterEvaluate((evaluated) -> project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {
|
||||
if (((Jar) project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME)).isEnabled()) {
|
||||
project.getComponents()
|
||||
.matching((component) -> component.getName().equals("java"))
|
||||
.all(mavenPublication::from);
|
||||
}
|
||||
}));
|
||||
project.getPlugins()
|
||||
.withType(JavaPlatformPlugin.class)
|
||||
.all((javaPlugin) -> project.getComponents()
|
||||
.matching((component) -> component.getName().equals("javaPlatform"))
|
||||
.all(mavenPublication::from));
|
||||
}
|
||||
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.plugins.ide.api.XmlFileContentMerger;
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
|
||||
import org.gradle.plugins.ide.eclipse.model.Classpath;
|
||||
import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||
import org.gradle.plugins.ide.eclipse.model.Library;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link EclipsePlugin} to work
|
||||
* around buildship issue {@code #1238}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class EclipseConventions {
|
||||
|
||||
void apply(Project project) {
|
||||
project.getPlugins().withType(EclipsePlugin.class, (eclipse) -> {
|
||||
EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
|
||||
eclipseModel.classpath(this::configureClasspath);
|
||||
});
|
||||
}
|
||||
|
||||
private void configureClasspath(EclipseClasspath classpath) {
|
||||
classpath.file(this::configureClasspathFile);
|
||||
}
|
||||
|
||||
private void configureClasspathFile(XmlFileContentMerger merger) {
|
||||
merger.whenMerged((content) -> {
|
||||
if (content instanceof Classpath classpath) {
|
||||
classpath.getEntries().removeIf(this::isKotlinPluginContributedBuildDirectory);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isKotlinPluginContributedBuildDirectory(ClasspathEntry entry) {
|
||||
return (entry instanceof Library library) && isKotlinPluginContributedBuildDirectory(library.getPath())
|
||||
&& isTest(library);
|
||||
}
|
||||
|
||||
private boolean isKotlinPluginContributedBuildDirectory(String path) {
|
||||
return path.contains("/main") && (path.contains("/build/classes/") || path.contains("/build/resources/"));
|
||||
}
|
||||
|
||||
private boolean isTest(Library library) {
|
||||
Object value = library.getEntryAttributes().get("test");
|
||||
return (value instanceof String string && Boolean.parseBoolean(string));
|
||||
}
|
||||
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
|
||||
/**
|
||||
* {@link Task} to extract resources from the classpath and write them to disk.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ExtractResources extends DefaultTask {
|
||||
|
||||
private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}");
|
||||
|
||||
private final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
private final DirectoryProperty destinationDirectory;
|
||||
|
||||
private List<String> resourceNames = new ArrayList<>();
|
||||
|
||||
public ExtractResources() {
|
||||
this.destinationDirectory = getProject().getObjects().directoryProperty();
|
||||
}
|
||||
|
||||
@Input
|
||||
public List<String> getResourceNames() {
|
||||
return this.resourceNames;
|
||||
}
|
||||
|
||||
public void setResourcesNames(List<String> resourceNames) {
|
||||
this.resourceNames = resourceNames;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public DirectoryProperty getDestinationDirectory() {
|
||||
return this.destinationDirectory;
|
||||
}
|
||||
|
||||
public void property(String name, String value) {
|
||||
this.properties.put(name, value);
|
||||
}
|
||||
|
||||
@Input
|
||||
public Map<String, String> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void extractResources() throws IOException {
|
||||
for (String resourceName : this.resourceNames) {
|
||||
InputStream resourceStream = getClass().getClassLoader().getResourceAsStream(resourceName);
|
||||
if (resourceStream == null) {
|
||||
throw new GradleException("Resource '" + resourceName + "' does not exist");
|
||||
}
|
||||
String resource = FileCopyUtils.copyToString(new InputStreamReader(resourceStream, StandardCharsets.UTF_8));
|
||||
resource = this.propertyPlaceholderHelper.replacePlaceholders(resource, this.properties::get);
|
||||
FileCopyUtils.copy(resource,
|
||||
new FileWriter(this.destinationDirectory.file(resourceName).get().getAsFile()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.gradle.enterprise.gradleplugin.testretry.TestRetryExtension;
|
||||
import com.gradle.enterprise.gradleplugin.testselection.PredictiveTestSelectionExtension;
|
||||
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
|
||||
import io.spring.javaformat.gradle.tasks.CheckFormat;
|
||||
import io.spring.javaformat.gradle.tasks.Format;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.DependencySet;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.plugins.quality.Checkstyle;
|
||||
import org.gradle.api.plugins.quality.CheckstyleExtension;
|
||||
import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
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;
|
||||
import org.gradle.external.javadoc.CoreJavadocOptions;
|
||||
|
||||
import org.springframework.boot.build.architecture.ArchitecturePlugin;
|
||||
import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies;
|
||||
import org.springframework.boot.build.optional.OptionalDependenciesPlugin;
|
||||
import org.springframework.boot.build.testing.TestFailuresPlugin;
|
||||
import org.springframework.boot.build.toolchain.ToolchainPlugin;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
|
||||
* plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The project is configured with source and target compatibility of 17
|
||||
* <li>{@link SpringJavaFormatPlugin Spring Java Format}, {@link CheckstylePlugin
|
||||
* Checkstyle}, {@link TestFailuresPlugin Test Failures}, and {@link ArchitecturePlugin
|
||||
* Architecture} plugins are applied
|
||||
* <li>{@link Test} tasks are configured:
|
||||
* <ul>
|
||||
* <li>to use JUnit Platform
|
||||
* <li>with a max heap of 1024M
|
||||
* <li>to run after any Checkstyle and format checking tasks
|
||||
* <li>to enable retries with a maximum of three attempts when running on CI
|
||||
* <li>to use predictive test selection when the value of the
|
||||
* {@code ENABLE_PREDICTIVE_TEST_SELECTION} environment variable is {@code true}
|
||||
* </ul>
|
||||
* <li>A {@code testRuntimeOnly} dependency upon
|
||||
* {@code org.junit.platform:junit-platform-launcher} is added to projects with the
|
||||
* {@link JavaPlugin} applied
|
||||
* <li>{@link JavaCompile}, {@link Javadoc}, and {@link Format} tasks are configured to
|
||||
* use UTF-8 encoding
|
||||
* <li>{@link JavaCompile} tasks are configured to:
|
||||
* <ul>
|
||||
* <li>Use {@code -parameters}.
|
||||
* <li>Treat warnings as errors
|
||||
* <li>Enable {@code unchecked}, {@code deprecation}, {@code rawtypes}, and {@code varags}
|
||||
* warnings
|
||||
* </ul>
|
||||
* <li>{@link Jar} tasks are configured to produce jars with LICENSE.txt and NOTICE.txt
|
||||
* files and 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>
|
||||
* <li>{@code spring-boot-parent} is used for dependency management</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p/>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Christoph Dreis
|
||||
* @author Mike Smithson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class JavaConventions {
|
||||
|
||||
private static final String SOURCE_AND_TARGET_COMPATIBILITY = "17";
|
||||
|
||||
void apply(Project project) {
|
||||
project.getPlugins().withType(JavaBasePlugin.class, (java) -> {
|
||||
project.getPlugins().apply(TestFailuresPlugin.class);
|
||||
project.getPlugins().apply(ArchitecturePlugin.class);
|
||||
configureSpringJavaFormat(project);
|
||||
configureJavaConventions(project);
|
||||
configureJavadocConventions(project);
|
||||
configureTestConventions(project);
|
||||
configureJarManifestConventions(project);
|
||||
configureDependencyManagement(project);
|
||||
configureToolchain(project);
|
||||
configureProhibitedDependencyChecks(project);
|
||||
});
|
||||
}
|
||||
|
||||
private void configureJarManifestConventions(Project project) {
|
||||
ExtractResources extractLegalResources = project.getTasks()
|
||||
.create("extractLegalResources", ExtractResources.class);
|
||||
extractLegalResources.getDestinationDirectory().set(project.getLayout().getBuildDirectory().dir("legal"));
|
||||
extractLegalResources.setResourcesNames(Arrays.asList("LICENSE.txt", "NOTICE.txt"));
|
||||
extractLegalResources.property("version", project.getVersion().toString());
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||
Set<String> sourceJarTaskNames = sourceSets.stream()
|
||||
.map(SourceSet::getSourcesJarTaskName)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> javadocJarTaskNames = sourceSets.stream()
|
||||
.map(SourceSet::getJavadocJarTaskName)
|
||||
.collect(Collectors.toSet());
|
||||
project.getTasks().withType(Jar.class, (jar) -> project.afterEvaluate((evaluated) -> {
|
||||
jar.metaInf((metaInf) -> metaInf.from(extractLegalResources));
|
||||
jar.manifest((manifest) -> {
|
||||
Map<String, Object> attributes = new TreeMap<>();
|
||||
attributes.put("Automatic-Module-Name", project.getName().replace("-", "."));
|
||||
attributes.put("Build-Jdk-Spec", SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
attributes.put("Built-By", "Spring");
|
||||
attributes.put("Implementation-Title",
|
||||
determineImplementationTitle(project, sourceJarTaskNames, javadocJarTaskNames, jar));
|
||||
attributes.put("Implementation-Version", project.getVersion());
|
||||
manifest.attributes(attributes);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
private String determineImplementationTitle(Project project, Set<String> sourceJarTaskNames,
|
||||
Set<String> javadocJarTaskNames, Jar jar) {
|
||||
if (sourceJarTaskNames.contains(jar.getName())) {
|
||||
return "Source for " + project.getName();
|
||||
}
|
||||
if (javadocJarTaskNames.contains(jar.getName())) {
|
||||
return "Javadoc for " + project.getName();
|
||||
}
|
||||
return project.getDescription();
|
||||
}
|
||||
|
||||
private void configureTestConventions(Project project) {
|
||||
project.getTasks().withType(Test.class, (test) -> {
|
||||
test.useJUnitPlatform();
|
||||
test.setMaxHeapSize("1024M");
|
||||
project.getTasks().withType(Checkstyle.class, test::mustRunAfter);
|
||||
project.getTasks().withType(CheckFormat.class, test::mustRunAfter);
|
||||
configureTestRetries(test);
|
||||
configurePredictiveTestSelection(test);
|
||||
});
|
||||
project.getPlugins()
|
||||
.withType(JavaPlugin.class, (javaPlugin) -> project.getDependencies()
|
||||
.add(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, "org.junit.platform:junit-platform-launcher"));
|
||||
}
|
||||
|
||||
private void configureTestRetries(Test test) {
|
||||
TestRetryExtension testRetry = test.getExtensions().getByType(TestRetryExtension.class);
|
||||
testRetry.getFailOnPassedAfterRetry().set(false);
|
||||
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
|
||||
}
|
||||
|
||||
private boolean isCi() {
|
||||
return Boolean.parseBoolean(System.getenv("CI"));
|
||||
}
|
||||
|
||||
private void configurePredictiveTestSelection(Test test) {
|
||||
if (isPredictiveTestSelectionEnabled()) {
|
||||
PredictiveTestSelectionExtension predictiveTestSelection = test.getExtensions()
|
||||
.getByType(PredictiveTestSelectionExtension.class);
|
||||
predictiveTestSelection.getEnabled().convention(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPredictiveTestSelectionEnabled() {
|
||||
return Boolean.parseBoolean(System.getenv("ENABLE_PREDICTIVE_TEST_SELECTION"));
|
||||
}
|
||||
|
||||
private void configureJavadocConventions(Project project) {
|
||||
project.getTasks().withType(Javadoc.class, (javadoc) -> {
|
||||
CoreJavadocOptions options = (CoreJavadocOptions) javadoc.getOptions();
|
||||
options.source("17");
|
||||
options.encoding("UTF-8");
|
||||
options.addStringOption("Xdoclint:none", "-quiet");
|
||||
});
|
||||
}
|
||||
|
||||
private void configureJavaConventions(Project project) {
|
||||
if (!project.hasProperty("toolchainVersion")) {
|
||||
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
javaPluginExtension.setSourceCompatibility(JavaVersion.toVersion(SOURCE_AND_TARGET_COMPATIBILITY));
|
||||
}
|
||||
project.getTasks().withType(JavaCompile.class, (compile) -> {
|
||||
compile.getOptions().setEncoding("UTF-8");
|
||||
List<String> args = compile.getOptions().getCompilerArgs();
|
||||
if (!args.contains("-parameters")) {
|
||||
args.add("-parameters");
|
||||
}
|
||||
if (project.hasProperty("toolchainVersion")) {
|
||||
compile.setSourceCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
compile.setTargetCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
}
|
||||
else if (buildingWithJava17(project)) {
|
||||
args.addAll(Arrays.asList("-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes",
|
||||
"-Xlint:varargs"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean buildingWithJava17(Project project) {
|
||||
return !project.hasProperty("toolchainVersion") && JavaVersion.current() == JavaVersion.VERSION_17;
|
||||
}
|
||||
|
||||
private void configureSpringJavaFormat(Project project) {
|
||||
project.getPlugins().apply(SpringJavaFormatPlugin.class);
|
||||
project.getTasks().withType(Format.class, (Format) -> Format.setEncoding("UTF-8"));
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("8.45.1");
|
||||
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));
|
||||
}
|
||||
|
||||
private void configureDependencyManagement(Project project) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
Configuration dependencyManagement = configurations.create("dependencyManagement", (configuration) -> {
|
||||
configuration.setVisible(false);
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setCanBeResolved(false);
|
||||
});
|
||||
configurations
|
||||
.matching((configuration) -> configuration.getName().endsWith("Classpath")
|
||||
|| JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME.equals(configuration.getName()))
|
||||
.all((configuration) -> configuration.extendsFrom(dependencyManagement));
|
||||
Dependency springBootParent = project.getDependencies()
|
||||
.enforcedPlatform(project.getDependencies()
|
||||
.project(Collections.singletonMap("path", ":spring-boot-project:spring-boot-parent")));
|
||||
dependencyManagement.getDependencies().add(springBootParent);
|
||||
project.getPlugins()
|
||||
.withType(OptionalDependenciesPlugin.class,
|
||||
(optionalDependencies) -> configurations
|
||||
.getByName(OptionalDependenciesPlugin.OPTIONAL_CONFIGURATION_NAME)
|
||||
.extendsFrom(dependencyManagement));
|
||||
}
|
||||
|
||||
private void configureToolchain(Project project) {
|
||||
project.getPlugins().apply(ToolchainPlugin.class);
|
||||
}
|
||||
|
||||
private void configureProhibitedDependencyChecks(Project project) {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||
sourceSets.all((sourceSet) -> createProhibitedDependenciesChecks(project,
|
||||
sourceSet.getCompileClasspathConfigurationName(), sourceSet.getRuntimeClasspathConfigurationName()));
|
||||
}
|
||||
|
||||
private void createProhibitedDependenciesChecks(Project project, String... configurationNames) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
for (String configurationName : configurationNames) {
|
||||
Configuration configuration = configurations.getByName(configurationName);
|
||||
createProhibitedDependenciesCheck(configuration, project);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions;
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@code org.jetbrains.kotlin.jvm}
|
||||
* plugin. When the plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link KotlinCompile} tasks are configured to:
|
||||
* <ul>
|
||||
* <li>Use {@code apiVersion} and {@code languageVersion} 1.7.
|
||||
* <li>Use {@code jvmTarget} 17.
|
||||
* <li>Treat all warnings as errors
|
||||
* <li>Suppress version warnings
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* <p/>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class KotlinConventions {
|
||||
|
||||
void apply(Project project) {
|
||||
project.getPlugins()
|
||||
.withId("org.jetbrains.kotlin.jvm",
|
||||
(plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure));
|
||||
}
|
||||
|
||||
private void configure(KotlinCompile compile) {
|
||||
KotlinJvmOptions kotlinOptions = compile.getKotlinOptions();
|
||||
kotlinOptions.setApiVersion("1.7");
|
||||
kotlinOptions.setLanguageVersion("1.7");
|
||||
kotlinOptions.setJvmTarget("17");
|
||||
kotlinOptions.setAllWarningsAsErrors(true);
|
||||
List<String> freeCompilerArgs = new ArrayList<>(compile.getKotlinOptions().getFreeCompilerArgs());
|
||||
freeCompilerArgs.add("-Xsuppress-version-warnings");
|
||||
compile.getKotlinOptions().setFreeCompilerArgs(freeCompilerArgs);
|
||||
}
|
||||
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import org.apache.maven.artifact.repository.MavenArtifactRepository;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.component.AdhocComponentWithVariants;
|
||||
import org.gradle.api.component.ConfigurationVariantDetails;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.VariantVersionMappingStrategy;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link MavenPublishPlugin}. When
|
||||
* the 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:
|
||||
* <ul>
|
||||
* <li>Creation of Javadoc and source jars is enabled.
|
||||
* <li>Publication metadata (poms and Gradle module metadata) is configured to use
|
||||
* resolved versions.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Christoph Dreis
|
||||
* @author Mike Smithson
|
||||
*/
|
||||
class MavenPublishingConventions {
|
||||
|
||||
void apply(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) -> customizeMavenPublication(mavenPublication, project));
|
||||
project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {
|
||||
JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
extension.withJavadocJar();
|
||||
extension.withSourcesJar();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeMavenPublication(MavenPublication publication, Project project) {
|
||||
customizePom(publication.getPom(), project);
|
||||
project.getPlugins()
|
||||
.withType(JavaPlugin.class)
|
||||
.all((javaPlugin) -> customizeJavaMavenPublication(publication, project));
|
||||
suppressMavenOptionalFeatureWarnings(publication);
|
||||
}
|
||||
|
||||
private void customizePom(MavenPom pom, Project project) {
|
||||
pom.getUrl().set("https://spring.io/projects/spring-boot");
|
||||
pom.getName().set(project.provider(project::getName));
|
||||
pom.getDescription().set(project.provider(project::getDescription));
|
||||
if (!isUserInherited(project)) {
|
||||
pom.organization(this::customizeOrganization);
|
||||
}
|
||||
pom.licenses(this::customizeLicences);
|
||||
pom.developers(this::customizeDevelopers);
|
||||
pom.scm((scm) -> customizeScm(scm, project));
|
||||
if (!isUserInherited(project)) {
|
||||
pom.issueManagement(this::customizeIssueManagement);
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeJavaMavenPublication(MavenPublication publication, Project project) {
|
||||
addMavenOptionalFeature(project);
|
||||
publication.versionMapping((strategy) -> strategy.usage(Usage.JAVA_API, (mappingStrategy) -> mappingStrategy
|
||||
.fromResolutionOf(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)));
|
||||
publication.versionMapping(
|
||||
(strategy) -> strategy.usage(Usage.JAVA_RUNTIME, VariantVersionMappingStrategy::fromResolutionResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a feature that allows maven plugins to declare optional dependencies that
|
||||
* appear in the POM. This is required to make m2e in Eclipse happy.
|
||||
* @param project the project to add the feature to
|
||||
*/
|
||||
private void addMavenOptionalFeature(Project project) {
|
||||
JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
extension.registerFeature("mavenOptional",
|
||||
(feature) -> feature.usingSourceSet(extension.getSourceSets().getByName("main")));
|
||||
AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.getComponents()
|
||||
.findByName("java");
|
||||
javaComponent.addVariantsFromConfiguration(
|
||||
project.getConfigurations().findByName("mavenOptionalRuntimeElements"),
|
||||
ConfigurationVariantDetails::mapToOptional);
|
||||
}
|
||||
|
||||
private void suppressMavenOptionalFeatureWarnings(MavenPublication publication) {
|
||||
publication.suppressPomMetadataWarningsFor("mavenOptionalApiElements");
|
||||
publication.suppressPomMetadataWarningsFor("mavenOptionalRuntimeElements");
|
||||
}
|
||||
|
||||
private void customizeOrganization(MavenPomOrganization organization) {
|
||||
organization.getName().set("VMware, 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("https://www.apache.org/licenses/LICENSE-2.0");
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeDevelopers(MavenPomDeveloperSpec developers) {
|
||||
developers.developer((developer) -> {
|
||||
developer.getName().set("Spring");
|
||||
developer.getEmail().set("ask@spring.io");
|
||||
developer.getOrganization().set("VMware, Inc.");
|
||||
developer.getOrganizationUrl().set("https://www.spring.io");
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeScm(MavenPomScm scm, Project project) {
|
||||
if (!isUserInherited(project)) {
|
||||
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");
|
||||
}
|
||||
|
||||
private boolean isUserInherited(Project project) {
|
||||
return "spring-boot-starter-parent".equals(project.getName())
|
||||
|| "spring-boot-dependencies".equals(project.getName());
|
||||
}
|
||||
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* Tasks for syncing the source code of a Spring Boot application, filtering its
|
||||
* {@code build.gradle} to set the version of its {@code org.springframework.boot} plugin.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class SyncAppSource extends DefaultTask {
|
||||
|
||||
private final DirectoryProperty sourceDirectory;
|
||||
|
||||
private final DirectoryProperty destinationDirectory;
|
||||
|
||||
private final Property<String> pluginVersion;
|
||||
|
||||
public SyncAppSource() {
|
||||
ObjectFactory objects = getProject().getObjects();
|
||||
this.sourceDirectory = objects.directoryProperty();
|
||||
this.destinationDirectory = objects.directoryProperty();
|
||||
this.pluginVersion = objects.property(String.class)
|
||||
.convention(getProject().provider(() -> getProject().getVersion().toString()));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void syncAppSources() {
|
||||
getProject().sync((copySpec) -> {
|
||||
copySpec.from(this.sourceDirectory);
|
||||
copySpec.into(this.destinationDirectory);
|
||||
copySpec.filter((line) -> line.replace("id \"org.springframework.boot\"",
|
||||
"id \"org.springframework.boot\" version \"" + getProject().getVersion() + "\""));
|
||||
});
|
||||
}
|
||||
|
||||
@InputDirectory
|
||||
public DirectoryProperty getSourceDirectory() {
|
||||
return this.sourceDirectory;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public DirectoryProperty getDestinationDirectory() {
|
||||
return this.destinationDirectory;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<String> getPluginVersion() {
|
||||
return this.pluginVersion;
|
||||
}
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.internal.IConventionAware;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin;
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||
import org.gradle.plugins.ide.eclipse.model.Facet;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {WarPlugin}. When the plugin is
|
||||
* applied:
|
||||
* <ul>
|
||||
* <li>Update Eclipse WTP Plugin facets to use Servlet 5.0</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WarConventions {
|
||||
|
||||
void apply(Project project) {
|
||||
project.getPlugins().withType(EclipseWtpPlugin.class, (wtp) -> {
|
||||
project.getTasks().getByName(EclipseWtpPlugin.ECLIPSE_WTP_FACET_TASK_NAME).doFirst((task) -> {
|
||||
EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
|
||||
((IConventionAware) eclipseModel.getWtp().getFacet()).getConventionMapping()
|
||||
.map("facets", () -> getFacets(project));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private List<Facet> getFacets(Project project) {
|
||||
JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
|
||||
List<Facet> facets = new ArrayList<>();
|
||||
facets.add(new Facet(Facet.FacetType.fixed, "jst.web", null));
|
||||
facets.add(new Facet(Facet.FacetType.installed, "jst.web", "5.0"));
|
||||
facets.add(new Facet(Facet.FacetType.installed, "jst.java", javaVersion.toString()));
|
||||
return facets;
|
||||
}
|
||||
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.architecture;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.tngtech.archunit.base.DescribedPredicate;
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.domain.JavaClass.Predicates;
|
||||
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||
import com.tngtech.archunit.core.domain.JavaMethod;
|
||||
import com.tngtech.archunit.core.domain.JavaParameter;
|
||||
import com.tngtech.archunit.core.domain.properties.CanBeAnnotated;
|
||||
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||
import com.tngtech.archunit.lang.ArchCondition;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.ConditionEvents;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.SimpleConditionEvent;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.tasks.IgnoreEmptyDirectories;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* {@link Task} that checks for architecture problems.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class ArchitectureCheck extends DefaultTask {
|
||||
|
||||
private FileCollection classes;
|
||||
|
||||
public ArchitectureCheck() {
|
||||
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
|
||||
getRules().addAll(allPackagesShouldBeFreeOfTangles(),
|
||||
allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(),
|
||||
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(),
|
||||
noClassesShouldCallStepVerifierStepVerifyComplete(),
|
||||
noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList());
|
||||
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void checkArchitecture() throws IOException {
|
||||
JavaClasses javaClasses = new ClassFileImporter()
|
||||
.importPaths(this.classes.getFiles().stream().map(File::toPath).toList());
|
||||
List<EvaluationResult> violations = getRules().get()
|
||||
.stream()
|
||||
.map((rule) -> rule.evaluate(javaClasses))
|
||||
.filter(EvaluationResult::hasViolation)
|
||||
.toList();
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
if (!violations.isEmpty()) {
|
||||
StringBuilder report = new StringBuilder();
|
||||
for (EvaluationResult violation : violations) {
|
||||
report.append(violation.getFailureReport().toString());
|
||||
report.append(String.format("%n"));
|
||||
}
|
||||
Files.writeString(outputFile.toPath(), report.toString(), StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
throw new GradleException("Architecture check failed. See '" + outputFile + "' for details.");
|
||||
}
|
||||
else {
|
||||
outputFile.createNewFile();
|
||||
}
|
||||
}
|
||||
|
||||
private ArchRule allPackagesShouldBeFreeOfTangles() {
|
||||
return SlicesRuleDefinition.slices().matching("(**)").should().beFreeOfCycles();
|
||||
}
|
||||
|
||||
private ArchRule allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization() {
|
||||
return ArchRuleDefinition.methods()
|
||||
.that()
|
||||
.areAnnotatedWith("org.springframework.context.annotation.Bean")
|
||||
.and()
|
||||
.haveRawReturnType(Predicates.assignableTo("org.springframework.beans.factory.config.BeanPostProcessor"))
|
||||
.should(onlyHaveParametersThatWillNotCauseEagerInitialization())
|
||||
.andShould()
|
||||
.beStatic()
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private ArchCondition<JavaMethod> onlyHaveParametersThatWillNotCauseEagerInitialization() {
|
||||
DescribedPredicate<CanBeAnnotated> notAnnotatedWithLazy = DescribedPredicate
|
||||
.not(CanBeAnnotated.Predicates.annotatedWith("org.springframework.context.annotation.Lazy"));
|
||||
DescribedPredicate<JavaClass> notOfASafeType = DescribedPredicate
|
||||
.not(Predicates.assignableTo("org.springframework.beans.factory.ObjectProvider")
|
||||
.or(Predicates.assignableTo("org.springframework.context.ApplicationContext"))
|
||||
.or(Predicates.assignableTo("org.springframework.core.env.Environment")));
|
||||
return new ArchCondition<>("not have parameters that will cause eager initialization") {
|
||||
|
||||
@Override
|
||||
public void check(JavaMethod item, ConditionEvents events) {
|
||||
item.getParameters()
|
||||
.stream()
|
||||
.filter(notAnnotatedWithLazy)
|
||||
.filter((parameter) -> notOfASafeType.test(parameter.getRawType()))
|
||||
.forEach((parameter) -> events.add(SimpleConditionEvent.violated(parameter,
|
||||
parameter.getDescription() + " will cause eager initialization as it is "
|
||||
+ notAnnotatedWithLazy.getDescription() + " and is "
|
||||
+ notOfASafeType.getDescription())));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private ArchRule allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters() {
|
||||
return ArchRuleDefinition.methods()
|
||||
.that()
|
||||
.areAnnotatedWith("org.springframework.context.annotation.Bean")
|
||||
.and()
|
||||
.haveRawReturnType(
|
||||
Predicates.assignableTo("org.springframework.beans.factory.config.BeanFactoryPostProcessor"))
|
||||
.should(haveNoParameters())
|
||||
.andShould()
|
||||
.beStatic()
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private ArchCondition<JavaMethod> haveNoParameters() {
|
||||
return new ArchCondition<>("have no parameters") {
|
||||
|
||||
@Override
|
||||
public void check(JavaMethod item, ConditionEvents events) {
|
||||
List<JavaParameter> parameters = item.getParameters();
|
||||
if (!parameters.isEmpty()) {
|
||||
events
|
||||
.add(SimpleConditionEvent.violated(item, item.getDescription() + " should have no parameters"));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private ArchRule noClassesShouldCallStepVerifierStepVerifyComplete() {
|
||||
return ArchRuleDefinition.noClasses()
|
||||
.should()
|
||||
.callMethod("reactor.test.StepVerifier$Step", "verifyComplete")
|
||||
.because("it can block indefinitely and expectComplete().verify(Duration) should be used instead");
|
||||
}
|
||||
|
||||
private ArchRule noClassesShouldConfigureDefaultStepVerifierTimeout() {
|
||||
return ArchRuleDefinition.noClasses()
|
||||
.should()
|
||||
.callMethod("reactor.test.StepVerifier", "setDefaultTimeout", "java.time.Duration")
|
||||
.because("expectComplete().verify(Duration) should be used instead");
|
||||
}
|
||||
|
||||
private ArchRule noClassesShouldCallCollectorsToList() {
|
||||
return ArchRuleDefinition.noClasses()
|
||||
.should()
|
||||
.callMethod(Collectors.class, "toList")
|
||||
.because("java.util.stream.Stream.toList() should be used instead");
|
||||
}
|
||||
|
||||
public void setClasses(FileCollection classes) {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@Internal
|
||||
public FileCollection getClasses() {
|
||||
return this.classes;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@SkipWhenEmpty
|
||||
@IgnoreEmptyDirectories
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
final FileTree getInputClasses() {
|
||||
return this.classes.getAsFileTree();
|
||||
}
|
||||
|
||||
@Optional
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public abstract DirectoryProperty getResourcesDirectory();
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getOutputDirectory();
|
||||
|
||||
@Internal
|
||||
public abstract ListProperty<ArchRule> getRules();
|
||||
|
||||
@Input
|
||||
// The rules themselves can't be an input as they aren't serializable so we use their
|
||||
// descriptions instead
|
||||
abstract ListProperty<String> getRuleDescriptions();
|
||||
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.architecture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for verifying a project's architecture.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ArchitecturePlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaBasePlugin.class, (javaPlugin) -> registerTasks(project));
|
||||
}
|
||||
|
||||
private void registerTasks(Project project) {
|
||||
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
List<TaskProvider<ArchitectureCheck>> packageTangleChecks = new ArrayList<>();
|
||||
for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) {
|
||||
TaskProvider<ArchitectureCheck> checkPackageTangles = project.getTasks()
|
||||
.register("checkArchitecture" + StringUtils.capitalize(sourceSet.getName()), ArchitectureCheck.class,
|
||||
(task) -> {
|
||||
task.setClasses(sourceSet.getOutput().getClassesDirs());
|
||||
task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir());
|
||||
task.setDescription("Checks the architecture of the classes of the " + sourceSet.getName()
|
||||
+ " source set.");
|
||||
task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||
});
|
||||
packageTangleChecks.add(checkPackageTangles);
|
||||
}
|
||||
if (!packageTangleChecks.isEmpty()) {
|
||||
TaskProvider<Task> checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME);
|
||||
checkTask.configure((check) -> check.dependsOn(packageTangleChecks));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.artifacts;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
/**
|
||||
* Information about artifacts produced by a build.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public final class ArtifactRelease {
|
||||
|
||||
private static final String SNAPSHOT = "snapshot";
|
||||
|
||||
private static final String MILESTONE = "milestone";
|
||||
|
||||
private static final String RELEASE = "release";
|
||||
|
||||
private static final String SPRING_REPO = "https://repo.spring.io/%s";
|
||||
|
||||
private static final String MAVEN_REPO = "https://repo.maven.apache.org/maven2";
|
||||
|
||||
private final String type;
|
||||
|
||||
private ArtifactRelease(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String getDownloadRepo() {
|
||||
return (this.isRelease()) ? MAVEN_REPO : String.format(SPRING_REPO, this.getType());
|
||||
}
|
||||
|
||||
public boolean isRelease() {
|
||||
return RELEASE.equals(this.type);
|
||||
}
|
||||
|
||||
public static ArtifactRelease forProject(Project project) {
|
||||
return new ArtifactRelease(determineReleaseType(project));
|
||||
}
|
||||
|
||||
private static String determineReleaseType(Project project) {
|
||||
String version = project.getVersion().toString();
|
||||
int modifierIndex = version.lastIndexOf('-');
|
||||
if (modifierIndex == -1) {
|
||||
return RELEASE;
|
||||
}
|
||||
String type = version.substring(modifierIndex + 1);
|
||||
if (type.startsWith("M") || type.startsWith("RC")) {
|
||||
return MILESTONE;
|
||||
}
|
||||
return SNAPSHOT;
|
||||
}
|
||||
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.autoconfigure;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
|
||||
/**
|
||||
* A {@link Task} for generating metadata describing a project's auto-configuration
|
||||
* classes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class AutoConfigurationMetadata extends DefaultTask {
|
||||
|
||||
private static final String COMMENT_START = "#";
|
||||
|
||||
private SourceSet sourceSet;
|
||||
|
||||
private File outputFile;
|
||||
|
||||
public AutoConfigurationMetadata() {
|
||||
getInputs()
|
||||
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("org.springframework.boot.autoconfigure.AutoConfiguration");
|
||||
|
||||
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 = CollectionFactory.createSortedProperties(true);
|
||||
List<String> classNames = readAutoConfigurationsFile();
|
||||
Set<String> publicClassNames = new LinkedHashSet<>();
|
||||
for (String className : classNames) {
|
||||
File classFile = findClassFile(className);
|
||||
if (classFile == null) {
|
||||
throw new IllegalStateException("Auto-configuration class '" + className + "' not found.");
|
||||
}
|
||||
try (InputStream in = new FileInputStream(classFile)) {
|
||||
int access = new ClassReader(in).getAccess();
|
||||
if ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
|
||||
publicClassNames.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
autoConfiguration.setProperty("autoConfigurationClassNames", String.join(",", publicClassNames));
|
||||
autoConfiguration.setProperty("module", getProject().getName());
|
||||
return autoConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads auto-configurations from
|
||||
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
|
||||
* @return auto-configurations
|
||||
*/
|
||||
private List<String> readAutoConfigurationsFile() throws IOException {
|
||||
File file = new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports");
|
||||
if (!file.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||
return reader.lines().map(this::stripComment).filter((line) -> !line.isEmpty()).toList();
|
||||
}
|
||||
}
|
||||
|
||||
private String stripComment(String line) {
|
||||
int commentStart = line.indexOf(COMMENT_START);
|
||||
if (commentStart == -1) {
|
||||
return line.trim();
|
||||
}
|
||||
return line.substring(0, commentStart).trim();
|
||||
}
|
||||
|
||||
private File findClassFile(String className) {
|
||||
String classFileName = className.replace(".", "/") + ".class";
|
||||
for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) {
|
||||
File classFile = new File(classesDir, classFileName);
|
||||
if (classFile.isFile()) {
|
||||
return classFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.lang.ArchCondition;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.ConditionEvents;
|
||||
import com.tngtech.archunit.lang.SimpleConditionEvent;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
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.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
import org.springframework.boot.build.DeployedPlugin;
|
||||
import org.springframework.boot.build.architecture.ArchitectureCheck;
|
||||
import org.springframework.boot.build.architecture.ArchitecturePlugin;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define auto-configuration. When applied, the plugin
|
||||
* applies the {@link DeployedPlugin}. Additionally, when the {@link JavaPlugin} is
|
||||
* applied it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Adds a dependency on the auto-configuration annotation processor.
|
||||
* <li>Defines a task that produces metadata describing the auto-configuration. The
|
||||
* metadata is made available as an artifact in the {@code autoConfigurationMetadata}
|
||||
* configuration.
|
||||
* <li>Reacts to the {@link ArchitecturePlugin} being applied and:
|
||||
* <ul>
|
||||
* <li>Adds a rule to the {@code checkArchitectureMain} task to verify that all
|
||||
* {@code AutoConfiguration} classes are listed in the {@code AutoConfiguration.imports}
|
||||
* file.
|
||||
* </ul>
|
||||
* </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";
|
||||
|
||||
private static final String AUTO_CONFIGURATION_IMPORTS_PATH = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().apply(DeployedPlugin.class);
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
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) -> {
|
||||
SourceSet main = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSourceSet(main);
|
||||
task.dependsOn(main.getClassesTaskName());
|
||||
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));
|
||||
});
|
||||
project.getPlugins().withType(ArchitecturePlugin.class, (architecturePlugin) -> {
|
||||
project.getTasks().named("checkArchitectureMain", ArchitectureCheck.class).configure((task) -> {
|
||||
SourceSet main = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
File resourcesDirectory = main.getOutput().getResourcesDir();
|
||||
task.dependsOn(main.getProcessResourcesTaskName());
|
||||
task.getInputs().files(resourcesDirectory).optional().withPathSensitivity(PathSensitivity.RELATIVE);
|
||||
task.getRules()
|
||||
.add(allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports(
|
||||
autoConfigurationImports(project, resourcesDirectory)));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private ArchRule allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports(
|
||||
Provider<AutoConfigurationImports> imports) {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.areAnnotatedWith("org.springframework.boot.autoconfigure.AutoConfiguration")
|
||||
.should(beListedInAutoConfigurationImports(imports))
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private ArchCondition<JavaClass> beListedInAutoConfigurationImports(Provider<AutoConfigurationImports> imports) {
|
||||
return new ArchCondition<>("be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH) {
|
||||
|
||||
@Override
|
||||
public void check(JavaClass item, ConditionEvents events) {
|
||||
AutoConfigurationImports autoConfigurationImports = imports.get();
|
||||
if (!autoConfigurationImports.imports.contains(item.getName())) {
|
||||
events.add(SimpleConditionEvent.violated(item,
|
||||
item.getName() + " was not listed in " + autoConfigurationImports.importsFile));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private Provider<AutoConfigurationImports> autoConfigurationImports(Project project, File resourcesDirectory) {
|
||||
Path importsFile = new File(resourcesDirectory, AUTO_CONFIGURATION_IMPORTS_PATH).toPath();
|
||||
return project.provider(() -> {
|
||||
try {
|
||||
return new AutoConfigurationImports(project.getProjectDir().toPath().relativize(importsFile),
|
||||
Files.readAllLines(importsFile));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException("Failed to read AutoConfiguration.imports", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static record AutoConfigurationImports(Path importsFile, List<String> imports) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
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 DefaultTask {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,495 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
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.lang.Closure;
|
||||
import groovy.lang.GroovyObjectSupport;
|
||||
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.InvalidUserCodeException;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||
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.bom.Library.Exclusion;
|
||||
import org.springframework.boot.build.bom.Library.Group;
|
||||
import org.springframework.boot.build.bom.Library.LibraryVersion;
|
||||
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;
|
||||
import org.springframework.boot.build.mavenplugin.MavenExec;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* 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<>();
|
||||
|
||||
private final UpgradeHandler upgradeHandler;
|
||||
|
||||
private final DependencyHandler dependencyHandler;
|
||||
|
||||
private final Project project;
|
||||
|
||||
public BomExtension(DependencyHandler dependencyHandler, Project project) {
|
||||
this.dependencyHandler = dependencyHandler;
|
||||
this.upgradeHandler = project.getObjects().newInstance(UpgradeHandler.class);
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public List<Library> getLibraries() {
|
||||
return this.libraries;
|
||||
}
|
||||
|
||||
public void upgrade(Action<UpgradeHandler> action) {
|
||||
action.execute(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, Action<LibraryHandler> action) {
|
||||
library(name, null, action);
|
||||
}
|
||||
|
||||
public void library(String name, String version, Action<LibraryHandler> action) {
|
||||
ObjectFactory objects = this.project.getObjects();
|
||||
LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : "");
|
||||
action.execute(libraryHandler);
|
||||
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version));
|
||||
addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups,
|
||||
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots));
|
||||
}
|
||||
|
||||
public void effectiveBomArtifact() {
|
||||
Configuration effectiveBomConfiguration = this.project.getConfigurations().create("effectiveBom");
|
||||
this.project.getTasks()
|
||||
.matching((task) -> task.getName().equals(DeployedPlugin.GENERATE_POM_TASK_NAME))
|
||||
.all((task) -> {
|
||||
Sync syncBom = this.project.getTasks().create("syncBom", Sync.class);
|
||||
syncBom.dependsOn(task);
|
||||
File generatedBomDir = new File(this.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(this.project.getBuildDir(), "local-m2-repository").getAbsolutePath());
|
||||
syncBom.from(this.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 = this.project.getTasks()
|
||||
.create("generateEffectiveBom", MavenExec.class);
|
||||
generateEffectiveBom.setProjectDir(generatedBomDir);
|
||||
File effectiveBom = new File(this.project.getBuildDir(),
|
||||
"generated/effective-bom/" + this.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));
|
||||
this.project.getArtifacts()
|
||||
.add(effectiveBomConfiguration.getName(), effectiveBom,
|
||||
(artifact) -> artifact.builtBy(generateEffectiveBom));
|
||||
});
|
||||
}
|
||||
|
||||
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 classifier) {
|
||||
String coordinates = groupId + ":" + artifactId + ":" + classifier;
|
||||
return this.artifactVersionProperties.get(coordinates);
|
||||
}
|
||||
|
||||
private void putArtifactVersionProperty(String groupId, String artifactId, String versionProperty) {
|
||||
putArtifactVersionProperty(groupId, artifactId, null, versionProperty);
|
||||
}
|
||||
|
||||
private void putArtifactVersionProperty(String groupId, String artifactId, String classifier,
|
||||
String versionProperty) {
|
||||
String coordinates = groupId + ":" + artifactId + ":" + ((classifier != null) ? classifier : "");
|
||||
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);
|
||||
String versionProperty = library.getVersionProperty();
|
||||
if (versionProperty != null) {
|
||||
this.properties.put(versionProperty, library.getVersion().getVersion());
|
||||
}
|
||||
for (Group group : library.getGroups()) {
|
||||
for (Module module : group.getModules()) {
|
||||
putArtifactVersionProperty(group.getId(), module.getName(), module.getClassifier(), versionProperty);
|
||||
this.dependencyHandler.getConstraints()
|
||||
.add(JavaPlatformPlugin.API_CONFIGURATION_NAME, createDependencyNotation(group.getId(),
|
||||
module.getName(), library.getVersion().getVersion()));
|
||||
}
|
||||
for (String bomImport : group.getBoms()) {
|
||||
putArtifactVersionProperty(group.getId(), bomImport, versionProperty);
|
||||
String bomDependency = createDependencyNotation(group.getId(), bomImport,
|
||||
library.getVersion().getVersion());
|
||||
this.dependencyHandler.add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
||||
this.dependencyHandler.platform(bomDependency));
|
||||
this.dependencyHandler.add(BomPlugin.API_ENFORCED_CONFIGURATION_NAME,
|
||||
this.dependencyHandler.enforcedPlatform(bomDependency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LibraryHandler {
|
||||
|
||||
private final List<Group> groups = new ArrayList<>();
|
||||
|
||||
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
|
||||
|
||||
private boolean considerSnapshots = false;
|
||||
|
||||
private String version;
|
||||
|
||||
private String calendarName;
|
||||
|
||||
@Inject
|
||||
public LibraryHandler(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void version(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void considerSnapshots() {
|
||||
this.considerSnapshots = true;
|
||||
}
|
||||
|
||||
public void setCalendarName(String calendarName) {
|
||||
this.calendarName = calendarName;
|
||||
}
|
||||
|
||||
public void group(String id, Action<GroupHandler> action) {
|
||||
GroupHandler groupHandler = new GroupHandler(id);
|
||||
action.execute(groupHandler);
|
||||
this.groups
|
||||
.add(new Group(groupHandler.id, groupHandler.modules, groupHandler.plugins, groupHandler.imports));
|
||||
}
|
||||
|
||||
public void prohibit(Action<ProhibitedHandler> action) {
|
||||
ProhibitedHandler handler = new ProhibitedHandler();
|
||||
action.execute(handler);
|
||||
this.prohibitedVersions.add(new ProhibitedVersion(handler.versionRange, handler.startsWith,
|
||||
handler.endsWith, handler.contains, handler.reason));
|
||||
}
|
||||
|
||||
public static class ProhibitedHandler {
|
||||
|
||||
private String reason;
|
||||
|
||||
private final List<String> startsWith = new ArrayList<>();
|
||||
|
||||
private final List<String> endsWith = new ArrayList<>();
|
||||
|
||||
private final List<String> contains = new ArrayList<>();
|
||||
|
||||
private VersionRange versionRange;
|
||||
|
||||
public void versionRange(String versionRange) {
|
||||
try {
|
||||
this.versionRange = VersionRange.createFromVersionSpec(versionRange);
|
||||
}
|
||||
catch (InvalidVersionSpecificationException ex) {
|
||||
throw new InvalidUserCodeException("Invalid version range", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void startsWith(String startsWith) {
|
||||
this.startsWith.add(startsWith);
|
||||
}
|
||||
|
||||
public void startsWith(Collection<String> startsWith) {
|
||||
this.startsWith.addAll(startsWith);
|
||||
}
|
||||
|
||||
public void endsWith(String endsWith) {
|
||||
this.endsWith.add(endsWith);
|
||||
}
|
||||
|
||||
public void endsWith(Collection<String> endsWith) {
|
||||
this.endsWith.addAll(endsWith);
|
||||
}
|
||||
|
||||
public void contains(String contains) {
|
||||
this.contains.add(contains);
|
||||
}
|
||||
|
||||
public void contains(List<String> contains) {
|
||||
this.contains.addAll(contains);
|
||||
}
|
||||
|
||||
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) ? module : new Module((String) input))
|
||||
.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<?> closure) {
|
||||
ModuleHandler moduleHandler = new ModuleHandler();
|
||||
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
|
||||
closure.setDelegate(moduleHandler);
|
||||
closure.call(moduleHandler);
|
||||
return new Module(name, moduleHandler.type, moduleHandler.classifier, moduleHandler.exclusions);
|
||||
}
|
||||
}
|
||||
throw new InvalidUserDataException("Invalid configuration for module '" + name + "'");
|
||||
}
|
||||
|
||||
public class ModuleHandler {
|
||||
|
||||
private final List<Exclusion> exclusions = new ArrayList<>();
|
||||
|
||||
private String type;
|
||||
|
||||
private String classifier;
|
||||
|
||||
public void exclude(Map<String, String> exclusion) {
|
||||
this.exclusions.add(new Exclusion(exclusion.get("group"), exclusion.get("module")));
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setClassifier(String classifier) {
|
||||
this.classifier = classifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UpgradeHandler {
|
||||
|
||||
private UpgradePolicy upgradePolicy;
|
||||
|
||||
private final GitHubHandler gitHub = new GitHubHandler();
|
||||
|
||||
public void setPolicy(UpgradePolicy upgradePolicy) {
|
||||
this.upgradePolicy = upgradePolicy;
|
||||
}
|
||||
|
||||
public void gitHub(Action<GitHubHandler> action) {
|
||||
action.execute(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 final 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,302 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import groovy.namespace.QName;
|
||||
import groovy.util.Node;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
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.springframework.boot.build.DeployedPlugin;
|
||||
import org.springframework.boot.build.MavenRepositoryPlugin;
|
||||
import org.springframework.boot.build.bom.Library.Group;
|
||||
import org.springframework.boot.build.bom.Library.Module;
|
||||
import org.springframework.boot.build.bom.bomr.MoveToSnapshots;
|
||||
import org.springframework.boot.build.bom.bomr.UpgradeBom;
|
||||
|
||||
/**
|
||||
* {@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> {
|
||||
|
||||
static final String API_ENFORCED_CONFIGURATION_NAME = "apiEnforced";
|
||||
|
||||
@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();
|
||||
createApiEnforcedConfiguration(project);
|
||||
BomExtension bom = project.getExtensions()
|
||||
.create("bom", BomExtension.class, project.getDependencies(), project);
|
||||
project.getTasks().create("bomrCheck", CheckBom.class, bom);
|
||||
project.getTasks().create("bomrUpgrade", UpgradeBom.class, bom);
|
||||
project.getTasks().create("moveToSnapshots", MoveToSnapshots.class, bom);
|
||||
new PublishingCustomizer(project, bom).customize();
|
||||
|
||||
}
|
||||
|
||||
private void createApiEnforcedConfiguration(Project project) {
|
||||
Configuration apiEnforced = project.getConfigurations()
|
||||
.create(API_ENFORCED_CONFIGURATION_NAME, (configuration) -> {
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setCanBeResolved(false);
|
||||
configuration.setVisible(false);
|
||||
});
|
||||
project.getConfigurations()
|
||||
.getByName(JavaPlatformPlugin.ENFORCED_API_ELEMENTS_CONFIGURATION_NAME)
|
||||
.extendsFrom(apiEnforced);
|
||||
project.getConfigurations()
|
||||
.getByName(JavaPlatformPlugin.ENFORCED_RUNTIME_ELEMENTS_CONFIGURATION_NAME)
|
||||
.extendsFrom(apiEnforced);
|
||||
}
|
||||
|
||||
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);
|
||||
addClassifiedManagedDependencies(dependencyManagement);
|
||||
replaceVersionsWithVersionPropertyReferences(dependencyManagement);
|
||||
addExclusionsToManagedDependencies(dependencyManagement);
|
||||
addTypesToManagedDependencies(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();
|
||||
Node classifierNode = findChild(dependency, "classifier");
|
||||
String classifier = (classifierNode != null) ? classifierNode.text() : "";
|
||||
String versionProperty = this.bom.getArtifactVersionProperty(groupId, artifactId, classifier);
|
||||
if (versionProperty != null) {
|
||||
findChild(dependency, "version").setValue("${" + versionProperty + "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 addTypesToManagedDependencies(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();
|
||||
Set<String> types = 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))
|
||||
.map(Module::getType)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (types.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"Multiple types for " + groupId + ":" + artifactId + ": " + types);
|
||||
}
|
||||
if (types.size() == 1) {
|
||||
String type = types.iterator().next();
|
||||
dependency.appendNode("type", type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addClassifiedManagedDependencies(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();
|
||||
String version = findChild(dependency, "version").text();
|
||||
Set<String> classifiers = 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))
|
||||
.map(Module::getClassifier)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Node target = dependency;
|
||||
for (String classifier : classifiers) {
|
||||
if (classifier.length() > 0) {
|
||||
if (target == null) {
|
||||
target = new Node(null, "dependency");
|
||||
target.appendNode("groupId", groupId);
|
||||
target.appendNode("artifactId", artifactId);
|
||||
target.appendNode("version", version);
|
||||
int index = dependency.parent().children().indexOf(dependency);
|
||||
dependency.parent().children().add(index + 1, target);
|
||||
}
|
||||
target.appendNode("classifier", classifier);
|
||||
}
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
String versionProperty = library.getVersionProperty();
|
||||
String value = (versionProperty != null) ? "${" + versionProperty + "}"
|
||||
: library.getVersion().getVersion().toString();
|
||||
plugin.appendNode("version", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if ((node.name() instanceof QName qname) && name.equals(qname.getLocalPart())) {
|
||||
return node;
|
||||
}
|
||||
if (name.equals(node.name())) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Node> findChildren(Node parent, String name) {
|
||||
return parent.children().stream().filter((child) -> isNodeWithName(child, name)).toList();
|
||||
}
|
||||
|
||||
private boolean isNodeWithName(Object candidate, String name) {
|
||||
if (candidate instanceof Node node) {
|
||||
if ((node.name() instanceof QName qname) && name.equals(qname.getLocalPart())) {
|
||||
return true;
|
||||
}
|
||||
if (name.equals(node.name())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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().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)) {
|
||||
if (exclusion.endsWith(":*")) {
|
||||
String group = exclusion.substring(0, exclusion.indexOf(':') + 1);
|
||||
if (resolved.stream().noneMatch((candidate) -> candidate.startsWith(group))) {
|
||||
unused.add(exclusion);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unused.add(exclusion);
|
||||
}
|
||||
}
|
||||
}
|
||||
exclusions.removeAll(resolved);
|
||||
if (!unused.isEmpty()) {
|
||||
throw new InvalidUserDataException(
|
||||
"Unnecessary exclusions on " + groupId + ":" + module.getName() + ": " + exclusions);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 String calendarName;
|
||||
|
||||
private final LibraryVersion version;
|
||||
|
||||
private final List<Group> groups;
|
||||
|
||||
private final String versionProperty;
|
||||
|
||||
private final List<ProhibitedVersion> prohibitedVersions;
|
||||
|
||||
private final boolean considerSnapshots;
|
||||
|
||||
/**
|
||||
* Create a new {@code Library} with the given {@code name}, {@code version}, and
|
||||
* {@code groups}.
|
||||
* @param name name of the library
|
||||
* @param calendarName name of the library as it appears in the Spring Calendar. May
|
||||
* be {@code null} in which case the {@code name} is used.
|
||||
* @param version version of the library
|
||||
* @param groups groups in the library
|
||||
* @param prohibitedVersions version of the library that are prohibited
|
||||
* @param considerSnapshots whether to consider snapshots
|
||||
*/
|
||||
public Library(String name, String calendarName, LibraryVersion version, List<Group> groups,
|
||||
List<ProhibitedVersion> prohibitedVersions, boolean considerSnapshots) {
|
||||
this.name = name;
|
||||
this.calendarName = (calendarName != null) ? calendarName : name;
|
||||
this.version = version;
|
||||
this.groups = groups;
|
||||
this.versionProperty = "Spring Boot".equals(name) ? null
|
||||
: name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version";
|
||||
this.prohibitedVersions = prohibitedVersions;
|
||||
this.considerSnapshots = considerSnapshots;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getCalendarName() {
|
||||
return this.calendarName;
|
||||
}
|
||||
|
||||
public LibraryVersion getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
return this.groups;
|
||||
}
|
||||
|
||||
public String getVersionProperty() {
|
||||
return this.versionProperty;
|
||||
}
|
||||
|
||||
public List<ProhibitedVersion> getProhibitedVersions() {
|
||||
return this.prohibitedVersions;
|
||||
}
|
||||
|
||||
public boolean isConsiderSnapshots() {
|
||||
return this.considerSnapshots;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 List<String> startsWith;
|
||||
|
||||
private final List<String> endsWith;
|
||||
|
||||
private final List<String> contains;
|
||||
|
||||
private final String reason;
|
||||
|
||||
public ProhibitedVersion(VersionRange range, List<String> startsWith, List<String> endsWith,
|
||||
List<String> contains, String reason) {
|
||||
this.range = range;
|
||||
this.startsWith = startsWith;
|
||||
this.endsWith = endsWith;
|
||||
this.contains = contains;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public VersionRange getRange() {
|
||||
return this.range;
|
||||
}
|
||||
|
||||
public List<String> getStartsWith() {
|
||||
return this.startsWith;
|
||||
}
|
||||
|
||||
public List<String> getEndsWith() {
|
||||
return this.endsWith;
|
||||
}
|
||||
|
||||
public List<String> getContains() {
|
||||
return this.contains;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class LibraryVersion {
|
||||
|
||||
private final DependencyVersion version;
|
||||
|
||||
public LibraryVersion(DependencyVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public DependencyVersion getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 String type;
|
||||
|
||||
private final String classifier;
|
||||
|
||||
private final List<Exclusion> exclusions;
|
||||
|
||||
public Module(String name) {
|
||||
this(name, Collections.emptyList());
|
||||
}
|
||||
|
||||
public Module(String name, String type) {
|
||||
this(name, type, null, Collections.emptyList());
|
||||
}
|
||||
|
||||
public Module(String name, List<Exclusion> exclusions) {
|
||||
this(name, null, null, exclusions);
|
||||
}
|
||||
|
||||
public Module(String name, String type, String classifier, List<Exclusion> exclusions) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.classifier = (classifier != null) ? classifier : "";
|
||||
this.exclusions = exclusions;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getClassifier() {
|
||||
return this.classifier;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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> {
|
||||
|
||||
/**
|
||||
* Any version.
|
||||
*/
|
||||
ANY((candidate, current) -> true),
|
||||
|
||||
/**
|
||||
* Minor versions of the current major version.
|
||||
*/
|
||||
SAME_MAJOR_VERSION((candidate, current) -> candidate.isSameMajor(current)),
|
||||
|
||||
/**
|
||||
* Patch versions of the current minor version.
|
||||
*/
|
||||
SAME_MINOR_VERSION((candidate, current) -> candidate.isSameMinor(current));
|
||||
|
||||
private final 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);
|
||||
}
|
||||
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
/**
|
||||
* 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 UserInputHandler userInputHandler;
|
||||
|
||||
private final LibraryUpdateResolver libraryUpdateResolver;
|
||||
|
||||
InteractiveUpgradeResolver(UserInputHandler userInputHandler, LibraryUpdateResolver libraryUpdateResolver) {
|
||||
this.userInputHandler = userInputHandler;
|
||||
this.libraryUpdateResolver = libraryUpdateResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Upgrade> resolveUpgrades(Collection<Library> librariesToUpgrade, Collection<Library> libraries) {
|
||||
Map<String, Library> librariesByName = new HashMap<>();
|
||||
for (Library library : libraries) {
|
||||
librariesByName.put(library.getName(), library);
|
||||
}
|
||||
List<LibraryWithVersionOptions> libraryUpdates = this.libraryUpdateResolver
|
||||
.findLibraryUpdates(librariesToUpgrade, librariesByName);
|
||||
return libraryUpdates.stream().map(this::resolveUpgrade).filter(Objects::nonNull).toList();
|
||||
}
|
||||
|
||||
private Upgrade resolveUpgrade(LibraryWithVersionOptions libraryWithVersionOptions) {
|
||||
if (libraryWithVersionOptions.getVersionOptions().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
VersionOption current = new VersionOption(libraryWithVersionOptions.getLibrary().getVersion().getVersion());
|
||||
VersionOption selected = this.userInputHandler.selectOption(
|
||||
libraryWithVersionOptions.getLibrary().getName() + " "
|
||||
+ libraryWithVersionOptions.getLibrary().getVersion().getVersion(),
|
||||
libraryWithVersionOptions.getVersionOptions(), current);
|
||||
return (selected.equals(current)) ? null
|
||||
: new Upgrade(libraryWithVersionOptions.getLibrary(), selected.getVersion());
|
||||
}
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
/**
|
||||
* Resolves library updates.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
public interface LibraryUpdateResolver {
|
||||
|
||||
/**
|
||||
* Finds library updates.
|
||||
* @param librariesToUpgrade libraries to update
|
||||
* @param librariesByName libraries indexed by name
|
||||
* @return library which have updates
|
||||
*/
|
||||
List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName);
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
class LibraryWithVersionOptions {
|
||||
|
||||
private final Library library;
|
||||
|
||||
private final List<VersionOption> versionOptions;
|
||||
|
||||
LibraryWithVersionOptions(Library library, List<VersionOption> versionOptions) {
|
||||
this.library = library;
|
||||
this.versionOptions = versionOptions;
|
||||
}
|
||||
|
||||
Library getLibrary() {
|
||||
return this.library;
|
||||
}
|
||||
|
||||
List<VersionOption> getVersionOptions() {
|
||||
return this.versionOptions;
|
||||
}
|
||||
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
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 Collection<URI> repositoryUrls;
|
||||
|
||||
MavenMetadataVersionResolver(Collection<URI> repositoryUrls) {
|
||||
this(new RestTemplate(Collections.singletonList(new StringHttpMessageConverter())), repositoryUrls);
|
||||
}
|
||||
|
||||
MavenMetadataVersionResolver(RestTemplate restTemplate, Collection<URI> repositoryUrls) {
|
||||
this.rest = restTemplate;
|
||||
this.repositoryUrls = normalize(repositoryUrls);
|
||||
}
|
||||
|
||||
private Collection<URI> normalize(Collection<URI> uris) {
|
||||
return uris.stream().map(this::normalize).toList();
|
||||
}
|
||||
|
||||
private URI normalize(URI uri) {
|
||||
if ("/".equals(uri.getPath())) {
|
||||
return uri;
|
||||
}
|
||||
return URI.create(uri.toString() + "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<DependencyVersion> resolveVersions(String groupId, String artifactId) {
|
||||
Set<String> versions = new HashSet<>();
|
||||
for (URI repositoryUrl : this.repositoryUrls) {
|
||||
versions.addAll(resolveVersions(groupId, artifactId, repositoryUrl));
|
||||
}
|
||||
return versions.stream().map(DependencyVersion::parse).collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
private Set<String> resolveVersions(String groupId, String artifactId, URI repositoryUrl) {
|
||||
Set<String> versions = new HashSet<>();
|
||||
URI url = repositoryUrl.resolve(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;
|
||||
}
|
||||
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.build.bom.BomExtension;
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
import org.springframework.boot.build.bom.bomr.ReleaseSchedule.Release;
|
||||
import org.springframework.boot.build.bom.bomr.github.Milestone;
|
||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
|
||||
/**
|
||||
* A {@link Task} to move to snapshot dependencies.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class MoveToSnapshots extends UpgradeDependencies {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MoveToSnapshots.class);
|
||||
|
||||
private final URI REPOSITORY_URI = URI.create("https://repo.spring.io/snapshot/");
|
||||
|
||||
@Inject
|
||||
public MoveToSnapshots(BomExtension bom) {
|
||||
super(bom, true);
|
||||
getRepositoryUris().add(this.REPOSITORY_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TaskAction
|
||||
void upgradeDependencies() {
|
||||
super.upgradeDependencies();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String issueTitle(Upgrade upgrade) {
|
||||
String snapshotVersion = upgrade.getVersion().toString();
|
||||
String releaseVersion = snapshotVersion.substring(0, snapshotVersion.length() - "-SNAPSHOT".length());
|
||||
return "Upgrade to " + upgrade.getLibrary().getName() + " " + releaseVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String commitMessage(Upgrade upgrade, int issueNumber) {
|
||||
return "Start building against " + upgrade.getLibrary().getName() + " " + releaseVersion(upgrade) + " snapshots"
|
||||
+ "\n\nSee gh-" + issueNumber;
|
||||
}
|
||||
|
||||
private String releaseVersion(Upgrade upgrade) {
|
||||
String snapshotVersion = upgrade.getVersion().toString();
|
||||
return snapshotVersion.substring(0, snapshotVersion.length() - "-SNAPSHOT".length());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean eligible(Library library) {
|
||||
return library.isConsiderSnapshots() && super.eligible(library);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BiPredicate<Library, DependencyVersion>> determineUpdatePredicates(Milestone milestone) {
|
||||
ReleaseSchedule releaseSchedule = new ReleaseSchedule();
|
||||
Map<String, List<Release>> releases = releaseSchedule.releasesBetween(OffsetDateTime.now(),
|
||||
milestone.getDueOn());
|
||||
List<BiPredicate<Library, DependencyVersion>> predicates = super.determineUpdatePredicates(milestone);
|
||||
predicates.add((library, candidate) -> {
|
||||
List<Release> releasesForLibrary = releases.get(library.getCalendarName());
|
||||
if (releasesForLibrary != null) {
|
||||
for (Release release : releasesForLibrary) {
|
||||
if (candidate.isSnapshotFor(release.getVersion())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Ignoring " + candidate + ". No release of " + library.getName() + " scheduled before "
|
||||
+ milestone.getDueOn());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return predicates;
|
||||
}
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
/**
|
||||
* {@link LibraryUpdateResolver} decorator that uses multiple threads to find library
|
||||
* updates.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class MultithreadedLibraryUpdateResolver implements LibraryUpdateResolver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MultithreadedLibraryUpdateResolver.class);
|
||||
|
||||
private final int threads;
|
||||
|
||||
private final LibraryUpdateResolver delegate;
|
||||
|
||||
MultithreadedLibraryUpdateResolver(int threads, LibraryUpdateResolver delegate) {
|
||||
this.threads = threads;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName) {
|
||||
LOGGER.info("Looking for updates using {} threads", this.threads);
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(this.threads);
|
||||
try {
|
||||
return librariesToUpgrade.stream()
|
||||
.map((library) -> executorService.submit(
|
||||
() -> this.delegate.findLibraryUpdates(Collections.singletonList(library), librariesByName)))
|
||||
.flatMap(this::getResult)
|
||||
.toList();
|
||||
}
|
||||
finally {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<LibraryWithVersionOptions> getResult(Future<List<LibraryWithVersionOptions>> job) {
|
||||
try {
|
||||
return job.get().stream();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Release schedule for Spring projects, retrieved from
|
||||
* <a href="https://calendar.spring.io">https://calendar.spring.io</a>.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ReleaseSchedule {
|
||||
|
||||
private static final Pattern LIBRARY_AND_VERSION = Pattern.compile("([A-Za-z0-9 ]+) ([0-9A-Za-z.-]+)");
|
||||
|
||||
private final RestOperations rest;
|
||||
|
||||
ReleaseSchedule() {
|
||||
this(new RestTemplate());
|
||||
}
|
||||
|
||||
ReleaseSchedule(RestOperations rest) {
|
||||
this.rest = rest;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
Map<String, List<Release>> releasesBetween(OffsetDateTime start, OffsetDateTime end) {
|
||||
ResponseEntity<List> response = this.rest
|
||||
.getForEntity("https://calendar.spring.io/releases?start=" + start + "&end=" + end, List.class);
|
||||
List<Map<String, String>> body = response.getBody();
|
||||
Map<String, List<Release>> releasesByLibrary = new LinkedCaseInsensitiveMap<>();
|
||||
body.stream()
|
||||
.map(this::asRelease)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach((release) -> releasesByLibrary.computeIfAbsent(release.getLibraryName(), (l) -> new ArrayList<>())
|
||||
.add(release));
|
||||
return releasesByLibrary;
|
||||
}
|
||||
|
||||
private Release asRelease(Map<String, String> entry) {
|
||||
LocalDate due = LocalDate.parse(entry.get("start"));
|
||||
String title = entry.get("title");
|
||||
Matcher matcher = LIBRARY_AND_VERSION.matcher(title);
|
||||
if (!matcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
String library = matcher.group(1);
|
||||
String version = matcher.group(2);
|
||||
return new Release(library, DependencyVersion.parse(version), due);
|
||||
}
|
||||
|
||||
static class Release {
|
||||
|
||||
private final String libraryName;
|
||||
|
||||
private final DependencyVersion version;
|
||||
|
||||
private final LocalDate dueOn;
|
||||
|
||||
Release(String libraryName, DependencyVersion version, LocalDate dueOn) {
|
||||
this.libraryName = libraryName;
|
||||
this.version = version;
|
||||
this.dueOn = dueOn;
|
||||
}
|
||||
|
||||
String getLibraryName() {
|
||||
return this.libraryName;
|
||||
}
|
||||
|
||||
DependencyVersion getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
LocalDate getDueOn() {
|
||||
return this.dueOn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.bomr.version.DependencyVersion;
|
||||
|
||||
/**
|
||||
* Standard implementation for {@link LibraryUpdateResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StandardLibraryUpdateResolver.class);
|
||||
|
||||
private final VersionResolver versionResolver;
|
||||
|
||||
private final BiPredicate<Library, DependencyVersion> predicate;
|
||||
|
||||
StandardLibraryUpdateResolver(VersionResolver versionResolver,
|
||||
List<BiPredicate<Library, DependencyVersion>> predicates) {
|
||||
this.versionResolver = versionResolver;
|
||||
this.predicate = (library, dependencyVersion) -> predicates.stream()
|
||||
.allMatch((predicate) -> predicate.test(library, dependencyVersion));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName) {
|
||||
List<LibraryWithVersionOptions> result = new ArrayList<>();
|
||||
for (Library library : librariesToUpgrade) {
|
||||
if (isLibraryExcluded(library)) {
|
||||
continue;
|
||||
}
|
||||
LOGGER.info("Looking for updates for {}", library.getName());
|
||||
long start = System.nanoTime();
|
||||
List<VersionOption> versionOptions = getVersionOptions(library, librariesByName);
|
||||
result.add(new LibraryWithVersionOptions(library, versionOptions));
|
||||
LOGGER.info("Found {} updates for {}, took {}", versionOptions.size(), library.getName(),
|
||||
Duration.ofNanos(System.nanoTime() - start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isLibraryExcluded(Library library) {
|
||||
return library.getName().equals("Spring Boot");
|
||||
}
|
||||
|
||||
protected List<VersionOption> getVersionOptions(Library library, Map<String, Library> libraries) {
|
||||
return determineResolvedVersionOptions(library);
|
||||
}
|
||||
|
||||
private List<VersionOption> determineResolvedVersionOptions(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));
|
||||
}
|
||||
for (String bom : group.getBoms()) {
|
||||
moduleVersions.put(group.getId() + ":" + bom, getLaterVersionsForModule(group.getId(), bom, library));
|
||||
}
|
||||
for (String plugin : group.getPlugins()) {
|
||||
moduleVersions.put(group.getId() + ":" + plugin,
|
||||
getLaterVersionsForModule(group.getId(), plugin, library));
|
||||
}
|
||||
}
|
||||
return moduleVersions.values()
|
||||
.stream()
|
||||
.flatMap(SortedSet::stream)
|
||||
.distinct()
|
||||
.filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion))
|
||||
.map((version) -> (VersionOption) new VersionOption.ResolvedVersionOption(version,
|
||||
getMissingModules(moduleVersions, version)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
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, Library library) {
|
||||
return this.versionResolver.resolveVersions(groupId, artifactId);
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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;
|
||||
}
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* {@code UpgradeApplicator} is used to apply an {@link Upgrade}. Modifies the bom
|
||||
* configuration in the build file or a version property in {@code gradle.properties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UpgradeApplicator {
|
||||
|
||||
private final Path buildFile;
|
||||
|
||||
private final Path gradleProperties;
|
||||
|
||||
UpgradeApplicator(Path buildFile, Path gradleProperties) {
|
||||
this.buildFile = buildFile;
|
||||
this.gradleProperties = gradleProperties;
|
||||
}
|
||||
|
||||
Path apply(Upgrade upgrade) throws IOException {
|
||||
String buildFileContents = Files.readString(this.buildFile);
|
||||
Matcher matcher = Pattern.compile("library\\(\"" + upgrade.getLibrary().getName() + "\", \"(.+)\"\\)")
|
||||
.matcher(buildFileContents);
|
||||
if (!matcher.find()) {
|
||||
matcher = Pattern
|
||||
.compile("library\\(\"" + upgrade.getLibrary().getName() + "\"\\) \\{\\s+version\\(\"(.+)\"\\)",
|
||||
Pattern.MULTILINE)
|
||||
.matcher(buildFileContents);
|
||||
if (!matcher.find()) {
|
||||
throw new IllegalStateException("Failed to find definition for library '"
|
||||
+ upgrade.getLibrary().getName() + "' in bom '" + this.buildFile + "'");
|
||||
}
|
||||
}
|
||||
String version = matcher.group(1);
|
||||
if (version.startsWith("${") && version.endsWith("}")) {
|
||||
updateGradleProperties(upgrade, version);
|
||||
return this.gradleProperties;
|
||||
}
|
||||
else {
|
||||
updateBuildFile(upgrade, buildFileContents, matcher.start(1), matcher.end(1));
|
||||
return this.buildFile;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGradleProperties(Upgrade upgrade, String version) throws IOException {
|
||||
String property = version.substring(2, version.length() - 1);
|
||||
String gradlePropertiesContents = Files.readString(this.gradleProperties);
|
||||
String modified = gradlePropertiesContents.replace(
|
||||
property + "=" + upgrade.getLibrary().getVersion().getVersion(), property + "=" + upgrade.getVersion());
|
||||
overwrite(this.gradleProperties, modified);
|
||||
}
|
||||
|
||||
private void updateBuildFile(Upgrade upgrade, String buildFileContents, int versionStart, int versionEnd)
|
||||
throws IOException {
|
||||
String modified = buildFileContents.substring(0, versionStart) + upgrade.getVersion()
|
||||
+ buildFileContents.substring(versionEnd);
|
||||
overwrite(this.buildFile, modified);
|
||||
}
|
||||
|
||||
private void overwrite(Path target, String content) throws IOException {
|
||||
Files.writeString(target, content, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
|
||||
|
||||
import org.springframework.boot.build.bom.BomExtension;
|
||||
|
||||
/**
|
||||
* {@link Task} to upgrade the libraries managed by a bom.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
public abstract class UpgradeBom extends UpgradeDependencies {
|
||||
|
||||
@Inject
|
||||
public UpgradeBom(BomExtension bom) {
|
||||
super(bom);
|
||||
getProject().getRepositories().withType(MavenArtifactRepository.class, (repository) -> {
|
||||
URI repositoryUrl = repository.getUrl();
|
||||
if (!repositoryUrl.toString().endsWith("snapshot")) {
|
||||
getRepositoryUris().add(repositoryUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String issueTitle(Upgrade upgrade) {
|
||||
return "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String commitMessage(Upgrade upgrade, int issueNumber) {
|
||||
return issueTitle(upgrade) + "\n\nCloses gh-" + issueNumber;
|
||||
}
|
||||
|
||||
}
|
@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
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.Library;
|
||||
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
|
||||
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.Issue;
|
||||
import org.springframework.boot.build.bom.bomr.github.Milestone;
|
||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for tasks that upgrade dependencies in a bom.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
public abstract class UpgradeDependencies extends DefaultTask {
|
||||
|
||||
private final BomExtension bom;
|
||||
|
||||
private final boolean movingToSnapshots;
|
||||
|
||||
@Inject
|
||||
public UpgradeDependencies(BomExtension bom) {
|
||||
this(bom, false);
|
||||
}
|
||||
|
||||
protected UpgradeDependencies(BomExtension bom, boolean movingToSnapshots) {
|
||||
this.bom = bom;
|
||||
getThreads().convention(2);
|
||||
this.movingToSnapshots = movingToSnapshots;
|
||||
}
|
||||
|
||||
@Input
|
||||
@Option(option = "milestone", description = "Milestone to which dependency upgrade issues should be assigned")
|
||||
public abstract Property<String> getMilestone();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
@Option(option = "threads", description = "Number of Threads to use for update resolution")
|
||||
public abstract Property<Integer> getThreads();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
@Option(option = "libraries", description = "Regular expression that identifies the libraries to upgrade")
|
||||
public abstract Property<String> getLibraries();
|
||||
|
||||
@Input
|
||||
abstract ListProperty<URI> getRepositoryUris();
|
||||
|
||||
@TaskAction
|
||||
void upgradeDependencies() {
|
||||
GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(),
|
||||
this.bom.getUpgrade().getGitHub().getRepository());
|
||||
List<String> issueLabels = verifyLabels(repository);
|
||||
Milestone milestone = determineMilestone(repository);
|
||||
List<Upgrade> upgrades = resolveUpgrades(milestone);
|
||||
applyUpgrades(repository, issueLabels, milestone, upgrades);
|
||||
}
|
||||
|
||||
private void applyUpgrades(GitHubRepository repository, List<String> issueLabels, Milestone milestone,
|
||||
List<Upgrade> upgrades) {
|
||||
Path buildFile = getProject().getBuildFile().toPath();
|
||||
Path gradleProperties = new File(getProject().getRootProject().getProjectDir(), "gradle.properties").toPath();
|
||||
UpgradeApplicator upgradeApplicator = new UpgradeApplicator(buildFile, gradleProperties);
|
||||
List<Issue> existingUpgradeIssues = repository.findIssues(issueLabels, milestone);
|
||||
System.out.println("Applying upgrades...");
|
||||
System.out.println("");
|
||||
for (Upgrade upgrade : upgrades) {
|
||||
System.out.println(upgrade.getLibrary().getName() + " " + upgrade.getVersion());
|
||||
String title = issueTitle(upgrade);
|
||||
Issue existingUpgradeIssue = findExistingUpgradeIssue(existingUpgradeIssues, upgrade);
|
||||
try {
|
||||
Path modified = upgradeApplicator.apply(upgrade);
|
||||
int issueNumber = getOrOpenUpgradeIssue(repository, issueLabels, milestone, title,
|
||||
existingUpgradeIssue);
|
||||
if (existingUpgradeIssue != null && existingUpgradeIssue.getState() == Issue.State.CLOSED) {
|
||||
existingUpgradeIssue.label(Arrays.asList("type: task", "status: superseded"));
|
||||
}
|
||||
System.out.println(" Issue: " + issueNumber + " - " + title
|
||||
+ getExistingUpgradeIssueMessageDetails(existingUpgradeIssue));
|
||||
if (new ProcessBuilder().command("git", "add", modified.toFile().getAbsolutePath())
|
||||
.start()
|
||||
.waitFor() != 0) {
|
||||
throw new IllegalStateException("git add failed");
|
||||
}
|
||||
String commitMessage = commitMessage(upgrade, issueNumber);
|
||||
if (new ProcessBuilder().command("git", "commit", "-m", commitMessage).start().waitFor() != 0) {
|
||||
throw new IllegalStateException("git commit failed");
|
||||
}
|
||||
System.out.println(" Commit: " + commitMessage.substring(0, commitMessage.indexOf('\n')));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new TaskExecutionException(this, ex);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getOrOpenUpgradeIssue(GitHubRepository repository, List<String> issueLabels, Milestone milestone,
|
||||
String title, Issue existingUpgradeIssue) {
|
||||
if (existingUpgradeIssue != null && existingUpgradeIssue.getState() == Issue.State.OPEN) {
|
||||
return existingUpgradeIssue.getNumber();
|
||||
}
|
||||
String body = (existingUpgradeIssue != null) ? "Supersedes #" + existingUpgradeIssue.getNumber() : "";
|
||||
return repository.openIssue(title, body, issueLabels, milestone);
|
||||
}
|
||||
|
||||
private String getExistingUpgradeIssueMessageDetails(Issue existingUpgradeIssue) {
|
||||
if (existingUpgradeIssue == null) {
|
||||
return "";
|
||||
}
|
||||
if (existingUpgradeIssue.getState() != Issue.State.CLOSED) {
|
||||
return " (completes existing upgrade)";
|
||||
}
|
||||
return " (supersedes #" + existingUpgradeIssue.getNumber() + " " + existingUpgradeIssue.getTitle() + ")";
|
||||
}
|
||||
|
||||
private List<String> verifyLabels(GitHubRepository repository) {
|
||||
Set<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));
|
||||
}
|
||||
return issueLabels;
|
||||
}
|
||||
|
||||
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 Milestone determineMilestone(GitHubRepository repository) {
|
||||
List<Milestone> milestones = repository.getMilestones();
|
||||
java.util.Optional<Milestone> matchingMilestone = milestones.stream()
|
||||
.filter((milestone) -> milestone.getName().equals(getMilestone().get()))
|
||||
.findFirst();
|
||||
if (!matchingMilestone.isPresent()) {
|
||||
throw new InvalidUserDataException("Unknown milestone: " + getMilestone().get());
|
||||
}
|
||||
return matchingMilestone.get();
|
||||
}
|
||||
|
||||
private Issue findExistingUpgradeIssue(List<Issue> existingUpgradeIssues, Upgrade upgrade) {
|
||||
String toMatch = "Upgrade to " + upgrade.getLibrary().getName();
|
||||
for (Issue existingUpgradeIssue : existingUpgradeIssues) {
|
||||
String title = existingUpgradeIssue.getTitle();
|
||||
int lastSpaceIndex = title.lastIndexOf(' ');
|
||||
if (lastSpaceIndex > -1) {
|
||||
title = title.substring(0, lastSpaceIndex);
|
||||
}
|
||||
if (title.equals(toMatch)) {
|
||||
return existingUpgradeIssue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private List<Upgrade> resolveUpgrades(Milestone milestone) {
|
||||
List<Upgrade> upgrades = new InteractiveUpgradeResolver(getServices().get(UserInputHandler.class),
|
||||
new MultithreadedLibraryUpdateResolver(getThreads().get(),
|
||||
new StandardLibraryUpdateResolver(new MavenMetadataVersionResolver(getRepositoryUris().get()),
|
||||
determineUpdatePredicates(milestone))))
|
||||
.resolveUpgrades(matchingLibraries(), this.bom.getLibraries());
|
||||
return upgrades;
|
||||
}
|
||||
|
||||
protected List<BiPredicate<Library, DependencyVersion>> determineUpdatePredicates(Milestone milestone) {
|
||||
List<BiPredicate<Library, DependencyVersion>> updatePredicates = new ArrayList<>();
|
||||
updatePredicates.add(this::compilesWithUpgradePolicy);
|
||||
updatePredicates.add(this::isAnUpgrade);
|
||||
updatePredicates.add(this::isNotProhibited);
|
||||
return updatePredicates;
|
||||
}
|
||||
|
||||
private boolean compilesWithUpgradePolicy(Library library, DependencyVersion candidate) {
|
||||
return this.bom.getUpgrade().getPolicy().test(candidate, library.getVersion().getVersion());
|
||||
}
|
||||
|
||||
private boolean isAnUpgrade(Library library, DependencyVersion candidate) {
|
||||
return library.getVersion().getVersion().isUpgrade(candidate, this.movingToSnapshots);
|
||||
}
|
||||
|
||||
private boolean isNotProhibited(Library library, DependencyVersion candidate) {
|
||||
return !library.getProhibitedVersions()
|
||||
.stream()
|
||||
.anyMatch((prohibited) -> isProhibited(prohibited, candidate.toString()));
|
||||
}
|
||||
|
||||
private boolean isProhibited(ProhibitedVersion prohibited, String candidate) {
|
||||
boolean result = false;
|
||||
VersionRange range = prohibited.getRange();
|
||||
result = result || (range != null && range.containsVersion(new DefaultArtifactVersion(candidate)));
|
||||
result = result || prohibited.getStartsWith().stream().anyMatch(candidate::startsWith);
|
||||
result = result || prohibited.getStartsWith().stream().anyMatch(candidate::endsWith);
|
||||
result = result || prohibited.getStartsWith().stream().anyMatch(candidate::contains);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Library> matchingLibraries() {
|
||||
List<Library> matchingLibraries = this.bom.getLibraries().stream().filter(this::eligible).toList();
|
||||
if (matchingLibraries.isEmpty()) {
|
||||
throw new InvalidUserDataException("No libraries to upgrade");
|
||||
}
|
||||
return matchingLibraries;
|
||||
}
|
||||
|
||||
protected boolean eligible(Library library) {
|
||||
String pattern = getLibraries().getOrNull();
|
||||
if (pattern == null) {
|
||||
return true;
|
||||
}
|
||||
Predicate<String> libraryPredicate = Pattern.compile(pattern).asPredicate();
|
||||
return libraryPredicate.test(library.getName());
|
||||
}
|
||||
|
||||
protected abstract String issueTitle(Upgrade upgrade);
|
||||
|
||||
protected abstract String commitMessage(Upgrade upgrade, int issueNumber);
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 librariesToUpgrade the libraries to upgrade
|
||||
* @param libraries all libraries
|
||||
* @return the upgrades
|
||||
*/
|
||||
List<Upgrade> resolveUpgrades(Collection<Library> librariesToUpgrade, Collection<Library> libraries);
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An option for a library update.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class VersionOption {
|
||||
|
||||
private final DependencyVersion version;
|
||||
|
||||
VersionOption(DependencyVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
DependencyVersion getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.version.toString();
|
||||
}
|
||||
|
||||
static final class AlignedVersionOption extends VersionOption {
|
||||
|
||||
private final Library alignedWith;
|
||||
|
||||
AlignedVersionOption(DependencyVersion version, Library alignedWith) {
|
||||
super(version);
|
||||
this.alignedWith = alignedWith;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " (aligned with " + this.alignedWith.getName() + " "
|
||||
+ this.alignedWith.getVersion().getVersion() + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class ResolvedVersionOption extends VersionOption {
|
||||
|
||||
private final List<String> missingModules;
|
||||
|
||||
ResolvedVersionOption(DependencyVersion version, List<String> missingModules) {
|
||||
super(version);
|
||||
this.missingModules = missingModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.missingModules.isEmpty()) {
|
||||
return super.toString();
|
||||
}
|
||||
return super.toString() + " (some modules are missing: "
|
||||
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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 body the body 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, String body, List<String> labels, Milestone milestone);
|
||||
|
||||
/**
|
||||
* Returns the labels in the repository.
|
||||
* @return the labels
|
||||
*/
|
||||
Set<String> getLabels();
|
||||
|
||||
/**
|
||||
* Returns the milestones in the repository.
|
||||
* @return the milestones
|
||||
*/
|
||||
List<Milestone> getMilestones();
|
||||
|
||||
/**
|
||||
* Finds issues that have the given {@code labels} and are assigned to the given
|
||||
* {@code milestone}.
|
||||
* @param labels issue labels
|
||||
* @param milestone assigned milestone
|
||||
* @return the matching issues
|
||||
*/
|
||||
List<Issue> findIssues(List<String> labels, Milestone milestone);
|
||||
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Minimal representation of a GitHub issue.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class Issue {
|
||||
|
||||
private final RestTemplate rest;
|
||||
|
||||
private final int number;
|
||||
|
||||
private final String title;
|
||||
|
||||
private final State state;
|
||||
|
||||
Issue(RestTemplate rest, int number, String title, State state) {
|
||||
this.rest = rest;
|
||||
this.number = number;
|
||||
this.title = title;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return this.number;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels the issue with the given {@code labels}. Any existing labels are removed.
|
||||
* @param labels the labels to apply to the issue
|
||||
*/
|
||||
public void label(List<String> labels) {
|
||||
Map<String, List<String>> body = Collections.singletonMap("labels", labels);
|
||||
this.rest.put("issues/" + this.number + "/labels", body);
|
||||
}
|
||||
|
||||
public enum State {
|
||||
|
||||
/**
|
||||
* The issue is open.
|
||||
*/
|
||||
OPEN,
|
||||
|
||||
/**
|
||||
* The issue is closed.
|
||||
*/
|
||||
CLOSED;
|
||||
|
||||
static State of(String state) {
|
||||
if ("open".equals(state)) {
|
||||
return OPEN;
|
||||
}
|
||||
if ("closed".equals(state)) {
|
||||
return CLOSED;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown state '" + state + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
/**
|
||||
* A milestone in a {@link GitHubRepository GitHub repository}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class Milestone {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final int number;
|
||||
|
||||
private final OffsetDateTime dueOn;
|
||||
|
||||
Milestone(String name, int number, OffsetDateTime dueOn) {
|
||||
this.name = name;
|
||||
this.number = number;
|
||||
this.dueOn = dueOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public OffsetDateTime getDueOn() {
|
||||
return this.dueOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name + " (" + this.number + ")";
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
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(
|
||||
Collections.singletonList(new MappingJackson2HttpMessageConverter(new ObjectMapper())));
|
||||
restTemplate.getInterceptors().add((request, body, execution) -> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.HttpClientErrorException.Forbidden;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
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, String body, List<String> labels, Milestone milestone) {
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("title", title);
|
||||
if (milestone != null) {
|
||||
requestBody.put("milestone", milestone.getNumber());
|
||||
}
|
||||
if (!labels.isEmpty()) {
|
||||
requestBody.put("labels", labels);
|
||||
}
|
||||
requestBody.put("body", body);
|
||||
try {
|
||||
ResponseEntity<Map> response = this.rest.postForEntity("issues", requestBody, Map.class);
|
||||
// See gh-30304
|
||||
sleep(Duration.ofSeconds(3));
|
||||
return (Integer) response.getBody().get("number");
|
||||
}
|
||||
catch (RestClientException ex) {
|
||||
if (ex instanceof Forbidden forbidden) {
|
||||
System.out.println("Received 403 response with headers " + forbidden.getResponseHeaders());
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getLabels() {
|
||||
return new HashSet<>(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"),
|
||||
(milestone.get("due_on") != null) ? OffsetDateTime.parse((String) milestone.get("due_on")) : null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Issue> findIssues(List<String> labels, Milestone milestone) {
|
||||
return get(
|
||||
"issues?per_page=100&state=all&labels=" + String.join(",", labels) + "&milestone="
|
||||
+ milestone.getNumber(),
|
||||
(issue) -> new Issue(this.rest, (Integer) issue.get("number"), (String) issue.get("title"),
|
||||
Issue.State.of((String) issue.get("state"))));
|
||||
}
|
||||
|
||||
@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);
|
||||
return ((List<Map<String, Object>>) response.getBody()).stream().map(mapper).toList();
|
||||
}
|
||||
|
||||
private static void sleep(Duration duration) {
|
||||
try {
|
||||
Thread.sleep(duration.toMillis());
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 otherVersion)
|
||||
? otherVersion.comparableVersion : new ComparableVersion(other.toString());
|
||||
return this.comparableVersion.compareTo(otherComparable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpgrade(DependencyVersion candidate, boolean movingToSnapshots) {
|
||||
ComparableVersion comparableCandidate = (candidate instanceof AbstractDependencyVersion)
|
||||
? ((AbstractDependencyVersion) candidate).comparableVersion
|
||||
: new ComparableVersion(candidate.toString());
|
||||
return comparableCandidate.compareTo(this.comparableVersion) > 0;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.version;
|
||||
|
||||
import java.util.Objects;
|
||||
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;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 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(toNormalizedString(artifactVersion)));
|
||||
this.artifactVersion = artifactVersion;
|
||||
}
|
||||
|
||||
private static String toNormalizedString(ArtifactVersion artifactVersion) {
|
||||
String versionString = artifactVersion.toString();
|
||||
if (versionString.endsWith(".RELEASE")) {
|
||||
return versionString.substring(0, versionString.length() - 8);
|
||||
}
|
||||
if (versionString.endsWith(".BUILD-SNAPSHOT")) {
|
||||
return versionString.substring(0, versionString.length() - 15) + "-SNAPSHOT";
|
||||
}
|
||||
return versionString;
|
||||
}
|
||||
|
||||
protected ArtifactVersionDependencyVersion(ArtifactVersion artifactVersion, ComparableVersion comparableVersion) {
|
||||
super(comparableVersion);
|
||||
this.artifactVersion = artifactVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMajor(DependencyVersion other) {
|
||||
if (other instanceof ReleaseTrainDependencyVersion) {
|
||||
return false;
|
||||
}
|
||||
return extractArtifactVersionDependencyVersion(other).map(this::isSameMajor).orElse(true);
|
||||
}
|
||||
|
||||
private boolean isSameMajor(ArtifactVersionDependencyVersion other) {
|
||||
return this.artifactVersion.getMajorVersion() == other.artifactVersion.getMajorVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMinor(DependencyVersion other) {
|
||||
if (other instanceof ReleaseTrainDependencyVersion) {
|
||||
return false;
|
||||
}
|
||||
return extractArtifactVersionDependencyVersion(other).map(this::isSameMinor).orElse(true);
|
||||
}
|
||||
|
||||
private boolean isSameMinor(ArtifactVersionDependencyVersion other) {
|
||||
return isSameMajor(other) && this.artifactVersion.getMinorVersion() == other.artifactVersion.getMinorVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpgrade(DependencyVersion candidate, boolean movingToSnapshots) {
|
||||
if (!(candidate instanceof ArtifactVersionDependencyVersion)) {
|
||||
return false;
|
||||
}
|
||||
ArtifactVersion other = ((ArtifactVersionDependencyVersion) candidate).artifactVersion;
|
||||
if (this.artifactVersion.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (sameMajorMinorIncremental(other)) {
|
||||
if (!StringUtils.hasLength(this.artifactVersion.getQualifier())
|
||||
|| "RELEASE".equals(this.artifactVersion.getQualifier())) {
|
||||
return false;
|
||||
}
|
||||
if (isSnapshot()) {
|
||||
return true;
|
||||
}
|
||||
else if (((ArtifactVersionDependencyVersion) candidate).isSnapshot()) {
|
||||
return movingToSnapshots;
|
||||
}
|
||||
}
|
||||
return super.isUpgrade(candidate, movingToSnapshots);
|
||||
}
|
||||
|
||||
private boolean sameMajorMinorIncremental(ArtifactVersion other) {
|
||||
return this.artifactVersion.getMajorVersion() == other.getMajorVersion()
|
||||
&& this.artifactVersion.getMinorVersion() == other.getMinorVersion()
|
||||
&& this.artifactVersion.getIncrementalVersion() == other.getIncrementalVersion();
|
||||
}
|
||||
|
||||
private boolean isSnapshot() {
|
||||
return "SNAPSHOT".equals(this.artifactVersion.getQualifier())
|
||||
|| "BUILD".equals(this.artifactVersion.getQualifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnapshotFor(DependencyVersion candidate) {
|
||||
if (!isSnapshot() || !(candidate instanceof ArtifactVersionDependencyVersion)) {
|
||||
return false;
|
||||
}
|
||||
return sameMajorMinorIncremental(((ArtifactVersionDependencyVersion) candidate).artifactVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DependencyVersion other) {
|
||||
if (other instanceof ArtifactVersionDependencyVersion otherArtifactDependencyVersion) {
|
||||
ArtifactVersion otherArtifactVersion = otherArtifactDependencyVersion.artifactVersion;
|
||||
if ((!Objects.equals(this.artifactVersion.getQualifier(), otherArtifactVersion.getQualifier()))
|
||||
&& "snapshot".equalsIgnoreCase(otherArtifactVersion.getQualifier())
|
||||
&& otherArtifactVersion.getMajorVersion() == this.artifactVersion.getMajorVersion()
|
||||
&& otherArtifactVersion.getMinorVersion() == this.artifactVersion.getMinorVersion()
|
||||
&& otherArtifactVersion.getIncrementalVersion() == this.artifactVersion.getIncrementalVersion()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return super.compareTo(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.artifactVersion.toString();
|
||||
}
|
||||
|
||||
protected Optional<ArtifactVersionDependencyVersion> extractArtifactVersionDependencyVersion(
|
||||
DependencyVersion other) {
|
||||
ArtifactVersionDependencyVersion artifactVersion = null;
|
||||
if (other instanceof ArtifactVersionDependencyVersion otherVersion) {
|
||||
artifactVersion = otherVersion;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.bom.bomr.version;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
|
||||
/**
|
||||
* A specialization of {@link ArtifactVersionDependencyVersion} for calendar versions.
|
||||
* Calendar versions are always considered to be newer than
|
||||
* {@link ReleaseTrainDependencyVersion release train versions}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class CalendarVersionDependencyVersion extends ArtifactVersionDependencyVersion {
|
||||
|
||||
private static final Pattern CALENDAR_VERSION_PATTERN = Pattern.compile("\\d{4}\\.\\d+\\.\\d+(-.+)?");
|
||||
|
||||
protected CalendarVersionDependencyVersion(ArtifactVersion artifactVersion) {
|
||||
super(artifactVersion);
|
||||
}
|
||||
|
||||
protected CalendarVersionDependencyVersion(ArtifactVersion artifactVersion, ComparableVersion comparableVersion) {
|
||||
super(artifactVersion, comparableVersion);
|
||||
}
|
||||
|
||||
static CalendarVersionDependencyVersion parse(String version) {
|
||||
if (!CALENDAR_VERSION_PATTERN.matcher(version).matches()) {
|
||||
return null;
|
||||
}
|
||||
ArtifactVersion artifactVersion = new DefaultArtifactVersion(version);
|
||||
if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(version)) {
|
||||
return null;
|
||||
}
|
||||
return new CalendarVersionDependencyVersion(artifactVersion);
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 has the same major and minor versions as the
|
||||
* {@code other} version.
|
||||
* @param other the version to test
|
||||
* @return {@code true} if this version has the same major and minor, otherwise
|
||||
* {@code false}
|
||||
*/
|
||||
boolean isSameMinor(DependencyVersion other);
|
||||
|
||||
/**
|
||||
* Returns whether this version has the same major version as the {@code other}
|
||||
* version.
|
||||
* @param other the version to test
|
||||
* @return {@code true} if this version has the same major, otherwise {@code false}
|
||||
*/
|
||||
boolean isSameMajor(DependencyVersion other);
|
||||
|
||||
/**
|
||||
* Returns whether the given {@code candidate} is an upgrade of this version.
|
||||
* @param candidate the version to consider
|
||||
* @param movingToSnapshots whether the upgrade is to be considered as part of moving
|
||||
* to snaphots
|
||||
* @return {@code true} if the candidate is an upgrade, otherwise false
|
||||
*/
|
||||
boolean isUpgrade(DependencyVersion candidate, boolean movingToSnapshots);
|
||||
|
||||
/**
|
||||
* Returns whether this version is a snapshot for the given {@code candidate}.
|
||||
* @param candidate the version to consider
|
||||
* @return {@code true} if this version is a snapshot for the candidate, otherwise
|
||||
* false
|
||||
*/
|
||||
boolean isSnapshotFor(DependencyVersion candidate);
|
||||
|
||||
static DependencyVersion parse(String version) {
|
||||
List<Function<String, DependencyVersion>> parsers = Arrays.asList(CalendarVersionDependencyVersion::parse,
|
||||
ArtifactVersionDependencyVersion::parse, ReleaseTrainDependencyVersion::parse,
|
||||
MultipleComponentsDependencyVersion::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");
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 or five components
|
||||
* that cannot be handled by {@link ArtifactVersion} because the fourth component is
|
||||
* numeric.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
final class MultipleComponentsDependencyVersion extends ArtifactVersionDependencyVersion {
|
||||
|
||||
private final String original;
|
||||
|
||||
private MultipleComponentsDependencyVersion(ArtifactVersion artifactVersion, String original) {
|
||||
super(artifactVersion, new ComparableVersion(original));
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.original;
|
||||
}
|
||||
|
||||
static MultipleComponentsDependencyVersion parse(String input) {
|
||||
String[] components = input.split("\\.");
|
||||
if (components.length == 4 || components.length == 5) {
|
||||
ArtifactVersion artifactVersion = new DefaultArtifactVersion(
|
||||
components[0] + "." + components[1] + "." + components[2]);
|
||||
if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(input)) {
|
||||
return null;
|
||||
}
|
||||
return new MultipleComponentsDependencyVersion(artifactVersion, input);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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]+)-((BUILD-SNAPSHOT)|([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 otherReleaseTrain)) {
|
||||
return -1;
|
||||
}
|
||||
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 isUpgrade(DependencyVersion candidate, boolean movingToSnapshots) {
|
||||
if (!(candidate instanceof ReleaseTrainDependencyVersion)) {
|
||||
return true;
|
||||
}
|
||||
ReleaseTrainDependencyVersion candidateReleaseTrain = (ReleaseTrainDependencyVersion) candidate;
|
||||
int comparison = this.releaseTrain.compareTo(candidateReleaseTrain.releaseTrain);
|
||||
if (comparison != 0) {
|
||||
return comparison < 0;
|
||||
}
|
||||
if (movingToSnapshots && !isSnapshot() && candidateReleaseTrain.isSnapshot()) {
|
||||
return true;
|
||||
}
|
||||
comparison = this.type.compareTo(candidateReleaseTrain.type);
|
||||
if (comparison != 0) {
|
||||
return comparison < 0;
|
||||
}
|
||||
return Integer.compare(this.version, candidateReleaseTrain.version) < 0;
|
||||
}
|
||||
|
||||
private boolean isSnapshot() {
|
||||
return "BUILD-SNAPSHOT".equals(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnapshotFor(DependencyVersion candidate) {
|
||||
if (!isSnapshot() || !(candidate instanceof ReleaseTrainDependencyVersion)) {
|
||||
return false;
|
||||
}
|
||||
ReleaseTrainDependencyVersion candidateReleaseTrain = (ReleaseTrainDependencyVersion) candidate;
|
||||
return this.releaseTrain.equals(candidateReleaseTrain.releaseTrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMajor(DependencyVersion other) {
|
||||
return isSameReleaseTrain(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMinor(DependencyVersion other) {
|
||||
return isSameReleaseTrain(other);
|
||||
}
|
||||
|
||||
private boolean isSameReleaseTrain(DependencyVersion other) {
|
||||
if (other instanceof CalendarVersionDependencyVersion) {
|
||||
return false;
|
||||
}
|
||||
if (other instanceof ReleaseTrainDependencyVersion otherReleaseTrain) {
|
||||
return otherReleaseTrain.releaseTrain.equals(this.releaseTrain);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@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),
|
||||
StringUtils.hasLength(matcher.group(3)) ? matcher.group(3) : matcher.group(4),
|
||||
(StringUtils.hasLength(matcher.group(5))) ? Integer.parseInt(matcher.group(5)) : 0, input);
|
||||
}
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 isSameMajor(DependencyVersion other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMinor(DependencyVersion other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnapshotFor(DependencyVersion candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static UnstructuredDependencyVersion parse(String version) {
|
||||
return new UnstructuredDependencyVersion(version);
|
||||
}
|
||||
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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 java.util.stream.Stream;
|
||||
|
||||
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();
|
||||
try (Stream<Path> pathStream = Files.walk(root)) {
|
||||
pathStream.filter(Files::isRegularFile)
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.classpath;
|
||||
|
||||
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() {
|
||||
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.equals("javax.cache")) {
|
||||
return false;
|
||||
}
|
||||
if (group.equals("javax.money")) {
|
||||
return false;
|
||||
}
|
||||
if (group.equals("org.codehaus.groovy")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.eclipse.jetty.toolchain")) {
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (group.startsWith("org.jboss.spec")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.apache.geronimo.specs")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("com.sun.activation")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.classpath;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector;
|
||||
import org.gradle.api.artifacts.result.DependencyResult;
|
||||
import org.gradle.api.artifacts.result.ResolutionResult;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* Tasks to check that none of classpath's direct dependencies are unconstrained.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class CheckClasspathForUnconstrainedDirectDependencies extends DefaultTask {
|
||||
|
||||
private Configuration classpath;
|
||||
|
||||
public CheckClasspathForUnconstrainedDirectDependencies() {
|
||||
getOutputs().upToDateWhen((task) -> true);
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
public void setClasspath(Configuration classpath) {
|
||||
this.classpath = classpath;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void checkForUnconstrainedDirectDependencies() {
|
||||
ResolutionResult resolutionResult = this.classpath.getIncoming().getResolutionResult();
|
||||
Set<? extends DependencyResult> dependencies = resolutionResult.getRoot().getDependencies();
|
||||
Set<String> unconstrainedDependencies = dependencies.stream()
|
||||
.map(DependencyResult::getRequested)
|
||||
.filter(ModuleComponentSelector.class::isInstance)
|
||||
.map(ModuleComponentSelector.class::cast)
|
||||
.map((selector) -> selector.getGroup() + ":" + selector.getModule())
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> constraints = resolutionResult.getAllDependencies()
|
||||
.stream()
|
||||
.filter(DependencyResult::isConstraint)
|
||||
.map(DependencyResult::getRequested)
|
||||
.filter(ModuleComponentSelector.class::isInstance)
|
||||
.map(ModuleComponentSelector.class::cast)
|
||||
.map((selector) -> selector.getGroup() + ":" + selector.getModule())
|
||||
.collect(Collectors.toSet());
|
||||
unconstrainedDependencies.removeAll(constraints);
|
||||
if (!unconstrainedDependencies.isEmpty()) {
|
||||
throw new GradleException("Found unconstrained direct dependencies: " + unconstrainedDependencies);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.classpath;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.ConfigurationContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.ExcludeRule;
|
||||
import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* A {@link Task} for checking the classpath for unnecessary exclusions.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class CheckClasspathForUnnecessaryExclusions extends DefaultTask {
|
||||
|
||||
private static final Map<String, String> SPRING_BOOT_DEPENDENCIES_PROJECT = Collections.singletonMap("path",
|
||||
":spring-boot-project:spring-boot-dependencies");
|
||||
|
||||
private final Map<String, Set<String>> exclusionsByDependencyId = new TreeMap<>();
|
||||
|
||||
private final Map<String, Dependency> dependencyById = new HashMap<>();
|
||||
|
||||
private final Dependency platform;
|
||||
|
||||
private final DependencyHandler dependencyHandler;
|
||||
|
||||
private final ConfigurationContainer configurations;
|
||||
|
||||
private Configuration classpath;
|
||||
|
||||
@Inject
|
||||
public CheckClasspathForUnnecessaryExclusions(DependencyHandler dependencyHandler,
|
||||
ConfigurationContainer configurations) {
|
||||
this.dependencyHandler = getProject().getDependencies();
|
||||
this.configurations = getProject().getConfigurations();
|
||||
this.platform = this.dependencyHandler
|
||||
.create(this.dependencyHandler.platform(this.dependencyHandler.project(SPRING_BOOT_DEPENDENCIES_PROJECT)));
|
||||
getOutputs().upToDateWhen((task) -> true);
|
||||
}
|
||||
|
||||
public void setClasspath(Configuration classpath) {
|
||||
this.classpath = classpath;
|
||||
this.exclusionsByDependencyId.clear();
|
||||
this.dependencyById.clear();
|
||||
classpath.getAllDependencies().all(this::processDependency);
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
private void processDependency(Dependency dependency) {
|
||||
if (dependency instanceof ModuleDependency moduleDependency) {
|
||||
processDependency(moduleDependency);
|
||||
}
|
||||
}
|
||||
|
||||
private void processDependency(ModuleDependency dependency) {
|
||||
String dependencyId = getId(dependency);
|
||||
TreeSet<String> exclusions = dependency.getExcludeRules()
|
||||
.stream()
|
||||
.map(this::getId)
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
this.exclusionsByDependencyId.put(dependencyId, exclusions);
|
||||
if (!exclusions.isEmpty()) {
|
||||
this.dependencyById.put(dependencyId, getProject().getDependencies().create(dependencyId));
|
||||
}
|
||||
}
|
||||
|
||||
@Input
|
||||
Map<String, Set<String>> getExclusionsByDependencyId() {
|
||||
return this.exclusionsByDependencyId;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void checkForUnnecessaryExclusions() {
|
||||
Map<String, Set<String>> unnecessaryExclusions = new HashMap<>();
|
||||
this.exclusionsByDependencyId.forEach((dependencyId, exclusions) -> {
|
||||
if (!exclusions.isEmpty()) {
|
||||
Dependency toCheck = this.dependencyById.get(dependencyId);
|
||||
this.configurations.detachedConfiguration(toCheck, this.platform)
|
||||
.getIncoming()
|
||||
.getArtifacts()
|
||||
.getArtifacts()
|
||||
.stream()
|
||||
.map(this::getId)
|
||||
.forEach(exclusions::remove);
|
||||
removeProfileExclusions(dependencyId, exclusions);
|
||||
if (!exclusions.isEmpty()) {
|
||||
unnecessaryExclusions.put(dependencyId, exclusions);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!unnecessaryExclusions.isEmpty()) {
|
||||
throw new GradleException(getExceptionMessage(unnecessaryExclusions));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeProfileExclusions(String dependencyId, Set<String> exclusions) {
|
||||
if ("org.xmlunit:xmlunit-core".equals(dependencyId)) {
|
||||
exclusions.remove("javax.xml.bind:jaxb-api");
|
||||
}
|
||||
}
|
||||
|
||||
private String getExceptionMessage(Map<String, Set<String>> unnecessaryExclusions) {
|
||||
StringBuilder message = new StringBuilder("Unnecessary exclusions detected:");
|
||||
for (Entry<String, Set<String>> entry : unnecessaryExclusions.entrySet()) {
|
||||
message.append(String.format("%n %s", entry.getKey()));
|
||||
for (String exclusion : entry.getValue()) {
|
||||
message.append(String.format("%n %s", exclusion));
|
||||
}
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
private String getId(ResolvedArtifactResult artifact) {
|
||||
return getId((ModuleComponentIdentifier) artifact.getId().getComponentIdentifier());
|
||||
}
|
||||
|
||||
private String getId(ModuleDependency dependency) {
|
||||
return dependency.getGroup() + ":" + dependency.getName();
|
||||
}
|
||||
|
||||
private String getId(ExcludeRule rule) {
|
||||
return rule.getGroup() + ":" + rule.getModule();
|
||||
}
|
||||
|
||||
private String getId(ModuleComponentIdentifier identifier) {
|
||||
return identifier.getGroup() + ":" + identifier.getModule();
|
||||
}
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.cli;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Collections;
|
||||
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.Task;
|
||||
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.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.TaskExecutionException;
|
||||
|
||||
import org.springframework.boot.build.artifacts.ArtifactRelease;
|
||||
|
||||
/**
|
||||
* A {@link Task} for creating a Homebrew formula manifest.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class HomebrewFormula extends DefaultTask {
|
||||
|
||||
private Provider<RegularFile> archive;
|
||||
|
||||
private File template;
|
||||
|
||||
private File outputDir;
|
||||
|
||||
public HomebrewFormula() {
|
||||
getInputs().property("version", getProject().provider(getProject()::getVersion));
|
||||
}
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public RegularFile getArchive() {
|
||||
return this.archive.get();
|
||||
}
|
||||
|
||||
public void setArchive(Provider<RegularFile> archive) {
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
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);
|
||||
copy.expand(getProperties(additionalProperties));
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, Object> getProperties(Map<String, Object> additionalProperties) {
|
||||
Map<String, Object> properties = new HashMap<>(additionalProperties);
|
||||
Project project = getProject();
|
||||
properties.put("hash", sha256(this.archive.get().getAsFile()));
|
||||
properties.put("repo", ArtifactRelease.forProject(project).getDownloadRepo());
|
||||
properties.put("project", project);
|
||||
return 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);
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void createFormula() {
|
||||
createDescriptor(Collections.emptyMap());
|
||||
}
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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("|===");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.VersionProperty;
|
||||
|
||||
/**
|
||||
* Task for documenting available version properties.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
*/
|
||||
public class DocumentVersionProperties extends DefaultTask {
|
||||
|
||||
private final SetProperty<VersionProperty> versionProperties;
|
||||
|
||||
private File outputFile;
|
||||
|
||||
@Inject
|
||||
public DocumentVersionProperties(ObjectFactory objectFactory) {
|
||||
this.versionProperties = objectFactory.setProperty(VersionProperty.class);
|
||||
}
|
||||
|
||||
@Input
|
||||
public SetProperty<VersionProperty> getVersionProperties() {
|
||||
return this.versionProperties;
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public File getOutputFile() {
|
||||
return this.outputFile;
|
||||
}
|
||||
|
||||
public void setOutputFile(File outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void documentVersionProperties() throws IOException {
|
||||
this.outputFile.getParentFile().mkdirs();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile))) {
|
||||
writer.println("|===");
|
||||
writer.println("| Library | Version Property");
|
||||
for (VersionProperty versionProperty : this.versionProperties.get()) {
|
||||
writer.println();
|
||||
writer.printf("| `%s`%n", versionProperty.getLibraryName());
|
||||
writer.printf("| `%s`%n", versionProperty.getVersionProperty());
|
||||
}
|
||||
writer.println("|===");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.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.DefaultTask;
|
||||
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.tasks.Internal;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.platform.base.Platform;
|
||||
|
||||
import org.springframework.boot.build.bom.BomExtension;
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
/**
|
||||
* {@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 DefaultTask {
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
private final Map<String, String> versionConstraints = new TreeMap<>();
|
||||
|
||||
private final Set<ConstrainedVersion> constrainedVersions = new TreeSet<>();
|
||||
|
||||
private final Set<VersionProperty> versionProperties = new TreeSet<>();
|
||||
|
||||
private final List<String> projectPaths = new ArrayList<>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Internal
|
||||
public Set<VersionProperty> getVersionProperties() {
|
||||
return this.versionProperties;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void extractVersionConstraints() {
|
||||
this.configuration.resolve();
|
||||
for (String projectPath : this.projectPaths) {
|
||||
extractVersionProperties(projectPath);
|
||||
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 extractVersionProperties(String projectPath) {
|
||||
Object bom = getProject().project(projectPath).getExtensions().getByName("bom");
|
||||
BomExtension bomExtension = (BomExtension) bom;
|
||||
for (Library lib : bomExtension.getLibraries()) {
|
||||
String versionProperty = lib.getVersionProperty();
|
||||
if (versionProperty != null) {
|
||||
this.versionProperties.add(new VersionProperty(lib.getName(), versionProperty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final class VersionProperty implements Comparable<VersionProperty>, Serializable {
|
||||
|
||||
private final String libraryName;
|
||||
|
||||
private final String versionProperty;
|
||||
|
||||
public VersionProperty(String libraryName, String versionProperty) {
|
||||
this.libraryName = libraryName;
|
||||
this.versionProperty = versionProperty;
|
||||
}
|
||||
|
||||
public String getLibraryName() {
|
||||
return this.libraryName;
|
||||
}
|
||||
|
||||
public String getVersionProperty() {
|
||||
return this.versionProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(VersionProperty other) {
|
||||
int groupComparison = this.libraryName.compareToIgnoreCase(other.libraryName);
|
||||
if (groupComparison != 0) {
|
||||
return groupComparison;
|
||||
}
|
||||
return this.versionProperty.compareTo(other.versionProperty);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 Asciidoc {
|
||||
|
||||
private final StringBuilder content;
|
||||
|
||||
Asciidoc() {
|
||||
this.content = new StringBuilder();
|
||||
}
|
||||
|
||||
Asciidoc appendWithHardLineBreaks(Object... items) {
|
||||
for (Object item : items) {
|
||||
appendln("`+", item, "+` +");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Asciidoc appendln(Object... items) {
|
||||
return append(items).newLine();
|
||||
}
|
||||
|
||||
Asciidoc append(Object... items) {
|
||||
for (Object item : items) {
|
||||
this.content.append(item);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Asciidoc newLine() {
|
||||
return append(System.lineSeparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.content.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* {@link SourceTask} that checks additional Spring configuration metadata files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class CheckAdditionalSpringConfigurationMetadata extends SourceTask {
|
||||
|
||||
private final RegularFileProperty reportLocation;
|
||||
|
||||
public CheckAdditionalSpringConfigurationMetadata() {
|
||||
this.reportLocation = getProject().getObjects().fileProperty();
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getReportLocation() {
|
||||
return this.reportLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileTree getSource() {
|
||||
return super.getSource();
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void check() throws JsonParseException, IOException {
|
||||
Report report = createReport();
|
||||
File reportFile = getReportLocation().get().getAsFile();
|
||||
Files.write(reportFile.toPath(), report, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
if (report.hasProblems()) {
|
||||
throw new GradleException(
|
||||
"Problems found in additional Spring configuration metadata. See " + reportFile + " for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Report createReport() throws IOException, JsonParseException, JsonMappingException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
Report report = new Report();
|
||||
for (File file : getSource().getFiles()) {
|
||||
Analysis analysis = report.analysis(getProject().getProjectDir().toPath().relativize(file.toPath()));
|
||||
Map<String, Object> json = objectMapper.readValue(file, Map.class);
|
||||
check("groups", json, analysis);
|
||||
check("properties", json, analysis);
|
||||
check("hints", json, analysis);
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void check(String key, Map<String, Object> json, Analysis analysis) {
|
||||
List<Map<String, Object>> groups = (List<Map<String, Object>>) json.get(key);
|
||||
List<String> names = groups.stream().map((group) -> (String) group.get("name")).toList();
|
||||
List<String> sortedNames = sortedCopy(names);
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
String actual = names.get(i);
|
||||
String expected = sortedNames.get(i);
|
||||
if (!actual.equals(expected)) {
|
||||
analysis.problems.add("Wrong order at $." + key + "[" + i + "].name - expected '" + expected
|
||||
+ "' but found '" + actual + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> sortedCopy(Collection<String> original) {
|
||||
List<String> copy = new ArrayList<>(original);
|
||||
Collections.sort(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static final class Report implements Iterable<String> {
|
||||
|
||||
private final List<Analysis> analyses = new ArrayList<>();
|
||||
|
||||
private Analysis analysis(Path path) {
|
||||
Analysis analysis = new Analysis(path);
|
||||
this.analyses.add(analysis);
|
||||
return analysis;
|
||||
}
|
||||
|
||||
private boolean hasProblems() {
|
||||
for (Analysis analysis : this.analyses) {
|
||||
if (!analysis.problems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
List<String> lines = new ArrayList<>();
|
||||
for (Analysis analysis : this.analyses) {
|
||||
lines.add(analysis.source.toString());
|
||||
lines.add("");
|
||||
if (analysis.problems.isEmpty()) {
|
||||
lines.add("No problems found.");
|
||||
}
|
||||
else {
|
||||
lines.addAll(analysis.problems);
|
||||
}
|
||||
lines.add("");
|
||||
}
|
||||
return lines.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Analysis {
|
||||
|
||||
private final List<String> problems = new ArrayList<>();
|
||||
|
||||
private final Path source;
|
||||
|
||||
private Analysis(Path source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.context.properties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* {@link SourceTask} that checks {@code spring-configuration-metadata.json} files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class CheckSpringConfigurationMetadata extends DefaultTask {
|
||||
|
||||
private List<String> exclusions = new ArrayList<>();
|
||||
|
||||
private final RegularFileProperty reportLocation;
|
||||
|
||||
private final RegularFileProperty metadataLocation;
|
||||
|
||||
public CheckSpringConfigurationMetadata() {
|
||||
this.metadataLocation = getProject().getObjects().fileProperty();
|
||||
this.reportLocation = getProject().getObjects().fileProperty();
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getReportLocation() {
|
||||
return this.reportLocation;
|
||||
}
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public RegularFileProperty getMetadataLocation() {
|
||||
return this.metadataLocation;
|
||||
}
|
||||
|
||||
public void setExclusions(List<String> exclusions) {
|
||||
this.exclusions = exclusions;
|
||||
}
|
||||
|
||||
@Input
|
||||
public List<String> getExclusions() {
|
||||
return this.exclusions;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void check() throws JsonParseException, IOException {
|
||||
Report report = createReport();
|
||||
File reportFile = getReportLocation().get().getAsFile();
|
||||
Files.write(reportFile.toPath(), report, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
if (report.hasProblems()) {
|
||||
throw new GradleException(
|
||||
"Problems found in Spring configuration metadata. See " + reportFile + " for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Report createReport() throws IOException, JsonParseException, JsonMappingException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
File file = this.metadataLocation.get().getAsFile();
|
||||
Report report = new Report(getProject().getProjectDir().toPath().relativize(file.toPath()));
|
||||
Map<String, Object> json = objectMapper.readValue(file, Map.class);
|
||||
List<Map<String, Object>> properties = (List<Map<String, Object>>) json.get("properties");
|
||||
for (Map<String, Object> property : properties) {
|
||||
String name = (String) property.get("name");
|
||||
if (!isDeprecated(property) && !isDescribed(property) && !isExcluded(name)) {
|
||||
report.propertiesWithNoDescription.add(name);
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
private boolean isExcluded(String propertyName) {
|
||||
for (String exclusion : this.exclusions) {
|
||||
if (propertyName.equals(exclusion)) {
|
||||
return true;
|
||||
}
|
||||
if (exclusion.endsWith(".*")) {
|
||||
if (propertyName.startsWith(exclusion.substring(0, exclusion.length() - 2))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isDeprecated(Map<String, Object> property) {
|
||||
return (Map<String, Object>) property.get("deprecation") != null;
|
||||
}
|
||||
|
||||
private boolean isDescribed(Map<String, Object> property) {
|
||||
return property.get("description") != null;
|
||||
}
|
||||
|
||||
private static final class Report implements Iterable<String> {
|
||||
|
||||
private final List<String> propertiesWithNoDescription = new ArrayList<>();
|
||||
|
||||
private final Path source;
|
||||
|
||||
private Report(Path source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
private boolean hasProblems() {
|
||||
return !this.propertiesWithNoDescription.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add(this.source.toString());
|
||||
lines.add("");
|
||||
if (this.propertiesWithNoDescription.isEmpty()) {
|
||||
lines.add("No problems found.");
|
||||
}
|
||||
else {
|
||||
lines.add("The following properties have no description:");
|
||||
lines.add("");
|
||||
lines.addAll(this.propertiesWithNoDescription.stream().map((line) -> "\t" + line).toList());
|
||||
}
|
||||
lines.add("");
|
||||
return lines.iterator();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Table row regrouping a list of configuration properties sharing the same description.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompoundRow extends Row {
|
||||
|
||||
private final Set<String> propertyNames;
|
||||
|
||||
private final String description;
|
||||
|
||||
CompoundRow(Snippet snippet, String prefix, String description) {
|
||||
super(snippet, prefix);
|
||||
this.description = description;
|
||||
this.propertyNames = new TreeSet<>();
|
||||
}
|
||||
|
||||
void addProperty(ConfigurationProperty property) {
|
||||
this.propertyNames.add(property.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Asciidoc asciidoc) {
|
||||
asciidoc.append("|");
|
||||
asciidoc.append("[[" + getAnchor() + "]]");
|
||||
asciidoc.append("<<" + getAnchor() + ",");
|
||||
this.propertyNames.forEach(asciidoc::appendWithHardLineBreaks);
|
||||
asciidoc.appendln(">>");
|
||||
asciidoc.appendln("|+++", this.description, "+++");
|
||||
asciidoc.appendln("|");
|
||||
}
|
||||
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Configuration properties read from one or more
|
||||
* {@code META-INF/spring-configuration-metadata.json} files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class ConfigurationProperties {
|
||||
|
||||
private final Map<String, ConfigurationProperty> byName;
|
||||
|
||||
private ConfigurationProperties(List<ConfigurationProperty> properties) {
|
||||
Map<String, ConfigurationProperty> byName = new LinkedHashMap<>();
|
||||
for (ConfigurationProperty property : properties) {
|
||||
byName.put(property.getName(), property);
|
||||
}
|
||||
this.byName = Collections.unmodifiableMap(byName);
|
||||
}
|
||||
|
||||
ConfigurationProperty get(String propertyName) {
|
||||
return this.byName.get(propertyName);
|
||||
}
|
||||
|
||||
Stream<ConfigurationProperty> stream() {
|
||||
return this.byName.values().stream();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static ConfigurationProperties fromFiles(Iterable<File> files) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<ConfigurationProperty> properties = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
Map<String, Object> json = objectMapper.readValue(file, Map.class);
|
||||
for (Map<String, Object> property : (List<Map<String, Object>>) json.get("properties")) {
|
||||
properties.add(ConfigurationProperty.fromJsonProperties(property));
|
||||
}
|
||||
}
|
||||
return new ConfigurationProperties(properties);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException("Failed to load configuration metadata", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.context.properties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.file.RegularFile;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
import org.springframework.boot.build.processors.ProcessedAnnotationsPlugin;
|
||||
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>Disables incremental compilation to avoid property descriptions being lost.
|
||||
* <li>Configuring the additional metadata locations annotation processor compiler
|
||||
* argument.
|
||||
* <li>Adding the outputs of the processResources task as inputs of the compileJava task
|
||||
* to ensure that the additional metadata is available when the annotation processor runs.
|
||||
* <li>Registering a {@link CheckAdditionalSpringConfigurationMetadata} task and
|
||||
* configuring the {@code check} task to depend upon it.
|
||||
* <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";
|
||||
|
||||
/**
|
||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
||||
*/
|
||||
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
||||
|
||||
/**
|
||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
||||
*/
|
||||
public static final String CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkSpringConfigurationMetadata";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
configureConfigurationPropertiesAnnotationProcessor(project);
|
||||
disableIncrementalCompilation(project);
|
||||
configureAdditionalMetadataLocationsCompilerArgument(project);
|
||||
registerCheckAdditionalMetadataTask(project);
|
||||
registerCheckMetadataTask(project);
|
||||
addMetadataArtifact(project);
|
||||
});
|
||||
}
|
||||
|
||||
private void configureConfigurationPropertiesAnnotationProcessor(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")));
|
||||
project.getPlugins().apply(ProcessedAnnotationsPlugin.class);
|
||||
}
|
||||
|
||||
private void disableIncrementalCompilation(Project project) {
|
||||
SourceSet mainSourceSet = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
project.getTasks()
|
||||
.named(mainSourceSet.getCompileJavaTaskName(), JavaCompile.class)
|
||||
.configure((compileJava) -> compileJava.getOptions().setIncremental(false));
|
||||
}
|
||||
|
||||
private void addMetadataArtifact(Project project) {
|
||||
SourceSet mainSourceSet = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.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,
|
||||
mainSourceSet.getJava()
|
||||
.getDestinationDirectory()
|
||||
.dir("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);
|
||||
((Task) compileJava).getInputs()
|
||||
.files(project.getTasks().getByName(JavaPlugin.PROCESS_RESOURCES_TASK_NAME))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("processed resources");
|
||||
SourceSet mainSourceSet = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.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())));
|
||||
}
|
||||
|
||||
private void registerCheckAdditionalMetadataTask(Project project) {
|
||||
TaskProvider<CheckAdditionalSpringConfigurationMetadata> checkConfigurationMetadata = project.getTasks()
|
||||
.register(CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME,
|
||||
CheckAdditionalSpringConfigurationMetadata.class);
|
||||
checkConfigurationMetadata.configure((check) -> {
|
||||
SourceSet mainSourceSet = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
check.setSource(mainSourceSet.getResources());
|
||||
check.include("META-INF/additional-spring-configuration-metadata.json");
|
||||
check.getReportLocation()
|
||||
.set(project.getLayout()
|
||||
.getBuildDirectory()
|
||||
.file("reports/additional-spring-configuration-metadata/check.txt"));
|
||||
});
|
||||
project.getTasks()
|
||||
.named(LifecycleBasePlugin.CHECK_TASK_NAME)
|
||||
.configure((check) -> check.dependsOn(checkConfigurationMetadata));
|
||||
}
|
||||
|
||||
private void registerCheckMetadataTask(Project project) {
|
||||
TaskProvider<CheckSpringConfigurationMetadata> checkConfigurationMetadata = project.getTasks()
|
||||
.register(CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME, CheckSpringConfigurationMetadata.class);
|
||||
checkConfigurationMetadata.configure((check) -> {
|
||||
SourceSet mainSourceSet = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
Provider<RegularFile> metadataLocation = project.getTasks()
|
||||
.named(mainSourceSet.getCompileJavaTaskName(), JavaCompile.class)
|
||||
.flatMap((javaCompile) -> javaCompile.getDestinationDirectory()
|
||||
.file("META-INF/spring-configuration-metadata.json"));
|
||||
check.getMetadataLocation().set(metadataLocation);
|
||||
check.getReportLocation()
|
||||
.set(project.getLayout().getBuildDirectory().file("reports/spring-configuration-metadata/check.txt"));
|
||||
});
|
||||
project.getTasks()
|
||||
.named(LifecycleBasePlugin.CHECK_TASK_NAME)
|
||||
.configure((check) -> check.dependsOn(checkConfigurationMetadata));
|
||||
}
|
||||
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.Map;
|
||||
|
||||
/**
|
||||
* A configuration property.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
String getDisplayName() {
|
||||
return (getType() != null && getType().startsWith("java.util.Map")) ? getName() + ".*" : getName();
|
||||
}
|
||||
|
||||
String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
Object getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
boolean isDeprecated() {
|
||||
return this.deprecated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigurationProperty [name=" + this.name + ", type=" + this.type + "]";
|
||||
}
|
||||
|
||||
static ConfigurationProperty fromJsonProperties(Map<String, Object> property) {
|
||||
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");
|
||||
return new ConfigurationProperty(name, type, defaultValue, description, deprecated);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue