diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000..f5e2a12c --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,73 @@ +plugins { + id "java-gradle-plugin" + id "java" + id "groovy" +} + +sourceCompatibility = JavaVersion.VERSION_17 + +repositories { + mavenCentral() + gradlePluginPortal() + maven { url 'https://repo.spring.io/plugins-release/' } +} + +sourceSets { + main { + java { + srcDirs = [] + } + groovy { + srcDirs += ["src/main/java"] + } + } +} + +gradlePlugin { + plugins { + managementConfiguration { + id = "io.spring.convention.management-configuration" + implementationClass = "io.spring.gradle.convention.ManagementConfigurationPlugin" + } + sagan { + id = "org.springframework.security.sagan" + implementationClass = "org.springframework.gradle.sagan.SaganPlugin" + } + githubMilestone { + id = "org.springframework.github.milestone" + implementationClass = "org.springframework.gradle.github.milestones.GitHubMilestonePlugin" + } + propdeps { + id = "org.springframework.propdeps" + implementationClass = "org.springframework.gradle.propdeps.PropDepsPlugin" + } + } +} + +configurations { + implementation { + exclude module: 'groovy-all' + } +} + +dependencies { + + implementation localGroovy() + + implementation 'com.apollographql.apollo:apollo-runtime:2.4.5' + implementation 'com.google.code.gson:gson:2.8.8' + implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0' + implementation 'com.github.spullara.mustache.java:compiler:0.9.10' + implementation 'io.github.gradle-nexus:publish-plugin:1.1.0' + implementation 'io.projectreactor:reactor-core:3.4.11' + implementation 'io.spring.nohttp:nohttp-gradle:0.0.9' + implementation 'io.spring.gradle:dependency-management-plugin:1.0.10.RELEASE' + implementation 'net.sourceforge.htmlunit:htmlunit:2.55.0' + implementation 'net.sourceforge.saxon:saxon:9.1.0.8' + implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.3.2' + implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.3.2' + implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.24.20' + implementation 'org.hidetake:gradle-ssh-plugin:2.10.1' + implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy new file mode 100644 index 00000000..0ec21f22 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2016 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.GroovyPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.PluginManager +import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.springframework.gradle.CopyPropertiesPlugin +import org.springframework.gradle.propdeps.PropDepsEclipsePlugin +import org.springframework.gradle.propdeps.PropDepsIdeaPlugin +import org.springframework.gradle.propdeps.PropDepsPlugin + +/** + * Base Gradle API Plugin for all Spring Java project Gradle Plugins. + * + * @author Rob Winch + * @author John Blum + * @see org.gradle.api.Plugin + * @see org.gradle.api.Project + */ +abstract class AbstractSpringJavaPlugin implements Plugin { + + @Override + final void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager() + + pluginManager.apply(JavaPlugin.class) + pluginManager.apply(ManagementConfigurationPlugin.class) + + if (project.file("src/main/groovy").exists() + || project.file("src/test/groovy").exists() + || project.file("src/integration-test/groovy").exists()) { + + pluginManager.apply(GroovyPlugin.class) + } + + pluginManager.apply("io.spring.convention.repository") + pluginManager.apply(EclipseWtpPlugin) + pluginManager.apply(IdeaPlugin) + pluginManager.apply(PropDepsPlugin) + pluginManager.apply(PropDepsEclipsePlugin) + pluginManager.apply(PropDepsIdeaPlugin) + pluginManager.apply("io.spring.convention.tests-configuration") + pluginManager.apply("io.spring.convention.integration-test") + pluginManager.apply("io.spring.convention.springdependencymangement") + pluginManager.apply("io.spring.convention.dependency-set") + pluginManager.apply("io.spring.convention.javadoc-options") + pluginManager.apply("io.spring.convention.checkstyle") + pluginManager.apply(CopyPropertiesPlugin) + + project.jar { + manifest.attributes["Created-By"] = "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" + manifest.attributes["Implementation-Title"] = project.name + manifest.attributes["Implementation-Version"] = project.version + manifest.attributes["Automatic-Module-Name"] = project.name.replace('-', '.') + } + + project.test { + useJUnitPlatform() + } + + additionalPlugins(project); + } + + protected abstract void additionalPlugins(Project project); + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy new file mode 100644 index 00000000..16810d5f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2017 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * @author Rob Winch + * @author John Blum + */ +class ArtifactoryPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.plugins.apply('com.jfrog.artifactory') + + boolean isSnapshot = Utils.isSnapshot(project); + boolean isMilestone = Utils.isMilestone(project); + + project.artifactory { + contextUrl = 'https://repo.spring.io' + publish { + repository { + repoKey = isSnapshot ? 'libs-snapshot-local' + : isMilestone ? 'libs-milestone-local' + : 'libs-release-local' + if (project.hasProperty('artifactoryUsername')) { + username = artifactoryUsername + password = artifactoryPassword + } + } + defaults { + publications('mavenJava') + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy new file mode 100644 index 00000000..4482103e --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy @@ -0,0 +1,54 @@ +/* + * Copyright 2016-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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds and configures Checkstyle plugin. + * + * @author Vedran Pavic + * @author John Blum + */ +class CheckstylePlugin implements Plugin { + + static final String CHECKSTYLE_PATHNAME = 'etc/checkstyle' + + @Override + void apply(Project project) { + + project.plugins.withType(JavaPlugin) { + + def checkstyleDirectory = project.rootProject.file(CHECKSTYLE_PATHNAME) + + if (checkstyleDirectory.exists() && checkstyleDirectory.directory) { + + project.getPluginManager().apply('checkstyle') + project.dependencies.add('checkstyle', + 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.29') + project.dependencies.add('checkstyle', + 'io.spring.nohttp:nohttp-checkstyle:0.0.3.RELEASE') + + project.checkstyle { + configDirectory = checkstyleDirectory + toolVersion = '8.21' + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencyManagementExportTask.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencyManagementExportTask.groovy new file mode 100644 index 00000000..ada35839 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencyManagementExportTask.groovy @@ -0,0 +1,80 @@ +/* + * Copyright 2016-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 io.spring.gradle.convention + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.component.ModuleComponentSelector +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction + +/** + * Gradle API Task to output all the configured project & subproject (runtime) dependencies. + * + * @author Rob Winch + * @author John Blum + */ +class DependencyManagementExportTask extends DefaultTask { + + @Internal + def projects; + + @Input + String getProjectNames() { + return this.projects*.name + } + + @TaskAction + void dependencyManagementExport() throws IOException { + + def projects = this.projects ?: project.subprojects + project + + def configurations = projects*.configurations*.findAll { + [ 'testRuntime', 'integrationTestRuntime', 'grettyRunnerTomcat10', 'ajtools' ].contains(it.name) + } + + def dependencyResults = configurations*.incoming*.resolutionResult*.allDependencies.flatten() + + def moduleVersionVersions = dependencyResults + .findAll { r -> r.requested instanceof ModuleComponentSelector } + .collect { r -> r.selected.moduleVersion } + + def projectDependencies = projects.collect { p -> + "${p.group}:${p.name}:${p.version}".toString() + } as Set + + def dependencies = moduleVersionVersions + .collect { d -> "${d.group}:${d.name}:${d.version}".toString() } + .sort() as Set + + println '' + println '' + println 'dependencyManagement {' + println '\tdependencies {' + + dependencies + .findAll { d -> !projectDependencies.contains(d) } + .each { println "\t\tdependency '$it'" } + + println '\t}' + println '}' + println '' + println '' + println 'TIP Use this to find duplicates:\n$ sort gradle/dependency-management.gradle| uniq -c | grep -v \'^\\s*1\'' + println '' + println '' + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy new file mode 100644 index 00000000..ed70f174 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2017 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds sets of dependencies to make it easy to add a grouping of dependencies. The + * dependencies added are: + * + *
    + *
  • jstlDependencies
  • + *
  • seleniumDependencies
  • + *
  • slf4jDependencies
  • + *
+ * + * @author Rob Winch + * @author John Blum + */ +class DependencySetPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.ext.jstlDependencies = [ + "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api", + "org.glassfish.web:jakarta.servlet.jsp.jstl" + ] + + project.ext.seleniumDependencies = [ + "org.seleniumhq.selenium:htmlunit-driver", + "org.seleniumhq.selenium:selenium-support" + ] + + project.ext.slf4jDependencies = [ + "org.slf4j:slf4j-api", + "org.slf4j:jcl-over-slf4j", + "org.slf4j:log4j-over-slf4j", + "ch.qos.logback:logback-classic" + ] + + project.ext.testDependencies = [ + "junit:junit", + "org.junit.jupiter:junit-jupiter-api", + "org.junit.vintage:junit-vintage-engine", + "org.assertj:assertj-core", + "org.mockito:mockito-core", + "org.projectlombok:lombok", + "org.springframework:spring-test", + "org.springframework.data:spring-data-geode-test", + "edu.umd.cs.mtc:multithreadedtc" + ] + + project.plugins.withType(JavaPlugin) { + project.dependencies { + testImplementation project.testDependencies + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy new file mode 100644 index 00000000..ed97afa0 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2017 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * @author Rob Winch + * @author John Blum + */ +class DeployDocsPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.getPluginManager().apply('org.hidetake.ssh') + + project.ssh.settings { + knownHosts = allowAnyHosts + } + + project.remotes { + docs { + + retryCount = 5 // retry 5 times (default is 0) + retryWaitSec = 10 // wait 10 seconds between retries (default is 0) + role 'docs' + + host = project.hasProperty('deployDocsHost') + ? project.findProperty('deployDocsHost') + : 'docs-ip.spring.io' + + user = project.findProperty('deployDocsSshUsername') + + identity = project.hasProperty('deployDocsSshKeyPath') + ? project.file(project.findProperty('deployDocsSshKeyPath')) + : project.hasProperty('deployDocsSshKey') + ? project.findProperty('deployDocsSshKey') + : identity + + passphrase = project.hasProperty('deployDocsSshPassphrase') + ? project.findProperty('deployDocsSshPassphrase') + : passphrase + } + } + + project.task('deployDocs') { + dependsOn 'docsZip' + doFirst { + project.ssh.run { + session(project.remotes.docs) { + + def now = System.currentTimeMillis() + def name = project.rootProject.name + def version = project.rootProject.version + def tempPath = "/tmp/${name}-${now}-docs/".replaceAll(' ', '_') + + execute "mkdir -p $tempPath" + + project.tasks.docsZip.outputs.each { o -> + put from: o.files, into: tempPath + } + + execute "unzip $tempPath*.zip -d $tempPath" + + def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/docs/${name}/${version}/" + + execute "rm -rf $extractPath" + execute "mkdir -p $extractPath" + execute "mv $tempPath/docs/* $extractPath" + execute "chmod -R g+w $extractPath" + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy new file mode 100644 index 00000000..3786d530 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2017 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.PluginManager +import org.gradle.api.tasks.bundling.Zip + +/** + * Aggregates Asciidoc, Javadoc, and deploying of the docs into a single Gradle Plugin. + * + * @author Rob Winch + * @author John Blum + */ +class DocsPlugin implements Plugin { + + @Override + void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager(); + + pluginManager.apply("org.asciidoctor.jvm.convert"); + pluginManager.apply("org.asciidoctor.jvm.pdf"); + pluginManager.apply(AsciidoctorConventionPlugin); + pluginManager.apply(DeployDocsPlugin); + pluginManager.apply(JavadocApiPlugin); + + Task docsZip = project.tasks.create('docsZip', Zip) { + + archiveBaseName = project.rootProject.name + archiveClassifier = 'docs' + group = 'Distribution' + description = "Builds -${archiveClassifier} archive containing all Docs for deployment at docs.spring.io." + dependsOn 'api', 'asciidoctor' + + from(project.tasks.api.outputs) { + into 'api' + } + + into 'docs' + duplicatesStrategy 'exclude' + } + + Task docs = project.tasks.create("docs") { + group = 'Documentation' + description 'Aggregator Task to generate all documentation.' + dependsOn docsZip + } + + project.tasks.assemble.dependsOn docs + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy new file mode 100644 index 00000000..d8591f1c --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy @@ -0,0 +1,125 @@ +/* + * Copyright 2016-2018 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.GroovyPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.testing.Test +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.springframework.gradle.propdeps.PropDepsPlugin + +/** + * Adds support for integration tests to Java projects. + * + *
    + *
  • Adds integrationTestCompile and integrationTestRuntime configurations
  • + *
  • A new source test folder of src/integration-test/java has been added
  • + *
  • A task to run integration tests named integrationTest is added
  • + *
  • If Groovy plugin is added a new source test folder src/integration-test/groovy is added
  • + *
+ * + * @author Rob Winch + * @author John Blum + */ +class IntegrationTestPlugin implements Plugin { + + @Override + void apply(Project project) { + project.plugins.withType(JavaPlugin.class) { + applyJava(project) + } + } + + private applyJava(Project project) { + + if (!project.file('src/integration-test/').exists()) { + // Do not add any configuration if there are no (integration) tests to avoid adding Gretty. + return + } + + project.configurations { + integrationTestCompile { + extendsFrom testImplementation + } + integrationTestRuntime { + extendsFrom integrationTestCompile, testRuntime, testRuntimeOnly + } + } + + project.sourceSets { + integrationTest { + java.srcDir project.file('src/integration-test/java') + resources.srcDir project.file('src/integration-test/resources') + compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompile + runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntime + } + } + + Task integrationTestTask = project.tasks.create("integrationTest", Test) { + group = 'Verification' + description = 'Runs the integration tests.' + dependsOn 'jar' + testClassesDirs = project.sourceSets.integrationTest.output.classesDirs + classpath = project.sourceSets.integrationTest.runtimeClasspath + shouldRunAfter project.tasks.test + useJUnitPlatform() + } + + project.tasks.check.dependsOn integrationTestTask + + project.plugins.withType(IdeaPlugin) { + project.idea { + module { + testSourceDirs += project.file('src/integration-test/java') + scopes.TEST.plus += [ project.configurations.integrationTestCompile ] + } + } + } + + project.plugins.withType(GroovyPlugin) { + project.sourceSets { + integrationTest { + groovy.srcDirs project.file('src/integration-test/groovy') + } + } + project.plugins.withType(IdeaPlugin) { + project.idea { + module { + testSourceDirs += project.file('src/integration-test/groovy') + } + } + } + } + + project.plugins.withType(PropDepsPlugin) { + project.configurations { + integrationTestCompile { + extendsFrom optional, provided + } + } + } + + project.plugins.withType(EclipsePlugin) { + project.eclipse.classpath { + plusConfigurations += [ project.configurations.integrationTestCompile ] + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy new file mode 100644 index 00000000..adae14fd --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2018 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds a version of jacoco to use and makes check depend on jacocoTestReport. + * + * @author Rob Winch + * @author John Blum + */ +class JacocoPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.plugins.withType(JavaPlugin) { + + project.getPluginManager().apply("jacoco") + project.tasks.check.dependsOn project.tasks.jacocoTestReport + + project.jacoco { + toolVersion = '0.8.7' + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy new file mode 100644 index 00000000..ff0ac88b --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2016 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 io.spring.gradle.convention + +import java.util.regex.Pattern + +import org.gradle.api.Action +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.javadoc.Javadoc +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +/** + * @author Rob Winch + * @author John Blum + */ +class JavadocApiPlugin implements Plugin { + + Logger logger = LoggerFactory.getLogger(getClass()) + + Set excludes = Collections.singleton(Pattern.compile("test")) + + @Override + void apply(Project project) { + + Project rootProject = project.getRootProject() + + Javadoc api = project.tasks.create("api", Javadoc) + + api.setGroup("Documentation") + api.setDescription("Generates aggregated Javadoc API documentation.") + api.setDestinationDir(new File(project.getBuildDir(), "api")) + api.setMaxMemory("1024m") + + api.doLast { + if (JavaVersion.current().isJava11Compatible()) { + project.copy({ copy -> copy + .from(api.destinationDir) + .into(api.destinationDir) + .include("element-list") + .rename("element-list", "package-list") + }) + } + } + + Set subprojects = rootProject.getSubprojects() + + if (subprojects.isEmpty()) { + addProject(api, project) + } + + for (Project subproject : subprojects) { + addProject(api, subproject) + } + + project.getPluginManager().apply("io.spring.convention.javadoc-options") + } + + @SuppressWarnings("unused") + void setExcludes(String... excludes) { + excludes ?= new String[0] + this.excludes = new HashSet<>(excludes.length) + excludes.each {this.excludes.add(Pattern.compile(it)) } + } + + private void addProject(Javadoc api, Project project) { + + if (isProjectIncluded(project)) { + + logInfo("Add sources for project {}", project) + + project.getPlugins().withType(SpringModulePlugin.class).all { plugin -> + + JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class) + SourceSet mainSourceSet = java.getSourceSets().getByName("main") + + api.setSource(api.getSource().plus(mainSourceSet.getAllJava())) + + project.getTasks().withType(Javadoc.class).all(new Action() { + + @Override + void execute(Javadoc projectJavadoc) { + api.setClasspath(api.getClasspath().plus(projectJavadoc.getClasspath())) + } + }) + } + } + } + + private boolean isProjectIncluded(Project project) { + + for (Pattern exclude : this.excludes) { + if (exclude.matcher(project.getName()).matches()) { + logInfo("Skipping project {} because it was excluded by {}", project, exclude) + return false + } + } + + return true + } + + private void logInfo(String message, Object... arguments) { + this.logger.info(message, arguments) + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy new file mode 100644 index 00000000..a968efc8 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2016 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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.javadoc.Javadoc + +/** + * @author Rob Winch + * @author John Blum + */ +class JavadocOptionsPlugin implements Plugin { + + @Override + void apply(Project project) { + project.getTasks().withType(Javadoc).all { t-> + t.options.addStringOption('Xdoclint:none', '-quiet') + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java b/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java new file mode 100644 index 00000000..a7e74108 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaTestFixturesPlugin; +import org.gradle.api.plugins.PluginContainer; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.VariantVersionMappingStrategy; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +import org.springframework.gradle.propdeps.PropDepsPlugin; + +/** + * Creates a Management configuration that is appropriate for adding a platform so that it is not exposed externally. + * + * If the JavaPlugin is applied, the compileClasspath, runtimeClasspath, testCompileClasspath, and testRuntimeClasspath + * will extend from it. + * + * @author Rob Winch + * @author John Blum + */ +public class ManagementConfigurationPlugin implements Plugin { + + public static final String MANAGEMENT_CONFIGURATION_NAME = "management"; + + @Override + public void apply(Project project) { + + ConfigurationContainer configurations = project.getConfigurations(); + + configurations.create(MANAGEMENT_CONFIGURATION_NAME, management -> { + + management.setCanBeConsumed(false); + management.setCanBeResolved(false); + management.setVisible(false); + + PluginContainer plugins = project.getPlugins(); + + plugins.withType(JavaPlugin.class, javaPlugin -> { + configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + }); + + plugins.withType(JavaTestFixturesPlugin.class, javaTestFixturesPlugin -> { + configurations.getByName("testFixturesCompileClasspath").extendsFrom(management); + configurations.getByName("testFixturesRuntimeClasspath").extendsFrom(management); + }); + + plugins.withType(MavenPublishPlugin.class, mavenPublish -> { + + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + + publishing.getPublications().withType(MavenPublication.class, mavenPublication -> + mavenPublication.versionMapping(versions -> + versions.allVariants(VariantVersionMappingStrategy::fromResolutionResult))); + }); + + plugins.withType(PropDepsPlugin.class, propDepsPlugin -> { + configurations.getByName("optional").extendsFrom(management); + configurations.getByName("provided").extendsFrom(management); + }); + }); + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy new file mode 100644 index 00000000..c69e1e11 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy @@ -0,0 +1,35 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlatformPlugin +import org.springframework.gradle.CopyPropertiesPlugin +import org.springframework.gradle.maven.SpringMavenPlugin + +/** + * @author Rob Winch + * @author John Blum + */ +class MavenBomPlugin implements Plugin { + + void apply(Project project) { + project.plugins.apply(JavaPlatformPlugin) + project.plugins.apply(SpringMavenPlugin) + project.plugins.apply(CopyPropertiesPlugin) + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy new file mode 100644 index 00000000..f2ef3b0f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy @@ -0,0 +1,95 @@ +/* + * Copyright 2016-2018 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 io.spring.gradle.convention; + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * @author Rob Winch + * @author John Blum + */ +class RepositoryConventionPlugin implements Plugin { + + @Override + void apply(Project project) { + + String[] forceMavenRepositories = + ((String) project.findProperty("forceMavenRepositories"))?.split(',') + + boolean isImplicitSnapshotRepository = forceMavenRepositories == null && Utils.isSnapshot(project) + boolean isImplicitMilestoneRepository = forceMavenRepositories == null && Utils.isMilestone(project) + boolean isSnapshot = isImplicitSnapshotRepository || forceMavenRepositories?.contains('snapshot') + boolean isMilestone = isImplicitMilestoneRepository || forceMavenRepositories?.contains('milestone') + + project.repositories { + + if (forceMavenRepositories?.contains('local')) { + mavenLocal() + } + + mavenCentral() + + jcenter() { + content { + includeGroup "org.gretty" + } + } + + if (isSnapshot) { + maven { + name = 'artifactory-snapshot' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/snapshot/' + } + } + + if (isSnapshot || isMilestone) { + maven { + name = 'artifactory-milestone' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/milestone/' + } + } + + maven { + name = 'artifactory-release' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/release/' + } + + maven { + name = 'shibboleth' + url = 'https://build.shibboleth.net/nexus/content/repositories/releases/' + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy new file mode 100644 index 00000000..8c7399db --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy @@ -0,0 +1,76 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.spring.gradle.convention + +import io.spring.nohttp.gradle.NoHttpPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.plugins.PluginManager +import org.springframework.gradle.maven.SpringNexusPublishPlugin + +/** + * @author Rob Winch + * @author John Blum + */ +class RootProjectPlugin implements Plugin { + + @Override + void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager() + + pluginManager.apply(BasePlugin) + pluginManager.apply(NoHttpPlugin) + pluginManager.apply(SchemaPlugin) + pluginManager.apply(SpringNexusPublishPlugin) + pluginManager.apply("org.sonarqube") + + project.repositories.mavenCentral() + + project.allprojects { + configurations.all { + resolutionStrategy { + cacheChangingModulesFor 0, "seconds" + cacheDynamicVersionsFor 0, "seconds" + } + } + } + + String projectName = Utils.getProjectName(project) + + project.sonarqube { + properties { + property "sonar.java.coveragePlugin", "jacoco" + property "sonar.projectName", projectName + property "sonar.jacoco.reportPath", "${project.buildDir.name}/jacoco.exec" + property "sonar.links.homepage", "https://spring.io/${projectName}" + property "sonar.links.ci", "https://jenkins.spring.io/job/${projectName}/" + property "sonar.links.issue", "https://github.com/spring-projects/${projectName}/issues" + property "sonar.links.scm", "https://github.com/spring-projects/${projectName}" + property "sonar.links.scm_dev", "https://github.com/spring-projects/${projectName}.git" + } + } + + project.tasks.create("dependencyManagementExport", DependencyManagementExportTask) + + def finalizeDeployArtifacts = project.task("finalizeDeployArtifacts") + + if (Utils.isRelease(project) && project.hasProperty("ossrhUsername")) { + finalizeDeployArtifacts.dependsOn project.tasks.closeAndReleaseOssrhStagingRepository + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy new file mode 100644 index 00000000..07d01c0f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy @@ -0,0 +1,94 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * @author Rob Winch + * @author John Blum + */ +class SchemaDeployPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.getPluginManager().apply('org.hidetake.ssh') + + project.ssh.settings { + knownHosts = allowAnyHosts + } + + project.remotes { + docs { + + retryCount = 5 // Retry 5 times (default is 0) + retryWaitSec = 10 // Wait 10 seconds between retries (default is 0) + role 'docs' + + host = project.hasProperty('deployDocsHost') + ? project.findProperty('deployDocsHost') + : 'docs.af.pivotal.io' + + user = project.findProperty('deployDocsSshUsername') + + identity = project.hasProperty('deployDocsSshKeyPath') + ? project.file(project.findProperty('deployDocsSshKeyPath')) + : project.hasProperty('deployDocsSshKey') + ? project.findProperty('deployDocsSshKey') + : identity + + passphrase = project.hasProperty('deployDocsSshPassphrase') + ? project.findProperty('deployDocsSshPassphrase') + : passphrase + } + } + + project.task('deploySchema') { + dependsOn 'schemaZip' + doFirst { + project.ssh.run { + session(project.remotes.docs) { + + def now = System.currentTimeMillis() + def name = project.rootProject.name + def version = project.rootProject.version + def tempPath = "/tmp/${name}-${now}-schema/".replaceAll(' ', '_') + + execute "mkdir -p $tempPath" + + project.tasks.schemaZip.outputs.each { o -> + println "Putting $o.files" + put from: o.files, into: tempPath + } + + execute "unzip $tempPath*.zip -d $tempPath" + + def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/schema/${name}/${version}/" + + execute "rm -rf $extractPath" + execute "mkdir -p $extractPath" + execute "rm -f $tempPath*.zip" + execute "rm -rf $extractPath*" + execute "mv $tempPath/* $extractPath" + execute "chmod -R g+w $extractPath" + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy new file mode 100644 index 00000000..591b7df7 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy @@ -0,0 +1,32 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * @author Rob Winch + * @author John Blum + */ +class SchemaPlugin implements Plugin { + + @Override + void apply(Project project) { + project.getPluginManager().apply(SchemaDeployPlugin) + project.getPluginManager().apply(SchemaZipPlugin) + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy new file mode 100644 index 00000000..eecf2e03 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy @@ -0,0 +1,70 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Zip + +/** + * @author Rob Winch + * @author John Blum + */ +class SchemaZipPlugin implements Plugin { + + @Override + void apply(Project project) { + + Zip schemaZip = project.tasks.create('schemaZip', Zip) + + schemaZip.archiveBaseName = project.rootProject.name + schemaZip.archiveClassifier = 'schema' + schemaZip.description = "Builds -${schemaZip.archiveClassifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + schemaZip.group = 'Distribution' + + project.rootProject.subprojects.each { module -> + + module.getPlugins().withType(JavaPlugin.class).all { + + Properties schemas = new Properties(); + + module.sourceSets.main.resources + .find { it.path.endsWith('META-INF/spring.schemas') } + ?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + + def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') + + assert shortName != key + + File xsdFile = module.sourceSets.main.resources.find { + it.path.endsWith(schemas.get(key)) + } + + assert xsdFile != null + + schemaZip.into (shortName) { + duplicatesStrategy 'exclude' + from xsdFile.path + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy new file mode 100644 index 00000000..f6c7f989 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy @@ -0,0 +1,60 @@ +/* + * Copyright 2016-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 io.spring.gradle.convention + +import io.spring.gradle.dependencymanagement.DependencyManagementPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * Adds and configures {@link DependencyManagementPlugin}. + *

+ * Additionally, if 'gradle/dependency-management.gradle' file is present it will be + * automatically applied file for configuring the dependencies. + * + * @author Rob Winch + * @author John Blum + */ +class SpringDependencyManagementConventionPlugin implements Plugin { + + static final String DEPENDENCY_MANAGEMENT_RESOURCE = "gradle/dependency-management.gradle" + + @Override + void apply(Project project) { + + project.getPluginManager().apply(ManagementConfigurationPlugin) + project.getPluginManager().apply(DependencyManagementPlugin) + + project.dependencyManagement { + resolutionStrategy { + cacheChangingModulesFor 0, "seconds" + } + } + + File rootDir = project.rootDir + List dependencyManagementFiles = [project.rootProject.file(DEPENDENCY_MANAGEMENT_RESOURCE)] + + for (File dir = project.projectDir; dir != rootDir; dir = dir.parentFile) { + dependencyManagementFiles.add(new File(dir, DEPENDENCY_MANAGEMENT_RESOURCE)) + } + + dependencyManagementFiles.each { f -> + if (f.exists()) { + project.apply from: f.absolutePath + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy new file mode 100644 index 00000000..d6704bf7 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.spring.gradle.convention + +import org.gradle.api.Project +import org.gradle.api.plugins.JavaLibraryPlugin +import org.gradle.api.plugins.PluginManager +import org.springframework.gradle.maven.SpringMavenPlugin + +/** + * Defines the Gradle project as a proper Spring Module. + * + * @author Rob Winch + * @author John Blum + */ +class SpringModulePlugin extends AbstractSpringJavaPlugin { + + @Override + void additionalPlugins(Project project) { + + PluginManager pluginManager = project.getPluginManager(); + + pluginManager.apply(JavaLibraryPlugin.class) + pluginManager.apply(SpringMavenPlugin.class); + pluginManager.apply("io.spring.convention.jacoco"); + + def deployArtifacts = project.task("deployArtifacts") + + deployArtifacts.group = 'Deploy tasks' + deployArtifacts.description = "Deploys the artifacts to either Artifactory or Maven Central" + + if (!Utils.isRelease(project)) { + deployArtifacts.dependsOn project.tasks.artifactoryPublish + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy new file mode 100644 index 00000000..6b05495d --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy @@ -0,0 +1,41 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention + +import org.gradle.api.Project +import org.gradle.api.plugins.PluginManager + +/** + * @author Rob Winch + * @author John Blum + */ +class SpringSampleBootPlugin extends SpringSamplePlugin { + + @Override + void additionalPlugins(Project project) { + + super.additionalPlugins(project); + + PluginManager pluginManager = project.getPluginManager(); + + pluginManager.apply("org.springframework.boot"); + + project.repositories { + maven { url 'https://repo.spring.io/snapshot' } + maven { url 'https://repo.spring.io/milestone' } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy new file mode 100644 index 00000000..aaec8595 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy @@ -0,0 +1,33 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention; + +import org.gradle.api.Project +import org.sonarqube.gradle.SonarQubePlugin; + +/** + * @author Rob Winch + * @author John Blum + */ +class SpringSamplePlugin extends AbstractSpringJavaPlugin { + + @Override + void additionalPlugins(Project project) { + project.plugins.withType(SonarQubePlugin) { + project.sonarqube.skipProject = true + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy new file mode 100644 index 00000000..4ae3e90f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy @@ -0,0 +1,105 @@ +/* + * Copyright 2016-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 io.spring.gradle.convention + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.PluginManager +import org.gradle.api.tasks.testing.Test + +/** + * @author Rob Winch + * @author John Blum + */ +class SpringSampleWarPlugin extends SpringSamplePlugin { + + @Override + void additionalPlugins(Project project) { + + super.additionalPlugins(project); + + PluginManager pluginManager = project.getPluginManager(); + + pluginManager.apply("war"); + pluginManager.apply("org.gretty"); + + project.gretty { + servletContainer = 'tomcat10' + contextPath = '/' + fileLogEnabled = false + } + + Task prepareAppServerForIntegrationTests = project.tasks.create('prepareAppServerForIntegrationTests') { + group = 'Verification' + description = 'Prepares the app server for integration tests' + doFirst { + project.gretty { + httpPort = getRandomFreePort() + httpsPort = getRandomPort() + } + } + } + + project.tasks.matching { it.name == "appBeforeIntegrationTest" }.all { task -> + task.dependsOn prepareAppServerForIntegrationTests + } + + project.tasks.withType(Test).all { task -> + if("integrationTest".equals(task.name)) { + applyForIntegrationTest(project, task) + } + } + } + + def applyForIntegrationTest(Project project, Task integrationTest) { + + project.gretty.integrationTestTask = integrationTest.name + + integrationTest.doFirst { + + def gretty = project.gretty + + boolean isHttps = gretty.httpsEnabled + + Integer httpPort = integrationTest.systemProperties['gretty.httpPort'] + Integer httpsPort = integrationTest.systemProperties['gretty.httpsPort'] + + int port = isHttps ? httpsPort : httpPort + + String host = project.gretty.host ?: 'localhost' + String contextPath = project.gretty.contextPath + String httpBaseUrl = "http://${host}:${httpPort}${contextPath}" + String httpsBaseUrl = "https://${host}:${httpsPort}${contextPath}" + String baseUrl = isHttps ? httpsBaseUrl : httpBaseUrl + + integrationTest.systemProperty 'app.port', port + integrationTest.systemProperty 'app.httpPort', httpPort + integrationTest.systemProperty 'app.httpsPort', httpsPort + integrationTest.systemProperty 'app.baseURI', baseUrl + integrationTest.systemProperty 'app.httpBaseURI', httpBaseUrl + integrationTest.systemProperty 'app.httpsBaseURI', httpsBaseUrl + integrationTest.systemProperty 'geb.build.baseUrl', baseUrl + integrationTest.systemProperty 'geb.build.reportsDir', 'build/geb-reports' + } + } + + def getRandomPort() { + ServerSocket serverSocket = new ServerSocket(0) + int port = serverSocket.localPort + serverSocket.close() + return port + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy new file mode 100644 index 00000000..9d9c842b --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy @@ -0,0 +1,30 @@ +/* + * Copyright 2002-2016 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 io.spring.gradle.convention; + +import org.gradle.api.Project; + +/** + * @author Rob Winch + * @author John Blum + */ +class SpringTestPlugin extends AbstractSpringJavaPlugin { + + @Override + void additionalPlugins(Project project) { + project.sonarqube.skipProject = true + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy new file mode 100644 index 00000000..22151cb4 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy @@ -0,0 +1,57 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention; + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.jvm.tasks.Jar + +/** + * Adds the ability to depends on the test jar within other projects using: + * + * + * testImplementation project(path: ':foo', configuration: 'tests') + * + * + * @author Rob Winch + * @author John Blum + */ +class TestsConfigurationPlugin implements Plugin { + + @Override + void apply(Project project) { + project.plugins.withType(JavaPlugin) { + applyJavaProject(project) + } + } + + private void applyJavaProject(Project project) { + + project.configurations { + tests.extendsFrom testRuntime, testRuntimeClasspath + } + + project.tasks.create('testJar', Jar) { + classifier = 'test' + from project.sourceSets.test.output + } + + project.artifacts { + tests project.testJar + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy new file mode 100644 index 00000000..18bb8143 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy @@ -0,0 +1,55 @@ +/* + * Copyright 2002-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 io.spring.gradle.convention; + +import org.gradle.api.Project; + +/** + * @author Rob Winch + * @author John Blum + */ +class Utils { + + private Utils() {} + + static String getProjectName(Project project) { + + String projectName = project.getRootProject().getName(); + + if(projectName.endsWith("-build")) { + projectName = projectName.substring(0, projectName.length() - "-build".length()); + } + + return projectName; + } + + static boolean isMilestone(Project project) { + return projectVersion(project).matches('^.*[.-]M\\d+$') + || projectVersion(project).matches('^.*[.-]RC\\d+$') + } + + static boolean isRelease(Project project) { + return !(isSnapshot(project) || isMilestone(project)) + } + + static boolean isSnapshot(Project project) { + return projectVersion(project).matches('^.*([.-]BUILD)?-SNAPSHOT$') + } + + private static String projectVersion(Project project) { + return String.valueOf(project.getVersion()); + } +} diff --git a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java new file mode 100644 index 00000000..754d476a --- /dev/null +++ b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java @@ -0,0 +1,204 @@ +/* + * Copyright 2019-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 io.spring.gradle.convention; + +import java.io.File; +import java.net.URI; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +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.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.Sync; + +/** + * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. + * + * When the plugin is applied: + * + *

    + *
  • All warnings are made fatal. + *
  • A task is created to resolve and unzip our documentation resources (CSS and Javascript). + *
  • For each {@link AsciidoctorTask} (HTML only): + *
      + *
    • A configuration named asciidoctorExtensions is ued to add the + * block switch extension + *
    • {@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. + *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax highlighting, CSS styling, + * docinfo, etc. + *
    + *
  • For each {@link AbstractAsciidoctorTask} (HTML and PDF): + *
      + *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable warnings for references to + * missing attributes, the year is added as @{code today-year}, etc + *
    • {@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} is enabled. + *
    + *
+ * + * @author Andy Wilkinson + * @author Rob Winch + */ +public class AsciidoctorConventionPlugin implements Plugin { + + @Override + public void apply(Project project) { + + project.getPlugins().withType(AsciidoctorJPlugin.class, asciidoctorPlugin -> { + + createDefaultAsciidoctorRepository(project); + makeAllWarningsFatal(project); + + Sync unzipResources = createUnzipDocumentationResourcesTask(project); + + project.getTasks().withType(AbstractAsciidoctorTask.class, asciidoctorTask -> { + + asciidoctorTask.dependsOn(unzipResources); + configureExtensions(project, asciidoctorTask); + configureCommonAttributes(project, asciidoctorTask); + configureOptions(asciidoctorTask); + asciidoctorTask.baseDirFollowsSourceDir(); + asciidoctorTask.useIntermediateWorkDir(); + + asciidoctorTask.resources(resourcesSpec -> { + resourcesSpec.from(unzipResources); + resourcesSpec.from(asciidoctorTask.getSourceDir(), resourcesSrcDirSpec -> { + // https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523 + // For now copy the entire sourceDir over so that include files are + // available in the intermediateWorkDir + // resourcesSrcDirSpec.include("images/**"); + }); + }); + + if (asciidoctorTask instanceof AsciidoctorTask) { + configureHtmlOnlyAttributes(project, asciidoctorTask); + } + }); + }); + } + + private void createDefaultAsciidoctorRepository(Project project) { + + project.getGradle().afterProject(it -> { + + RepositoryHandler repositories = it.getRepositories(); + + if (repositories.isEmpty()) { + repositories.mavenCentral(); + repositories.maven(repo -> repo.setUrl(URI.create("https://repo.spring.io/release"))); + } + }); + } + + @SuppressWarnings("all") + private Sync createUnzipDocumentationResourcesTask(Project project) { + + Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources"); + + documentationResources.getDependencies() + .add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5")); + + Sync unzipResources = project.getTasks().create("unzipDocumentationResources", Sync.class, sync -> { + + sync.dependsOn(documentationResources); + + sync.from((Callable>) () -> { + List result = new ArrayList<>(); + documentationResources.getAsFileTree().forEach(file -> result.add(project.zipTree(file))); + return result; + }); + + File destination = new File(project.getBuildDir(), "docs/resources"); + + sync.into(project.relativePath(destination)); + + }); + + return unzipResources; + } + + private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { + + Map attributes = new HashMap<>(); + + attributes.put("attribute-missing", "warn"); + attributes.put("icons", "font"); + attributes.put("idprefix", ""); + attributes.put("idseparator", "-"); + attributes.put("docinfo", "shared"); + attributes.put("sectanchors", ""); + attributes.put("sectnums", ""); + attributes.put("today-year", LocalDate.now().getYear()); + + asciidoctorTask.attributes(attributes); + } + + private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { + + Map attributes = new HashMap<>(); + + attributes.put("source-highlighter", "highlight.js"); + attributes.put("highlightjsdir", "js/highlight"); + attributes.put("highlightjs-theme", "github"); + attributes.put("linkcss", true); + attributes.put("icons", "font"); + attributes.put("stylesheet", "css/spring.css"); + + asciidoctorTask.getAttributeProviders().add(() -> { + + Object version = project.getVersion(); + + Map localAttributes = new HashMap<>(); + + if (version != null && !Project.DEFAULT_VERSION.equals(version)) { + localAttributes.put("revnumber", version); + } + + return localAttributes; + }); + + asciidoctorTask.attributes(attributes); + } + + private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) { + + Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions"); + + extensionsConfiguration.defaultDependencies(dependencies -> dependencies.add(project.getDependencies() + .create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE"))); + + asciidoctorTask.configurations(extensionsConfiguration); + } + + private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { + asciidoctorTask.options(Collections.singletonMap("doctype", "book")); + } + + private void makeAllWarningsFatal(Project project) { + project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/CopyPropertiesPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/CopyPropertiesPlugin.java new file mode 100644 index 00000000..78819fe5 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/CopyPropertiesPlugin.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-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.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class CopyPropertiesPlugin implements Plugin { + @Override + public void apply(Project project) { + copyPropertyFromRootProjectTo("group", project); + copyPropertyFromRootProjectTo("version", project); + copyPropertyFromRootProjectTo("description", project); + } + + + private void copyPropertyFromRootProjectTo(String propertyName, Project project) { + Project rootProject = project.getRootProject(); + Object property = rootProject.findProperty(propertyName); + if(property != null) { + project.setProperty(propertyName, property); + } + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneApi.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneApi.java new file mode 100644 index 00000000..31f1274a --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneApi.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019-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.gradle.github.milestones; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; +import java.util.List; + +public class GitHubMilestoneApi { + private String baseUrl = "https://api.github.com"; + + private OkHttpClient client; + + private Gson gson = new Gson(); + + public GitHubMilestoneApi() { + this.client = new OkHttpClient.Builder().build(); + } + + public GitHubMilestoneApi(String gitHubToken) { + this.client = new OkHttpClient.Builder() + .addInterceptor(new AuthorizationInterceptor(gitHubToken)) + .build(); + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public long findMilestoneNumberByTitle(RepositoryRef repositoryRef, String milestoneTitle) { + String url = this.baseUrl + "/repos/" + repositoryRef.getOwner() + "/" + repositoryRef.getName() + "/milestones?per_page=100"; + Request request = new Request.Builder().get().url(url) + .build(); + try { + Response response = this.client.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new RuntimeException("Could not find milestone with title " + milestoneTitle + " for repository " + repositoryRef + ". Response " + response); + } + List milestones = this.gson.fromJson(response.body().charStream(), new TypeToken>(){}.getType()); + for (Milestone milestone : milestones) { + if (milestoneTitle.equals(milestone.getTitle())) { + return milestone.getNumber(); + } + } + if (milestones.size() <= 100) { + throw new RuntimeException("Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones); + } + throw new RuntimeException("It is possible there are too many open milestones open (only 100 are supported). Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones); + } catch (IOException e) { + throw new RuntimeException("Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef, e); + } + } + + public boolean isOpenIssuesForMilestoneNumber(RepositoryRef repositoryRef, long milestoneNumber) { + String url = this.baseUrl + "/repos/" + repositoryRef.getOwner() + "/" + repositoryRef.getName() + "/issues?per_page=1&milestone=" + milestoneNumber; + Request request = new Request.Builder().get().url(url) + .build(); + try { + Response response = this.client.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new RuntimeException("Could not find issues for milestone number " + milestoneNumber + " for repository " + repositoryRef + ". Response " + response); + } + List issues = this.gson.fromJson(response.body().charStream(), new TypeToken>(){}.getType()); + return !issues.isEmpty(); + } catch (IOException e) { + throw new RuntimeException("Could not find issues for milestone number " + milestoneNumber + " for repository " + repositoryRef, e); + } + } + +// public boolean isOpenIssuesForMilestoneName(String owner, String repository, String milestoneName) { +// +// } + + + private static class AuthorizationInterceptor implements Interceptor { + + private final String token; + + public AuthorizationInterceptor(String token) { + this.token = token; + } + + @Override + public okhttp3.Response intercept(Chain chain) throws IOException { + Request request = chain.request().newBuilder() + .addHeader("Authorization", "Bearer " + this.token).build(); + return chain.proceed(request); + } + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneHasNoOpenIssuesTask.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneHasNoOpenIssuesTask.java new file mode 100644 index 00000000..65f90616 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestoneHasNoOpenIssuesTask.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019-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.gradle.github.milestones; + +import org.gradle.api.Action; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskAction; + +public class GitHubMilestoneHasNoOpenIssuesTask extends DefaultTask { + @Input + private RepositoryRef repository = new RepositoryRef(); + + @Input + private String milestoneTitle; + + @Input @Optional + private String gitHubAccessToken; + + private GitHubMilestoneApi milestones = new GitHubMilestoneApi(); + + @TaskAction + public void checkHasNoOpenIssues() { + long milestoneNumber = this.milestones.findMilestoneNumberByTitle(this.repository, this.milestoneTitle); + boolean isOpenIssues = this.milestones.isOpenIssuesForMilestoneNumber(this.repository, milestoneNumber); + if (isOpenIssues) { + throw new IllegalStateException("The repository " + this.repository + " has open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber); + } + System.out.println("The repository " + this.repository + " has no open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber); + } + + public RepositoryRef getRepository() { + return repository; + } + + public void repository(Action repository) { + repository.execute(this.repository); + } + + public void setRepository(RepositoryRef repository) { + this.repository = repository; + } + + public String getMilestoneTitle() { + return milestoneTitle; + } + + public void setMilestoneTitle(String milestoneTitle) { + this.milestoneTitle = milestoneTitle; + } + + public String getGitHubAccessToken() { + return gitHubAccessToken; + } + + public void setGitHubAccessToken(String gitHubAccessToken) { + this.gitHubAccessToken = gitHubAccessToken; + this.milestones = new GitHubMilestoneApi(gitHubAccessToken); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestonePlugin.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestonePlugin.java new file mode 100644 index 00000000..527b7676 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/GitHubMilestonePlugin.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-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.gradle.github.milestones; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class GitHubMilestonePlugin implements Plugin { + @Override + public void apply(Project project) { + project.getTasks().register("gitHubCheckMilestoneHasNoOpenIssues", GitHubMilestoneHasNoOpenIssuesTask.class, new Action() { + @Override + public void execute(GitHubMilestoneHasNoOpenIssuesTask githubCheckMilestoneHasNoOpenIssues) { + githubCheckMilestoneHasNoOpenIssues.setGroup("Release"); + githubCheckMilestoneHasNoOpenIssues.setDescription("Checks if there are any open issues for the specified repository and milestone"); + githubCheckMilestoneHasNoOpenIssues.setMilestoneTitle((String) project.findProperty("nextVersion")); + if (project.hasProperty("githubAccessToken")) { + githubCheckMilestoneHasNoOpenIssues.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken")); + } + } + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/Milestone.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/Milestone.java new file mode 100644 index 00000000..5d0ff234 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/Milestone.java @@ -0,0 +1,31 @@ +package org.springframework.gradle.github.milestones; + +public class Milestone { + private String title; + + private long number; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public long getNumber() { + return number; + } + + public void setNumber(long number) { + this.number = number; + } + + @Override + public String toString() { + return "Milestone{" + + "title='" + title + '\'' + + ", number=" + number + + '}'; + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/RepositoryRef.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/RepositoryRef.java new file mode 100644 index 00000000..70eec3b1 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/RepositoryRef.java @@ -0,0 +1,65 @@ +package org.springframework.gradle.github.milestones; +public class RepositoryRef { + private String owner; + + private String name; + + RepositoryRef() { + } + + public RepositoryRef(String owner, String name) { + this.owner = owner; + this.name = name; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "RepositoryRef{" + + "owner='" + owner + '\'' + + ", name='" + name + '\'' + + '}'; + } + + public static RepositoryRefBuilder owner(String owner) { + return new RepositoryRefBuilder().owner(owner); + } + + public static final class RepositoryRefBuilder { + private String owner; + private String repository; + + private RepositoryRefBuilder() { + } + + private RepositoryRefBuilder owner(String owner) { + this.owner = owner; + return this; + } + + public RepositoryRefBuilder repository(String repository) { + this.repository = repository; + return this; + } + + public RepositoryRef build() { + return new RepositoryRef(owner, repository); + } + } +} + diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/MavenPublishingConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/MavenPublishingConventionsPlugin.java new file mode 100644 index 00000000..8ed94c4c --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/MavenPublishingConventionsPlugin.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016-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.gradle.maven; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPom; +import org.gradle.api.publish.maven.MavenPomDeveloperSpec; +import org.gradle.api.publish.maven.MavenPomIssueManagement; +import org.gradle.api.publish.maven.MavenPomLicenseSpec; +import org.gradle.api.publish.maven.MavenPomOrganization; +import org.gradle.api.publish.maven.MavenPomScm; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +public class MavenPublishingConventionsPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().withType(MavenPublishPlugin.class).all(new Action() { + @Override + public void execute(MavenPublishPlugin mavenPublish) { + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + publishing.getPublications().withType(MavenPublication.class) + .all((mavenPublication) -> MavenPublishingConventionsPlugin.this.customizePom(mavenPublication.getPom(), project)); + MavenPublishingConventionsPlugin.this.customizeJavaPlugin(project); + } + }); + } + + private void customizePom(MavenPom pom, Project project) { + pom.getUrl().set("https://spring.io/projects/spring-session"); + pom.getName().set(project.provider(project::getName)); + pom.getDescription().set(project.provider(project::getDescription)); + pom.organization(this::customizeOrganization); + pom.licenses(this::customizeLicences); + pom.developers(this::customizeDevelopers); + pom.scm(this::customizeScm); + pom.issueManagement(this::customizeIssueManagement); + } + + private void customizeOrganization(MavenPomOrganization organization) { + organization.getName().set("Pivotal Software, Inc."); + organization.getUrl().set("https://spring.io"); + } + + private void customizeLicences(MavenPomLicenseSpec licences) { + licences.license((licence) -> { + licence.getName().set("Apache License, Version 2.0"); + licence.getUrl().set("https://www.apache.org/licenses/LICENSE-2.0"); + }); + } + + private void customizeDevelopers(MavenPomDeveloperSpec developers) { + developers.developer((developer) -> { + developer.getName().set("Pivotal"); + developer.getEmail().set("info@pivotal.io"); + developer.getOrganization().set("Pivotal Software, Inc."); + developer.getOrganizationUrl().set("https://www.spring.io"); + }); + } + + private void customizeScm(MavenPomScm scm) { + scm.getConnection().set("scm:git:git://github.com/spring-projects/spring-session.git"); + scm.getDeveloperConnection().set("scm:git:ssh://git@github.com/spring-projects/spring-session.git"); + scm.getUrl().set("https://github.com/spring-projects/spring-session"); + } + + private void customizeIssueManagement(MavenPomIssueManagement issueManagement) { + issueManagement.getSystem().set("GitHub"); + issueManagement.getUrl().set("https://github.com/spring-projects/spring-session/issues"); + } + + private void customizeJavaPlugin(Project project) { + project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> { + JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class); + extension.withJavadocJar(); + extension.withSourcesJar(); + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/PublishAllJavaComponentsPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishAllJavaComponentsPlugin.java new file mode 100644 index 00000000..408d83e7 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishAllJavaComponentsPlugin.java @@ -0,0 +1,33 @@ +package org.springframework.gradle.maven; + + +import org.gradle.api.Action; +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.VariantVersionMappingStrategy; +import org.gradle.api.publish.VersionMappingStrategy; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +public class PublishAllJavaComponentsPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> { + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + publishing.getPublications().create("mavenJava", MavenPublication.class, new Action() { + @Override + public void execute(MavenPublication maven) { + project.getPlugins().withType(JavaPlugin.class, (plugin) -> { + maven.from(project.getComponents().getByName("java")); + }); + project.getPlugins().withType(JavaPlatformPlugin.class, (plugin) -> { + maven.from(project.getComponents().getByName("javaPlatform")); + }); + } + }); + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/PublishArtifactsPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishArtifactsPlugin.java new file mode 100644 index 00000000..ed3985e2 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishArtifactsPlugin.java @@ -0,0 +1,26 @@ +package org.springframework.gradle.maven; + +import io.spring.gradle.convention.Utils; +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; + +public class PublishArtifactsPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getTasks().register("publishArtifacts", new Action() { + @Override + public void execute(Task publishArtifacts) { + publishArtifacts.setGroup("Publishing"); + publishArtifacts.setDescription("Publish the artifacts to either Artifactory or Maven Central based on the version"); + if (Utils.isRelease(project)) { + publishArtifacts.dependsOn("publishToOssrh"); + } + else { + publishArtifacts.dependsOn("artifactoryPublish"); + } + } + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java new file mode 100644 index 00000000..54f9e497 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java @@ -0,0 +1,29 @@ +package org.springframework.gradle.maven; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +import java.io.File; + +public class PublishLocalPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().withType(MavenPublishPlugin.class).all(new Action() { + @Override + public void execute(MavenPublishPlugin mavenPublish) { + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + publishing.getRepositories().maven(new Action() { + @Override + public void execute(MavenArtifactRepository maven) { + maven.setName("local"); + maven.setUrl(new File(project.getRootProject().getBuildDir(), "publications/repos")); + } + }); + } + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/SpringMavenPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringMavenPlugin.java new file mode 100644 index 00000000..7a042ce1 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringMavenPlugin.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2017 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.gradle.maven; + +import io.spring.gradle.convention.ArtifactoryPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.PluginManager; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +/** + * @author Rob Winch + * @author John Blum + */ +public class SpringMavenPlugin implements Plugin { + + @Override + public void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager(); + + pluginManager.apply(MavenPublishPlugin.class); + pluginManager.apply(SpringSigningPlugin.class); + pluginManager.apply(MavenPublishingConventionsPlugin.class); + pluginManager.apply(PublishAllJavaComponentsPlugin.class); + pluginManager.apply(PublishLocalPlugin.class); + pluginManager.apply(PublishArtifactsPlugin.class); + pluginManager.apply(ArtifactoryPlugin.class); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/SpringNexusPublishPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringNexusPublishPlugin.java new file mode 100644 index 00000000..4376e5fb --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringNexusPublishPlugin.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2016 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.gradle.maven; + +import java.net.URI; +import java.time.Duration; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +import io.github.gradlenexus.publishplugin.NexusPublishExtension; +import io.github.gradlenexus.publishplugin.NexusPublishPlugin; + +public class SpringNexusPublishPlugin implements Plugin { + + @Override + public void apply(Project project) { + + project.getPlugins().apply(NexusPublishPlugin.class); + + NexusPublishExtension nexusPublishing = project.getExtensions().findByType(NexusPublishExtension.class); + + nexusPublishing.getRepositories().create("ossrh", nexusRepository -> { + nexusRepository.getNexusUrl().set(URI.create("https://s01.oss.sonatype.org/service/local/")); + nexusRepository.getSnapshotRepositoryUrl() + .set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/")); + }); + + nexusPublishing.getConnectTimeout().set(Duration.ofMinutes(3)); + nexusPublishing.getClientTimeout().set(Duration.ofMinutes(3)); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java new file mode 100644 index 00000000..53acf0a5 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.gradle.maven; + +import java.util.concurrent.Callable; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.publish.Publication; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.plugins.signing.SigningExtension; +import org.gradle.plugins.signing.SigningPlugin; + +public class SpringSigningPlugin implements Plugin { + + @Override + public void apply(Project project) { + + project.getPluginManager().apply(SigningPlugin.class); + + project.getPlugins().withType(SigningPlugin.class).all(signingPlugin -> { + + boolean hasSigningKey = project.hasProperty("signing.keyId") + || project.hasProperty("signingKey"); + + if (hasSigningKey) { + sign(project); + } + }); + } + + private void sign(Project project) { + + SigningExtension signing = project.getExtensions().findByType(SigningExtension.class); + + signing.setRequired((Callable) () -> + project.getGradle().getTaskGraph().hasTask("publishArtifacts")); + + String signingKey = (String) project.findProperty("signingKey"); + String signingKeyId = (String) project.findProperty("signingKeyId"); + String signingPassword = (String) project.findProperty("signingPassword"); + + if (signingKeyId != null) { + signing.useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword); + } + else { + signing.useInMemoryPgpKeys(signingKey, signingPassword); + } + + project.getPlugins().withType(PublishAllJavaComponentsPlugin.class).all(publishingPlugin -> { + PublishingExtension publishing = project.getExtensions().findByType(PublishingExtension.class); + Publication maven = publishing.getPublications().getByName("mavenJava"); + signing.sign(maven); + }); + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsEclipsePlugin.groovy b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsEclipsePlugin.groovy new file mode 100644 index 00000000..bf88ca6c --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsEclipsePlugin.groovy @@ -0,0 +1,43 @@ +/* + * Copyright 2002-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.gradle.propdeps + + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.plugins.ide.eclipse.EclipsePlugin + +/** + * Plugin to allow optional and provided dependency configurations to work with the + * standard gradle 'eclipse' plugin + * + * @author Phillip Webb + */ +class PropDepsEclipsePlugin implements Plugin { + + public void apply(Project project) { + project.plugins.apply(PropDepsPlugin) + project.plugins.apply(EclipsePlugin) + + project.eclipse { + classpath { + plusConfigurations += [project.configurations.provided, project.configurations.optional] + } + } + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsIdeaPlugin.groovy b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsIdeaPlugin.groovy new file mode 100644 index 00000000..4035971a --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsIdeaPlugin.groovy @@ -0,0 +1,46 @@ +/* + * Copyright 2002-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.gradle.propdeps + + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.plugins.ide.idea.IdeaPlugin + +/** + * Plugin to allow optional and provided dependency configurations to work with the + * standard gradle 'idea' plugin + * + * @author Phillip Webb + * @author Brian Clozel + * @link https://youtrack.jetbrains.com/issue/IDEA-107046 + * @link https://youtrack.jetbrains.com/issue/IDEA-117668 + */ +class PropDepsIdeaPlugin implements Plugin { + + public void apply(Project project) { + project.plugins.apply(PropDepsPlugin) + project.plugins.apply(IdeaPlugin) + project.idea.module { + // IDEA internally deals with 4 scopes : COMPILE, TEST, PROVIDED, RUNTIME + // but only PROVIDED seems to be picked up + scopes.PROVIDED.plus += [project.configurations.provided] + scopes.PROVIDED.plus += [project.configurations.optional] + } + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsPlugin.groovy b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsPlugin.groovy new file mode 100644 index 00000000..e0893e6a --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsPlugin.groovy @@ -0,0 +1,76 @@ +/* + * Copyright 2002-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.gradle.propdeps + + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.plugins.JavaLibraryPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.javadoc.Javadoc + +/** + * Plugin to allow 'optional' and 'provided' dependency configurations + * + * As stated in the maven documentation, provided scope "is only available on the compilation and test classpath, + * and is not transitive". + * + * This plugin creates two new configurations, and each one: + *
    + *
  • is a parent of the compile configuration
  • + *
  • is not visible, not transitive
  • + *
  • all dependencies are excluded from the default configuration
  • + *
+ * + * @author Phillip Webb + * @author Brian Clozel + * @author Rob Winch + * + * @see Maven documentation + * @see Gradle configurations + * @see PropDepsEclipsePlugin + * @see PropDepsIdeaPlugin + */ +class PropDepsPlugin implements Plugin { + + public void apply(Project project) { + project.plugins.apply(JavaPlugin) + + Configuration provided = addConfiguration(project, "provided") + Configuration optional = addConfiguration(project, "optional") + + Javadoc javadoc = project.tasks.getByName(JavaPlugin.JAVADOC_TASK_NAME) + javadoc.classpath = javadoc.classpath.plus(provided).plus(optional) + } + + private Configuration addConfiguration(Project project, String name) { + Configuration configuration = project.configurations.create(name) + configuration.extendsFrom(project.configurations.implementation) + project.plugins.withType(JavaLibraryPlugin, { + configuration.extendsFrom(project.configurations.api) + }) + + project.sourceSets.all { + compileClasspath += configuration + runtimeClasspath += configuration + } + + return configuration + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/Release.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/Release.java new file mode 100644 index 00000000..5e62c658 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/Release.java @@ -0,0 +1,123 @@ +/* + * Copyright 2019-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.gradle.sagan; + +import java.util.regex.Pattern; + +/** + * Domain object for creating a new release version. + */ +public class Release { + private String version; + + private ReleaseStatus status; + + private boolean current; + + private String referenceDocUrl; + + private String apiDocUrl; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public ReleaseStatus getStatus() { + return status; + } + + public void setStatus(ReleaseStatus status) { + this.status = status; + } + + public boolean isCurrent() { + return current; + } + + public void setCurrent(boolean current) { + this.current = current; + } + + public String getReferenceDocUrl() { + return referenceDocUrl; + } + + public void setReferenceDocUrl(String referenceDocUrl) { + this.referenceDocUrl = referenceDocUrl; + } + + public String getApiDocUrl() { + return apiDocUrl; + } + + public void setApiDocUrl(String apiDocUrl) { + this.apiDocUrl = apiDocUrl; + } + + @Override + public String toString() { + return "Release{" + + "version='" + version + '\'' + + ", status=" + status + + ", current=" + current + + ", referenceDocUrl='" + referenceDocUrl + '\'' + + ", apiDocUrl='" + apiDocUrl + '\'' + + '}'; + } + + public enum ReleaseStatus { + /** + * Unstable version with limited support + */ + SNAPSHOT, + /** + * Pre-Release version meant to be tested by the community + */ + PRERELEASE, + /** + * Release Generally Available on public artifact repositories and enjoying full support from maintainers + */ + GENERAL_AVAILABILITY; + + private static final Pattern PRERELEASE_PATTERN = Pattern.compile("[A-Za-z0-9\\.\\-]+?(M|RC)\\d+"); + + private static final String SNAPSHOT_SUFFIX = "SNAPSHOT"; + + /** + * Parse the ReleaseStatus from a String + * @param version a project version + * @return the release status for this version + */ + public static ReleaseStatus parse(String version) { + if (version == null) { + throw new IllegalArgumentException("version cannot be null"); + } + if (version.endsWith(SNAPSHOT_SUFFIX)) { + return SNAPSHOT; + } + if (PRERELEASE_PATTERN.matcher(version).matches()) { + return PRERELEASE; + } + return GENERAL_AVAILABILITY; + } + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganApi.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganApi.java new file mode 100644 index 00000000..24eeb311 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganApi.java @@ -0,0 +1,93 @@ +/* + * Copyright 2019-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.gradle.sagan; + +import com.google.gson.Gson; +import okhttp3.*; + +import java.io.IOException; +import java.util.Base64; + +/** + * Implements necessary calls to the Sagan API See https://spring.io/restdocs/index.html + */ +public class SaganApi { + private String baseUrl = "https://spring.io/api"; + + private OkHttpClient client; + private Gson gson = new Gson(); + + public SaganApi(String gitHubToken) { + this.client = new OkHttpClient.Builder() + .addInterceptor(new BasicInterceptor("not-used", gitHubToken)) + .build(); + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public void createReleaseForProject(Release release, String projectName) { + String url = this.baseUrl + "/projects/" + projectName + "/releases"; + String releaseJsonString = gson.toJson(release); + RequestBody body = RequestBody.create(MediaType.parse("application/json"), releaseJsonString); + Request request = new Request.Builder() + .url(url) + .post(body) + .build(); + try { + Response response = this.client.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new RuntimeException("Could not create release " + release + ". Got response " + response); + } + } catch (IOException fail) { + throw new RuntimeException("Could not create release " + release, fail); + } + } + + public void deleteReleaseForProject(String release, String projectName) { + String url = this.baseUrl + "/projects/" + projectName + "/releases/" + release; + Request request = new Request.Builder() + .url(url) + .delete() + .build(); + try { + Response response = this.client.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new RuntimeException("Could not delete release " + release + ". Got response " + response); + } + } catch (IOException fail) { + throw new RuntimeException("Could not delete release " + release, fail); + } + } + + private static class BasicInterceptor implements Interceptor { + + private final String token; + + public BasicInterceptor(String username, String token) { + this.token = Base64.getEncoder().encodeToString((username + ":" + token).getBytes()); + } + + @Override + public okhttp3.Response intercept(Chain chain) throws IOException { + Request request = chain.request().newBuilder() + .addHeader("Authorization", "Basic " + this.token).build(); + return chain.proceed(request); + } + } +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java new file mode 100644 index 00000000..6592544b --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java @@ -0,0 +1,86 @@ +/* + * Copyright 2019-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.gradle.sagan; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; + +public class SaganCreateReleaseTask extends DefaultTask { + + @Input + private String gitHubAccessToken; + @Input + private String version; + @Input + private String apiDocUrl; + @Input + private String referenceDocUrl; + @Input + private String projectName; + + @TaskAction + public void saganCreateRelease() { + SaganApi sagan = new SaganApi(this.gitHubAccessToken); + Release release = new Release(); + release.setVersion(this.version); + release.setApiDocUrl(this.apiDocUrl); + release.setReferenceDocUrl(this.referenceDocUrl); + sagan.createReleaseForProject(release, this.projectName); + } + + public String getGitHubAccessToken() { + return gitHubAccessToken; + } + + public void setGitHubAccessToken(String gitHubAccessToken) { + this.gitHubAccessToken = gitHubAccessToken; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getApiDocUrl() { + return apiDocUrl; + } + + public void setApiDocUrl(String apiDocUrl) { + this.apiDocUrl = apiDocUrl; + } + + public String getReferenceDocUrl() { + return referenceDocUrl; + } + + public void setReferenceDocUrl(String referenceDocUrl) { + this.referenceDocUrl = referenceDocUrl; + } + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganDeleteReleaseTask.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganDeleteReleaseTask.java new file mode 100644 index 00000000..49a38852 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganDeleteReleaseTask.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019-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.gradle.sagan; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; + +public class SaganDeleteReleaseTask extends DefaultTask { + + @Input + private String gitHubAccessToken; + @Input + private String version; + @Input + private String projectName; + + @TaskAction + public void saganCreateRelease() { + SaganApi sagan = new SaganApi(this.gitHubAccessToken); + sagan.deleteReleaseForProject(this.version, this.projectName); + } + + public String getGitHubAccessToken() { + return gitHubAccessToken; + } + + public void setGitHubAccessToken(String gitHubAccessToken) { + this.gitHubAccessToken = gitHubAccessToken; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + +} diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganPlugin.java new file mode 100644 index 00000000..388a2d15 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganPlugin.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-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.gradle.sagan; + +import io.spring.gradle.convention.Utils; +import org.gradle.api.*; + +public class SaganPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getTasks().register("saganCreateRelease", SaganCreateReleaseTask.class, new Action() { + @Override + public void execute(SaganCreateReleaseTask saganCreateVersion) { + saganCreateVersion.setGroup("Release"); + saganCreateVersion.setDescription("Creates a new version for the specified project on spring.io"); + saganCreateVersion.setVersion((String) project.findProperty("nextVersion")); + saganCreateVersion.setProjectName(Utils.getProjectName(project)); + saganCreateVersion.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken")); + } + }); + project.getTasks().register("saganDeleteRelease", SaganDeleteReleaseTask.class, new Action() { + @Override + public void execute(SaganDeleteReleaseTask saganDeleteVersion) { + saganDeleteVersion.setGroup("Release"); + saganDeleteVersion.setDescription("Delete a version for the specified project on spring.io"); + saganDeleteVersion.setVersion((String) project.findProperty("previousVersion")); + saganDeleteVersion.setProjectName(Utils.getProjectName(project)); + saganDeleteVersion.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken")); + } + }); + } + +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties new file mode 100644 index 00000000..bb6a3057 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/gemfire-server.properties @@ -0,0 +1 @@ +implementation-class=build.GemFireServerPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties new file mode 100644 index 00000000..573d1281 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.ArtifactoryPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties new file mode 100644 index 00000000..dec2e7a8 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.MavenBomPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties new file mode 100644 index 00000000..9259a914 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.CheckstylePlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties new file mode 100644 index 00000000..12edaa46 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.DependencySetPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties new file mode 100644 index 00000000..fcad41ab --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.DocsPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties new file mode 100644 index 00000000..0da39b33 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.IntegrationTestPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties new file mode 100644 index 00000000..70cfb7d0 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JacocoPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties new file mode 100644 index 00000000..bfb92306 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JavadocApiPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties new file mode 100644 index 00000000..77e7832e --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JavadocOptionsPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties new file mode 100644 index 00000000..b427d552 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.RepositoryConventionPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties new file mode 100644 index 00000000..9844db62 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.RootProjectPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties new file mode 100644 index 00000000..124dd85f --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringModulePlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-boot.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-boot.properties new file mode 100644 index 00000000..4ecd703e --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-boot.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringSampleBootPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-war.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-war.properties new file mode 100644 index 00000000..367e0189 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample-war.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringSampleWarPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample.properties new file mode 100644 index 00000000..77cb21d5 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-sample.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringSamplePlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties new file mode 100644 index 00000000..4881e2d0 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringTestPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.springdependencymangement.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.springdependencymangement.properties new file mode 100644 index 00000000..10b8580c --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.springdependencymangement.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringDependencyManagementConventionPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties new file mode 100644 index 00000000..afd0a712 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.TestsConfigurationPlugin \ No newline at end of file