diff --git a/README.md b/README.md index bd5ae81..225293c 100644 --- a/README.md +++ b/README.md @@ -3,24 +3,32 @@ [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fgradlex-org%2Fplugin-publish-conventions%2Fbadge%3Fref%3Dmain&style=flat)](https://actions-badge.atrox.dev/gradlex-org/plugin-publish-conventions/goto?ref=main) [![Gradle Plugin Portal](https://img.shields.io/maven-metadata/v?label=Plugin%20Portal&metadataUrl=https%3A%2F%2Fplugins.gradle.org%2Fm2%2Forg%2Fgradlex%2Finternal%2Fplugin-publish-conventions%2Forg.gradlex.internal.plugin-publish-conventions.gradle.plugin%2Fmaven-metadata.xml)](https://plugins.gradle.org/plugin/org.gradlex.internal.plugin-publish-conventions) -**Note: This plugin is currently to be used within the GradleX organization only to publish `org.gradlex` plugins** +**Note: This plugin is currently to be used within the GradleX organization only to develop `org.gradlex` plugins** # Usage +**settings.gradle.kts** ```kotlin plugins { - id("org.gradlex.internal.plugin-publish-conventions") + id("org.gradlex.internal-build-conventions") version "0.7" } +``` -pluginPublishConventions { - id("${project.group}.${project.name}") - implementationClass("org.gradlex.buildparameters.BuildParametersPlugin") - displayName("Build Parameters Gradle Plugin") - description("Compile-safe access to parameters supplied to a Gradle build.") - tags("gradlex", "parameters", "build parameters") +**build.gradle.kts** +```kotlin +publishingConventions { + pluginPortal("${project.group}.${project.name}") { + implementationClass("org.gradlex.buildparameters.BuildParametersPlugin") + displayName("Build Parameters Gradle Plugin") + description("Compile-safe access to parameters supplied to a Gradle build.") + tags("gradlex", "parameters", "build parameters") + } gitHub("https://github.com/gradlex-org/build-parameters") // ... } +testingConventions { + testGradleVersions("6.8.3", "6.9.4", "7.0.2", "8.0.2", "8.14.3") +} ``` # Disclaimer diff --git a/build.gradle.kts b/build.gradle.kts index 17876c8..fe70968 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,20 +1,97 @@ plugins { id("java-gradle-plugin") + id("org.gradlex.build-parameters") version "1.4.4" id("org.gradlex.internal.plugin-publish-conventions") version "0.6" } -group = "org.gradlex" version = "0.7" -java { - toolchain.languageVersion = JavaLanguageVersion.of(11) +dependencies { + implementation("com.diffplug.spotless:spotless-plugin-gradle:8.0.0") { + // Exclude transitive dependencies of JGit as we do not need git functionality. + // We can't exclude JGit itself as types are referenced in SpotlessTask.class. + exclude("com.googlecode.javaewah", "JavaEWAH") + exclude("commons-codec", "commons-codec") + exclude("org.slf4j", "slf4j-api") + } + implementation("com.gradle.publish:plugin-publish-plugin:2.0.0") + implementation("com.gradle:common-custom-user-data-gradle-plugin:2.4.0") + implementation("com.gradle:develocity-gradle-plugin:4.2.2") + implementation("com.gradleup.nmcp:nmcp:1.2.0") + implementation("org.asciidoctor:asciidoctor-gradle-jvm:4.0.5") + implementation("org.gradlex:jvm-dependency-conflict-resolution:2.4") + implementation("org.gradlex:reproducible-builds:1.1") +} + +dependencies.constraints { + implementation("org.jetbrains:annotations:13.0!!") { + because("This version is enforced by Gradle through the Kotlin plugin") + } +} + +// ==== the following can be remove once we update the onventions to '0.7' +group = "org.gradlex" +java { toolchain.languageVersion = JavaLanguageVersion.of(17) } +tasks.checkstyleMain { exclude("buildparameters/**") } +// ==== + +buildParameters { + pluginId("org.gradlex.internal.gradlex-build-parameters") + bool("ci") { + description = "Whether or not the build is running in a CI environment" + fromEnvironment() + defaultValue = false + } + group("signing") { + // allow to disable signing for locat testing + bool("disable") { + defaultValue = false + } + // key and passphrase need default values because SigningExtension.useInMemoryPgpKeys does not accept providers + description = "Details about artifact signing" + string("key") { + description = "The ID of the PGP key to use for signing artifacts" + fromEnvironment() + defaultValue = "UNSET" + } + string("passphrase") { + description = "The passphrase for the PGP key specified by signing.key" + fromEnvironment() + defaultValue = "UNSET" + } + } + group("pluginPortal") { + // The publish-plugin reads these values directly from System.env. We model them here + // for completeness and documentation purposes. + description = "Credentials for publishing to the plugin portal" + string("key") { + description = "The Plugin portal key for publishing the plugin" + fromEnvironment("GRADLE_PUBLISH_KEY") + } + string("secret") { + description = "The Plugin portal secret for publishing the plugin" + fromEnvironment("GRADLE_PUBLISH_SECRET") + } + } + + group("mavenCentral") { + description = "Credentials for publishing to Maven Central" + string("username") { + description = "The Maven Central username for publishing" + fromEnvironment() + } + string("password") { + description = "The Maven Central password for publishing" + fromEnvironment() + } + } } pluginPublishConventions { - id("${project.group}.internal.${project.name}") - implementationClass("org.gradlex.conventions.pluginpublish.PluginPublishConventionsPlugin") - displayName("Plugin Publish Conventions Gradle plugin") - description("Conventions for publishing GradleX plugins.") + id("${project.group}.${project.name}") + implementationClass("org.gradlex.conventions.plugin.GradleXPluginConventionsPlugin") + displayName("Conventions for building Gradle plugins") + description("Conventions for building Gradle plugins used by all projects in the GradleX organisation.") tags("gradlex", "conventions", "publish", "plugins") gitHub("https://github.com/gradlex-org/plugin-publish-conventions") developer { @@ -27,8 +104,9 @@ pluginPublishConventions { name = "Jendrik Johannes" email = "jendrik@gradlex.org" } -} - -dependencies { - implementation("com.gradle.publish:plugin-publish-plugin:2.0.0") -} + developer { + id = "ljacomet" + name = "Louis Jacomet" + email = "louis@gradlex.org" + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7064119..216f58e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,2 @@ org.gradle.caching=true -org.gradle.parallel=true -systemProp.org.gradle.unsafe.kotlin.assignment=true \ No newline at end of file +org.gradle.configuration-cache=true \ No newline at end of file diff --git a/gradle/checkstyle/header.txt b/gradle/checkstyle/header.txt index 3cdede4..4fe8a00 100644 --- a/gradle/checkstyle/header.txt +++ b/gradle/checkstyle/header.txt @@ -1,5 +1,5 @@ /* - * Copyright 2022 the GradleX team. + * Copyright the GradleX team. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/settings.gradle.kts b/settings.gradle.kts index ade93c6..bfd5f08 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,10 @@ -rootProject.name = "plugin-publish-conventions" - plugins { id("com.gradle.develocity") version "4.2.2" id("com.gradle.common-custom-user-data-gradle-plugin") version "2.4.0" } +rootProject.name = "internal-build-conventions" + dependencyResolutionManagement { repositories.gradlePluginPortal() } diff --git a/src/main/java/org/gradlex/conventions/base/DependencyRulesPlugin.java b/src/main/java/org/gradlex/conventions/base/DependencyRulesPlugin.java new file mode 100644 index 0000000..a505ad8 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/base/DependencyRulesPlugin.java @@ -0,0 +1,48 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.base; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradlex.jvm.dependency.conflict.resolution.JvmDependencyConflictResolutionPlugin; +import org.gradlex.jvm.dependency.conflict.resolution.JvmDependencyConflictsExtension; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class DependencyRulesPlugin implements Plugin { + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + + plugins.apply(JvmDependencyConflictResolutionPlugin.class); + + var jvmDependencyConflicts = extensions.getByType(JvmDependencyConflictsExtension.class); + var patch = jvmDependencyConflicts.getPatch(); + + patch.module("com.google.guava:guava", module -> { + module.removeDependency("com.google.code.findbugs:jsr305"); + module.removeDependency("com.google.errorprone:error_prone_annotations"); + module.removeDependency("com.google.guava:failureaccess"); + module.removeDependency("com.google.j2objc:j2objc-annotations"); + module.removeDependency("org.checkerframework:checker-qual"); + }); + patch.module("org.gradle.exemplar:samples-discovery", module -> { + module.removeDependency("org.asciidoctor:asciidoctorj"); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java b/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java new file mode 100644 index 0000000..aae9724 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java @@ -0,0 +1,81 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.base; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.testing.Test; +import org.jspecify.annotations.NullMarked; + +import java.util.List; + +import static org.gradle.api.plugins.JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME; +import static org.gradle.api.plugins.JavaBasePlugin.BUILD_NEEDED_TASK_NAME; +import static org.gradle.api.plugins.JavaPlugin.CLASSES_TASK_NAME; +import static org.gradle.api.plugins.JavaPlugin.TEST_CLASSES_TASK_NAME; +import static org.gradle.api.plugins.JavaPlugin.TEST_TASK_NAME; +import static org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME; +import static org.gradle.language.base.plugins.LifecycleBasePlugin.BUILD_GROUP; +import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP; + +@NullMarked +public abstract class LifecycleConventionsPlugin implements Plugin { + @Override + public void apply(Project project) { + var tasks = project.getTasks(); + + tasks.register("qualityCheck", task -> { + task.setGroup(VERIFICATION_GROUP); + task.setDescription("Run all spotless and quality checks."); + task.dependsOn(tasks.named(ASSEMBLE_TASK_NAME)); + }); + tasks.register("qualityGate", task -> { + task.setGroup(BUILD_GROUP); + task.setDescription("Apply spotless rules and run all quality checks."); + task.dependsOn(tasks.named(ASSEMBLE_TASK_NAME)); + }); + tasks.register("quickCheck", task -> { + task.setGroup(VERIFICATION_GROUP); + task.setDescription("Runs all of qualityCheck and tests only against the current Gradle version."); + task.dependsOn(tasks.named("qualityCheck")); + task.dependsOn(tasks.named(TEST_TASK_NAME)); + }); + + // cleanup 'build' group + var unimportantLifecycleTasks = List.of( + BUILD_DEPENDENTS_TASK_NAME, + BUILD_NEEDED_TASK_NAME, + CLASSES_TASK_NAME, + TEST_CLASSES_TASK_NAME, + "testSamplesClasses" + ); + project.afterEvaluate(__ -> + tasks.configureEach(task -> { + if (unimportantLifecycleTasks.contains(task.getName())) { + task.setGroup(null); + } + if (task instanceof Jar) { + task.setGroup(null); + } + if (task instanceof Test) { + task.setGroup(BUILD_GROUP); + } + }) + ); + } +} diff --git a/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java new file mode 100644 index 0000000..8eb5e6d --- /dev/null +++ b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java @@ -0,0 +1,63 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.check; + +import com.diffplug.gradle.spotless.SpotlessExtension; +import com.diffplug.gradle.spotless.SpotlessPlugin; +import com.diffplug.spotless.LineEnding; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradlex.conventions.base.LifecycleConventionsPlugin; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class SpotlessConventionsPlugin implements Plugin { + + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + var tasks = project.getTasks(); + + plugins.apply(SpotlessPlugin.class); + plugins.apply(LifecycleConventionsPlugin.class); + + var spotless = extensions.getByType(SpotlessExtension.class); + + tasks.named("qualityCheck", task -> task.dependsOn(tasks.named("spotlessCheck"))); + tasks.named("qualityGate", task -> task.dependsOn(tasks.named("spotlessApply"))); + + spotless.setLineEndings(LineEnding.UNIX); + + // format the source code + spotless.java(java -> { + java.targetExclude("build/**"); + java.palantirJavaFormat(); + java.licenseHeader("// SPDX-License-Identifier: Apache-2.0\n", "package|import"); + }); + // separate 'package-info' formatting due to https://github.com/diffplug/spotless/issues/532 + spotless.format("javaPackageInfoFiles", java -> { + java.targetExclude("build"); + java.target("src/**/package-info.java"); + java.licenseHeader("// SPDX-License-Identifier: Apache-2.0\n", "package|import|@"); + }); + + // format the build itself + spotless.kotlinGradle(gradle -> + gradle.ktfmt().kotlinlangStyle().configure(conf -> conf.setMaxWidth(120))); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/AsciidoctorConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/AsciidoctorConventionsPlugin.java new file mode 100644 index 0000000..7c572d6 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/AsciidoctorConventionsPlugin.java @@ -0,0 +1,71 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.asciidoctor.gradle.base.log.Severity; +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.plugins.JavaPlugin; +import org.gradle.api.tasks.PathSensitivity; +import org.jspecify.annotations.NullMarked; + +import java.util.HashMap; +import java.util.Map; + +@NullMarked +public abstract class AsciidoctorConventionsPlugin implements Plugin { + + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var layout = project.getLayout(); + var tasks = project.getTasks(); + + plugins.apply(JavaPlugin.class); + plugins.apply(AsciidoctorJPlugin.class); + + tasks.named("asciidoctor", AsciidoctorTask.class, task -> { + task.notCompatibleWithConfigurationCache( + "https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/564"); + task.getInputs().dir("src/docs/samples") + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("samples"); + + task.setFailureLevel(Severity.WARN); + + Map attributes = new HashMap<>(); + attributes.put("docinfodir", "src/docs/asciidoc"); + attributes.put("docinfo", "shared"); + attributes.put("imagesdir", "./images"); + attributes.put("source-highlighter", "prettify"); + attributes.put("tabsize", "4"); + attributes.put("toc", "left"); + attributes.put("tip-caption", "💡"); + attributes.put("note-caption", "ℹ️"); + attributes.put("important-caption", "❗"); + attributes.put("caution-caption", "🔥"); + attributes.put("warning-caption", "⚠️"); + attributes.put("sectanchors", true); + attributes.put("idprefix", ""); + attributes.put("idseparator", "-"); + attributes.put("samples-path", layout.getProjectDirectory().dir("src/docs/samples").getAsFile().toString()); + task.setAttributes(attributes); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/CompileConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/CompileConventionsPlugin.java new file mode 100644 index 0000000..a026c5c --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/CompileConventionsPlugin.java @@ -0,0 +1,65 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +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.tasks.SourceSetContainer; +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradlex.reproduciblebuilds.ReproducibleBuildsPlugin; +import org.jspecify.annotations.NullMarked; + +import static org.gradle.api.plugins.JavaPlugin.COMPILE_JAVA_TASK_NAME; +import static org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME; + +@NullMarked +public abstract class CompileConventionsPlugin implements Plugin { + + private static final int JDK_VERSION = 17; + private static final int JDK_GRADLE_RT_VERSION = 8; + + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + var tasks = project.getTasks(); + + plugins.apply(JavaPlugin.class); + plugins.apply(ReproducibleBuildsPlugin.class); + + var sourceSets = extensions.getByType(SourceSetContainer.class); + var java = extensions.getByType(JavaPluginExtension.class); + + sourceSets.forEach(sourceSet -> { + var classes = tasks.named(sourceSet.getClassesTaskName()); + tasks.named(ASSEMBLE_TASK_NAME, task -> task.dependsOn(classes)); + }); + + java.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(JDK_VERSION)); + tasks.named(COMPILE_JAVA_TASK_NAME, JavaCompile.class, task -> + task.getOptions().getRelease().set(JDK_GRADLE_RT_VERSION)); + + tasks.withType(JavaCompile.class).configureEach(task -> { + task.getOptions().getCompilerArgs().add("-implicit:none"); + task.getOptions().getCompilerArgs().add("-Werror"); + task.getOptions().getCompilerArgs().add("-Xlint:all,-serial"); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/JavadocConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/JavadocConventionsPlugin.java new file mode 100644 index 0000000..c72d156 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/JavadocConventionsPlugin.java @@ -0,0 +1,43 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.gradle.external.javadoc.StandardJavadocDocletOptions; +import org.gradlex.reproduciblebuilds.ReproducibleBuildsPlugin; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class JavadocConventionsPlugin implements Plugin { + + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var tasks = project.getTasks(); + + plugins.apply(JavaPlugin.class); + plugins.apply(ReproducibleBuildsPlugin.class); + + tasks.withType(Javadoc.class).configureEach(task -> { + var options = (StandardJavadocDocletOptions) task.getOptions(); + options.addStringOption("Xdoclint:all,-missing", "-Xwerror"); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java new file mode 100644 index 0000000..3ccbdca --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java @@ -0,0 +1,94 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.provider.Provider; +import org.gradle.api.publish.maven.MavenPomDeveloper; +import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; +import org.jspecify.annotations.NullMarked; + +import java.util.ArrayList; +import java.util.List; + +@NullMarked +public abstract class PublishingConventionsExtension { + + public static final String NAME = "publishingConventions"; + + private final Project project; + private final GradlePluginDevelopmentExtension gradlePlugin; + private final List definitions = new ArrayList<>(); + + List> developers = new ArrayList<>(); + + public PublishingConventionsExtension( + Project project, + GradlePluginDevelopmentExtension gradlePlugin + ) { + this.project = project; + this.gradlePlugin = gradlePlugin; + } + + public void pluginPortal(String id, Action action) { + var definition = project.getObjects().newInstance( + PublishingPluginPortalDefinition.class, id, gradlePlugin, project.getProviders()); + definitions.stream().filter(d -> d instanceof PublishingPluginPortalDefinition).findFirst().ifPresent( + d -> definition.getTags().set(((PublishingPluginPortalDefinition) d).getTags())); + action.execute(definition); + definitions.add(definition); + } + + public void mavenCentral(Action action) { + var definition = project.getObjects().newInstance(PublishingMavenCentralDefinition.class); + action.execute(definition); + definitions.add(definition); + } + + public void gitHub(String gitHub) { + gradlePlugin.getVcsUrl().set(gitHub); + gradlePlugin.getWebsite().convention(gitHub); + } + + public void website(String website) { + gradlePlugin.getWebsite().set(website); + } + + public void developer(Action action) { + developers.add(action); + } + + public Provider getGitHub() { + return gradlePlugin.getVcsUrl(); + } + + public Provider getWebsite() { + return gradlePlugin.getWebsite(); + } + + public Provider getDisplayName() { + if (definitions.isEmpty()) { throw new GradleException("No publication defined"); } + return definitions.get(0).getDisplayName(); + } + + public Provider getDescription() { + if (definitions.isEmpty()) { throw new GradleException("No publication defined"); } + return definitions.get(0).getDescription(); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java new file mode 100644 index 0000000..f6ed20d --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java @@ -0,0 +1,99 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import buildparameters.BuildParametersExtension; +import buildparameters.GeneratedBuildParametersPlugin; +import com.gradle.publish.PublishPlugin; +import nmcp.NmcpAggregationExtension; +import nmcp.NmcpAggregationPlugin; +import nmcp.NmcpPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; +import org.gradle.plugins.signing.SigningExtension; +import org.gradle.plugins.signing.SigningPlugin; +import org.jspecify.annotations.NullMarked; + +import static org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME; + +@NullMarked +public abstract class PublishingConventionsPlugin implements Plugin { + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + var tasks = project.getTasks(); + var dependencies = project.getDependencies(); + + plugins.apply(PublishPlugin.class); + plugins.apply(NmcpPlugin.class); + plugins.apply(NmcpAggregationPlugin.class); + plugins.apply(SigningPlugin.class); + plugins.apply(GeneratedBuildParametersPlugin.class); + + var java = extensions.getByType(JavaPluginExtension.class); + var gradlePlugin = extensions.getByType(GradlePluginDevelopmentExtension.class); + var publishing = extensions.getByType(PublishingExtension.class); + var nmcpAggregation = extensions.getByType(NmcpAggregationExtension.class); + var signing = extensions.getByType(SigningExtension.class); + var buildParameters = extensions.getByType(BuildParametersExtension.class); + var pluginPublishConventions = extensions.create( + PublishingConventionsExtension.NAME, PublishingConventionsExtension.class, + project, gradlePlugin); + + tasks.named("publishPlugins", task -> task.dependsOn(CHECK_TASK_NAME)); + + java.withJavadocJar(); + java.withSourcesJar(); + + // signing + signing.setRequired(!buildParameters.getSigning().getDisable()); + if (signing.isRequired()) { + signing.useInMemoryPgpKeys( + buildParameters.getSigning().getKey(), + buildParameters.getSigning().getPassphrase() + ); + } + + // Maven Central + dependencies.add("nmcpAggregation", project.project(project.getPath())); // for NmcpAggregationPlugin + nmcpAggregation.centralPortal(central -> { + central.getUsername().set(buildParameters.getMavenCentral().getUsername()); + central.getPassword().set(buildParameters.getMavenCentral().getPassword()); + central.getPublishingType().set("AUTOMATIC"); // "USER_MANAGED" + }); + + // Metadata for all publications + publishing.getPublications().withType(MavenPublication.class).configureEach(p -> { + p.getPom().getName().set(pluginPublishConventions.getDisplayName()); + p.getPom().getDescription().set(pluginPublishConventions.getDescription()); + p.getPom().getUrl().set(pluginPublishConventions.getWebsite()); + p.getPom().licenses(licenses -> { + licenses.license(l -> { + l.getName().set("Apache-2.0"); + l.getUrl().set("http://www.apache.org/licenses/LICENSE-2.0.txt"); + }); + }); + p.getPom().scm(scm -> scm.getUrl().set(pluginPublishConventions.getGitHub())); + p.getPom().developers(d -> pluginPublishConventions.developers.forEach(d::developer)); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/PublishingDefinition.java b/src/main/java/org/gradlex/conventions/feature/PublishingDefinition.java new file mode 100644 index 0000000..20d64d4 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PublishingDefinition.java @@ -0,0 +1,27 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.provider.Provider; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public interface PublishingDefinition { + Provider getDisplayName(); + + Provider getDescription(); +} diff --git a/src/main/java/org/gradlex/conventions/feature/PublishingMavenCentralDefinition.java b/src/main/java/org/gradlex/conventions/feature/PublishingMavenCentralDefinition.java new file mode 100644 index 0000000..c5e7128 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PublishingMavenCentralDefinition.java @@ -0,0 +1,36 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.provider.Property; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class PublishingMavenCentralDefinition implements PublishingDefinition { + + public abstract Property getDisplayName(); + + public abstract Property getDescription(); + + public void displayName(String displayName) { + getDisplayName().set(displayName); + } + + public void description(String description) { + getDescription().set(description); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/PublishingPluginPortalDefinition.java b/src/main/java/org/gradlex/conventions/feature/PublishingPluginPortalDefinition.java new file mode 100644 index 0000000..b22ef5c --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PublishingPluginPortalDefinition.java @@ -0,0 +1,84 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.provider.SetProperty; +import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; +import org.gradle.plugin.devel.PluginDeclaration; +import org.jspecify.annotations.NullMarked; + +import javax.inject.Inject; +import java.util.Arrays; +import java.util.stream.Collectors; + +@NullMarked +public abstract class PublishingPluginPortalDefinition implements PublishingDefinition { + + private final PluginDeclaration pluginDeclaration; + private final ProviderFactory providers; + + @Inject + public PublishingPluginPortalDefinition(String id, GradlePluginDevelopmentExtension gradlePlugin, ProviderFactory providers) { + this.pluginDeclaration = gradlePlugin.getPlugins().create(toCamelCase(id).replace("org.gradlex.", "")); + this.pluginDeclaration.setId(id); + this.providers = providers; + } + + public void implementationClass(String implementationClass) { + pluginDeclaration.setImplementationClass(implementationClass); + } + + public void displayName(String displayName) { + pluginDeclaration.setDisplayName(displayName); + } + + public void description(String description) { + pluginDeclaration.setDescription(description); + } + + public void tags(String... tags) { + pluginDeclaration.getTags().set(Arrays.asList(tags)); + } + + public Provider getId() { + return providers.provider(pluginDeclaration::getId); + } + + public Provider getImplementationClass() { + return providers.provider(pluginDeclaration::getImplementationClass); + } + + public Provider getDisplayName() { + return providers.provider(pluginDeclaration::getDisplayName); + } + + public Provider getDescription() { + return providers.provider(pluginDeclaration::getDescription); + } + + public SetProperty getTags() { + return pluginDeclaration.getTags(); + } + + private static String toCamelCase(String s) { + var cc = Arrays.stream(s.split("-")).map(segment -> + segment.substring(0, 1).toUpperCase() + segment.substring(1)).collect(Collectors.joining()); + return cc.substring(0, 1).toLowerCase() + cc.substring(1); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java new file mode 100644 index 0000000..deb9782 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java @@ -0,0 +1,58 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.plugins.jvm.JvmTestSuite; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.jspecify.annotations.NullMarked; + +import java.util.Arrays; + +import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP; + +@SuppressWarnings("UnstableApiUsage") +@NullMarked +public abstract class TestingConventionsExtension { + + public static final String NAME = "testingConventions"; + + private final JvmTestSuite suite; + private final JavaToolchainService javaToolchains; + + public TestingConventionsExtension(JvmTestSuite suite, JavaToolchainService javaToolchains) { + this.suite = suite; + this.javaToolchains = javaToolchains; + } + + public void testGradleVersions(String... gradleVersions) { + Arrays.stream(gradleVersions).forEach(gradleVersionUnderTest -> + suite.getTargets().register("test" + gradleVersionUnderTest, target -> + target.getTestTask().configure(testTask -> { + testTask.setGroup(VERIFICATION_GROUP); + testTask.setDescription("Runs tests against Gradle" + gradleVersionUnderTest); + testTask.systemProperty("gradleVersionUnderTest", gradleVersionUnderTest); + testTask.useJUnitPlatform(junit -> junit.excludeTags("no-cross-version")); + if (gradleVersionUnderTest.startsWith("6")) { + testTask.getJavaLauncher().set(javaToolchains.launcherFor(tc -> + tc.getLanguageVersion().set(JavaLanguageVersion.of(11)))); + } + }) + ) + ); + } +} diff --git a/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java new file mode 100644 index 0000000..bf7e677 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java @@ -0,0 +1,81 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.feature; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.jvm.JvmTestSuite; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.testing.base.TestingExtension; +import org.jspecify.annotations.NullMarked; + +@SuppressWarnings("UnstableApiUsage") +@NullMarked +public abstract class TestingConventionsPlugin implements Plugin { + + @Override + public void apply(Project project) { + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + var layout = project.getLayout(); + + plugins.apply(JavaPlugin.class); + + var testing = extensions.getByType(TestingExtension.class); + var javaToolchains = extensions.getByType(JavaToolchainService.class); + + var samplesDir = layout.getProjectDirectory().dir("samples"); + var snippetsDir = layout.getProjectDirectory().dir("src/docs/snippets"); + + // Unite tests with cross-version support + var testSuite = testing.getSuites().named("test", JvmTestSuite.class, suite -> { + suite.useJUnitJupiter(); + suite.dependencies(dependencies-> dependencies.getImplementation().add("org.assertj:assertj-core:3.27.6")); + }); + + extensions.create(TestingConventionsExtension.NAME, TestingConventionsExtension.class, + testSuite.get(), javaToolchains); + + // tested samples + testing.getSuites().register("testSamples", JvmTestSuite.class, suite -> { + suite.getTargets().configureEach(target -> + target.getTestTask().configure(testTask -> { + testTask.setMaxParallelForks(4); + if (samplesDir.getAsFile().exists()) { + testTask.getInputs() + .dir(samplesDir) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("samples"); + } + if (snippetsDir.getAsFile().exists()) { + testTask.getInputs() + .dir(snippetsDir) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("snippets"); + } + } + ) + ); + suite.dependencies(dependencies-> { + dependencies.getImplementation().add("org.gradle.exemplar:samples-check:1.0.3"); + dependencies.getImplementation().add("org.junit.vintage:junit-vintage-engine"); + }); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java b/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java new file mode 100644 index 0000000..6988e7f --- /dev/null +++ b/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java @@ -0,0 +1,59 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.plugin; + +import org.gradle.api.Plugin; +import org.gradle.api.initialization.Settings; +import org.gradlex.conventions.base.DependencyRulesPlugin; +import org.gradlex.conventions.base.LifecycleConventionsPlugin; +import org.gradlex.conventions.check.SpotlessConventionsPlugin; +import org.gradlex.conventions.feature.AsciidoctorConventionsPlugin; +import org.gradlex.conventions.feature.CompileConventionsPlugin; +import org.gradlex.conventions.feature.JavadocConventionsPlugin; +import org.gradlex.conventions.feature.PublishingConventionsPlugin; +import org.gradlex.conventions.feature.TestingConventionsPlugin; +import org.gradlex.conventions.report.DevelocityConventionsPlugin; +import org.jspecify.annotations.NullMarked; + +@SuppressWarnings("UnstableApiUsage") +@NullMarked +public class GradleXPluginConventionsPlugin implements Plugin { + + @Override + public void apply(Settings settings) { + var settingsPlugins = settings.getPlugins(); + var repositories = settings.getDependencyResolutionManagement().getRepositories(); + + repositories.gradlePluginPortal(); + settingsPlugins.apply(DevelocityConventionsPlugin.class); + + settings.getGradle().getLifecycle().beforeProject(project -> { + var plugins = project.getPlugins(); + + project.setGroup("org.gradlex"); + + plugins.apply(LifecycleConventionsPlugin.class); + plugins.apply(DependencyRulesPlugin.class); + plugins.apply(CompileConventionsPlugin.class); + plugins.apply(JavadocConventionsPlugin.class); + plugins.apply(TestingConventionsPlugin.class); + plugins.apply(AsciidoctorConventionsPlugin.class); + plugins.apply(PublishingConventionsPlugin.class); + plugins.apply(SpotlessConventionsPlugin.class); + }); + } +} diff --git a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsExtension.java b/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsExtension.java deleted file mode 100644 index c4d49f4..0000000 --- a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsExtension.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2022 the GradleX team. - * - * 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 - * - * http://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.gradlex.conventions.pluginpublish; - -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.provider.Provider; -import org.gradle.api.publish.maven.MavenPomDeveloper; -import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; -import org.gradle.plugin.devel.PluginDeclaration; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -@SuppressWarnings("unused") -public abstract class PluginPublishConventionsExtension { - - public static final String NAME = "pluginPublishConventions"; - - private final Project project; - private final GradlePluginDevelopmentExtension gradlePlugin; - private final PluginDeclaration pluginDefinition; - - List> developers = new ArrayList<>(); - - public PluginPublishConventionsExtension( - Project project, - GradlePluginDevelopmentExtension gradlePlugin - ) { - this.project = project; - this.gradlePlugin = gradlePlugin; - this.pluginDefinition = gradlePlugin.getPlugins().create(toCamelCase(project.getName())); - } - - - public Provider getId() { - return project.getProviders().provider(pluginDefinition::getId); - } - - public Provider getImplementationClass() { - return project.getProviders().provider(pluginDefinition::getImplementationClass); - } - - public Provider getDisplayName() { - return project.getProviders().provider(pluginDefinition::getDisplayName); - } - - public Provider getDescription() { - return project.getProviders().provider(pluginDefinition::getDescription); - } - - public Provider getGitHub() { - return gradlePlugin.getVcsUrl(); - } - - public Provider getWebsite() { - return gradlePlugin.getWebsite(); - } - - public Provider> getTags() { - return pluginDefinition.getTags(); - } - - public void id(String id) { - pluginDefinition.setId(id); - } - - public void implementationClass(String implementationClass) { - pluginDefinition.setImplementationClass(implementationClass); - } - - public void displayName(String displayName) { - pluginDefinition.setDisplayName(displayName); - } - - public void description(String description) { - pluginDefinition.setDescription(description); - } - - public void gitHub(String gitHub) { - gradlePlugin.getVcsUrl().set(gitHub); - gradlePlugin.getWebsite().convention(gitHub); - } - - public void website(String website) { - gradlePlugin.getWebsite().set(website); - } - - public void tags(String... tags) { - pluginDefinition.getTags().set(Arrays.asList(tags)); - } - - public void developer(Action action) { - developers.add(action); - } - - private static String toCamelCase(String s) { - String cc = Arrays.stream(s.split("-")).map(segment -> - segment.substring(0, 1).toUpperCase() + segment.substring(1)).collect(Collectors.joining()); - return cc.substring(0, 1).toLowerCase() + cc.substring(1); - } -} diff --git a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java b/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java deleted file mode 100644 index 15430ae..0000000 --- a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022 the GradleX team. - * - * 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 - * - * http://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.gradlex.conventions.pluginpublish; - -import com.gradle.publish.PublishPlugin; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.plugins.ExtensionContainer; -import org.gradle.api.plugins.PluginContainer; -import org.gradle.api.plugins.quality.CheckstyleExtension; -import org.gradle.api.plugins.quality.CheckstylePlugin; -import org.gradle.api.provider.ProviderFactory; -import org.gradle.api.publish.PublishingExtension; -import org.gradle.api.publish.maven.MavenPublication; -import org.gradle.plugin.devel.GradlePluginDevelopmentExtension; -import org.gradle.plugins.signing.SigningExtension; -import org.gradle.plugins.signing.SigningPlugin; - -@SuppressWarnings("unused") -public abstract class PluginPublishConventionsPlugin implements Plugin { - @Override - public void apply(Project project) { - PluginContainer plugins = project.getPlugins(); - ExtensionContainer extensions = project.getExtensions(); - ProviderFactory providers = project.getProviders(); - - plugins.apply(CheckstylePlugin.class); - plugins.apply(PublishPlugin.class); - plugins.apply(SigningPlugin.class); - - GradlePluginDevelopmentExtension gradlePlugin = extensions.getByType(GradlePluginDevelopmentExtension.class); - PublishingExtension publishing = extensions.getByType(PublishingExtension.class); - CheckstyleExtension checkstyle = extensions.getByType(CheckstyleExtension.class); - SigningExtension signing = extensions.getByType(SigningExtension.class); - - signing.useInMemoryPgpKeys( - providers.environmentVariable("SIGNING_KEY").getOrNull(), - providers.environmentVariable("SIGNING_PASSPHRASE").getOrNull() - ); - - PluginPublishConventionsExtension pluginPublishConventions = extensions.create( - PluginPublishConventionsExtension.NAME, PluginPublishConventionsExtension.class, - project, gradlePlugin); - - publishing.getPublications().withType(MavenPublication.class).configureEach(p -> { - p.getPom().getName().set(pluginPublishConventions.getDisplayName()); - p.getPom().getDescription().set(pluginPublishConventions.getDescription()); - p.getPom().getUrl().set(pluginPublishConventions.getWebsite()); - p.getPom().licenses(licenses -> { - licenses.license(l -> { - l.getName().set("Apache-2.0"); - l.getUrl().set("http://www.apache.org/licenses/LICENSE-2.0.txt"); - }); - }); - p.getPom().scm(scm -> scm.getUrl().set(pluginPublishConventions.getGitHub())); - p.getPom().developers(d -> pluginPublishConventions.developers.forEach(d::developer)); - }); - - checkstyle.getConfigDirectory().set(project.getLayout().getProjectDirectory().dir("gradle/checkstyle")); - } -} diff --git a/src/main/java/org/gradlex/conventions/report/DevelocityConventionsPlugin.java b/src/main/java/org/gradlex/conventions/report/DevelocityConventionsPlugin.java new file mode 100644 index 0000000..3ba18d4 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/report/DevelocityConventionsPlugin.java @@ -0,0 +1,52 @@ +/* + * Copyright the GradleX team. + * + * 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 + * + * http://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.gradlex.conventions.report; + +import buildparameters.BuildParametersExtension; +import buildparameters.GeneratedBuildParametersPlugin; +import com.gradle.CommonCustomUserDataGradlePlugin; +import com.gradle.develocity.agent.gradle.DevelocityConfiguration; +import com.gradle.develocity.agent.gradle.DevelocityPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.initialization.Settings; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class DevelocityConventionsPlugin implements Plugin { + + @Override + public void apply(Settings settings) { + var plugins = settings.getPlugins(); + var extensions = settings.getExtensions(); + + plugins.apply(DevelocityPlugin.class); + plugins.apply(CommonCustomUserDataGradlePlugin.class); + plugins.apply(GeneratedBuildParametersPlugin.class); + + var develocity = extensions.getByType(DevelocityConfiguration.class); + var buildParameters = extensions.getByType(BuildParametersExtension.class); + + develocity.buildScan(buildScan -> { + // required to bind this to a local variable for configuration cache compatibility + var isCi = buildParameters.getCi(); + + buildScan.getTermsOfUseUrl().set("https://gradle.com/help/legal-terms-of-use"); + buildScan.getTermsOfUseAgree().set("yes"); + buildScan.getPublishing().onlyIf(__ -> isCi); + }); + } +}