From f87145cfe34efb7ac3af39e51a787e96fd8cd195 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Fri, 17 Oct 2025 12:55:54 +0200 Subject: [PATCH 1/7] Define more conventions for all GradleX projects --- README.md | 11 ++- build.gradle.kts | 67 ++++++++++++--- gradle.properties | 3 +- gradle/checkstyle/header.txt | 2 +- .../base/DependencyRulesPlugin.java | 48 +++++++++++ .../base/LifecycleConventionsPlugin.java | 76 +++++++++++++++++ .../check/SpotlessConventionsPlugin.java | 58 +++++++++++++ .../feature/AsciidoctorConventionsPlugin.java | 71 ++++++++++++++++ .../feature/CompileConventionsPlugin.java | 65 +++++++++++++++ .../feature/JavadocConventionsPlugin.java | 43 ++++++++++ .../conventions/feature/PluginDefinition.java | 60 ++++++++++++++ .../PluginPublishConventionsExtension.java | 81 +++++++------------ .../PluginPublishConventionsPlugin.java | 54 +++++++------ .../feature/TestingConventionsExtension.java | 61 ++++++++++++++ .../feature/TestingConventionsPlugin.java | 71 ++++++++++++++++ .../GradleXPluginConventionsPlugin.java | 59 ++++++++++++++ .../report/DevelocityConventionsPlugin.java | 52 ++++++++++++ 17 files changed, 788 insertions(+), 94 deletions(-) create mode 100644 src/main/java/org/gradlex/conventions/base/DependencyRulesPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/feature/AsciidoctorConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/feature/CompileConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/feature/JavadocConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/feature/PluginDefinition.java rename src/main/java/org/gradlex/conventions/{pluginpublish => feature}/PluginPublishConventionsExtension.java (66%) rename src/main/java/org/gradlex/conventions/{pluginpublish => feature}/PluginPublishConventionsPlugin.java (61%) create mode 100644 src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java create mode 100644 src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java create mode 100644 src/main/java/org/gradlex/conventions/report/DevelocityConventionsPlugin.java diff --git a/README.md b/README.md index bd5ae81..f94a729 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,19 @@ [![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.gradlex-build-conventions") version "0.7" } +``` +**build.gradle.kts** +```kotlin pluginPublishConventions { id("${project.group}.${project.name}") implementationClass("org.gradlex.buildparameters.BuildParametersPlugin") @@ -21,6 +25,9 @@ pluginPublishConventions { 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..3f915a8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,20 +1,67 @@ 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") + 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("org.asciidoctor:asciidoctor-gradle-jvm:4.0.5") + implementation("org.gradlex:jvm-dependency-conflict-resolution:2.4") + implementation("org.gradlex:reproducible-builds:1.1") +} + +// ==== 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("gradlexbuild.build-parameters") + bool("ci") { + description.set("Whether or not the build is running in a CI environment") + fromEnvironment() + defaultValue.set(false) + } + group("signing") { + // key and passphrase need default values because SigningExtension.useInMemoryPgpKeys does not accept providers + description.set("Details about artifact signing") + string("key") { + description.set("The ID of the PGP key to use for signing artifacts") + fromEnvironment() + defaultValue.set("UNSET") + } + string("passphrase") { + description.set("The passphrase for the PGP key specified by signing.key") + fromEnvironment() + defaultValue.set("UNSET") + } + } + group("pluginPortal") { + // The publish-plugin reads these values directly from System.env. We model them here + // for completeness and documentation purposes. + description.set("Credentials for publishing to the plugin portal") + string("key") { + description.set("The Plugin portal key for publishing the plugin") + fromEnvironment("GRADLE_PUBLISH_KEY") + } + string("secret") { + description.set("The Plugin portal secret for publishing the plugin") + fromEnvironment("GRADLE_PUBLISH_SECRET") + } + } } +// ==== 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}.internal.gradlex-build-conventions") + 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 +74,4 @@ pluginPublishConventions { name = "Jendrik Johannes" email = "jendrik@gradlex.org" } -} - -dependencies { - implementation("com.gradle.publish:plugin-publish-plugin:2.0.0") -} +} \ 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/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..e4ba368 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java @@ -0,0 +1,76 @@ +/* + * 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); + 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..a14b192 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.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.check; + +import com.diffplug.gradle.spotless.SpotlessExtension; +import com.diffplug.gradle.spotless.SpotlessPlugin; +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"))); + + // format the source code + spotless.java(java -> { + java.palantirJavaFormat(); + java.licenseHeader("// SPDX-License-Identifier: Apache-2.0\n", "package|import"); + }); + spotless.format("javaPackageInfoFiles", java -> { + // add a separate extension due to https://github.com/diffplug/spotless/issues/532 + 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/PluginDefinition.java b/src/main/java/org/gradlex/conventions/feature/PluginDefinition.java new file mode 100644 index 0000000..ba56300 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/PluginDefinition.java @@ -0,0 +1,60 @@ +/* + * 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.plugin.devel.GradlePluginDevelopmentExtension; +import org.gradle.plugin.devel.PluginDeclaration; + +import javax.inject.Inject; +import java.util.Arrays; +import java.util.stream.Collectors; + +public abstract class PluginDefinition { + + protected final PluginDeclaration pluginDefinition; + + @Inject + public PluginDefinition(String name, GradlePluginDevelopmentExtension gradlePlugin) { + this.pluginDefinition = gradlePlugin.getPlugins().create(toCamelCase(name).replace("org.gradlex.", "")); + } + + 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 tags(String... tags) { + pluginDefinition.getTags().set(Arrays.asList(tags)); + } + + 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/pluginpublish/PluginPublishConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsExtension.java similarity index 66% rename from src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsExtension.java rename to src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsExtension.java index c4d49f4..744244b 100644 --- a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsExtension.java +++ b/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsExtension.java @@ -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. @@ -14,29 +14,26 @@ * limitations under the License. */ -package org.gradlex.conventions.pluginpublish; +package org.gradlex.conventions.feature; 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 org.jspecify.annotations.NullMarked; 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 { +@NullMarked +public abstract class PluginPublishConventionsExtension extends PluginDefinition { public static final String NAME = "pluginPublishConventions"; private final Project project; private final GradlePluginDevelopmentExtension gradlePlugin; - private final PluginDeclaration pluginDefinition; List> developers = new ArrayList<>(); @@ -44,11 +41,30 @@ public PluginPublishConventionsExtension( Project project, GradlePluginDevelopmentExtension gradlePlugin ) { + super(project.getName(), gradlePlugin); this.project = project; this.gradlePlugin = gradlePlugin; - this.pluginDefinition = gradlePlugin.getPlugins().create(toCamelCase(project.getName())); } + public void additionalPlugin(String id, Action action) { + PluginDefinition plugin = project.getObjects().newInstance(PluginDefinition.class, id, gradlePlugin); + plugin.id(id); + plugin.pluginDefinition.getTags().set(getTags()); + action.execute(plugin); + } + + 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 getId() { return project.getProviders().provider(pluginDefinition::getId); @@ -66,54 +82,15 @@ 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); + public Provider getGitHub() { + return gradlePlugin.getVcsUrl(); } - 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); + public Provider getWebsite() { + return gradlePlugin.getWebsite(); } } diff --git a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java similarity index 61% rename from src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java rename to src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java index 15430ae..5ee1fea 100644 --- a/src/main/java/org/gradlex/conventions/pluginpublish/PluginPublishConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java @@ -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. @@ -14,48 +14,54 @@ * limitations under the License. */ -package org.gradlex.conventions.pluginpublish; +package org.gradlex.conventions.feature; +import buildparameters.BuildParametersExtension; +import buildparameters.GeneratedBuildParametersPlugin; 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.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; -@SuppressWarnings("unused") +import static org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME; + +@NullMarked 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); + var plugins = project.getPlugins(); + var extensions = project.getExtensions(); + var tasks = project.getTasks(); + plugins.apply(PublishPlugin.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 signing = extensions.getByType(SigningExtension.class); + var buildParameters = extensions.getByType(BuildParametersExtension.class); + var pluginPublishConventions = extensions.create( + PluginPublishConventionsExtension.NAME, PluginPublishConventionsExtension.class, + project, gradlePlugin); - GradlePluginDevelopmentExtension gradlePlugin = extensions.getByType(GradlePluginDevelopmentExtension.class); - PublishingExtension publishing = extensions.getByType(PublishingExtension.class); - CheckstyleExtension checkstyle = extensions.getByType(CheckstyleExtension.class); - SigningExtension signing = extensions.getByType(SigningExtension.class); + tasks.named("publishPlugins", task -> task.dependsOn(CHECK_TASK_NAME)); + + java.withJavadocJar(); + java.withSourcesJar(); signing.useInMemoryPgpKeys( - providers.environmentVariable("SIGNING_KEY").getOrNull(), - providers.environmentVariable("SIGNING_PASSPHRASE").getOrNull() + buildParameters.getSigning().getKey(), + buildParameters.getSigning().getPassphrase() ); - 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()); @@ -69,7 +75,5 @@ public void apply(Project project) { 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/feature/TestingConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java new file mode 100644 index 0000000..e337fed --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java @@ -0,0 +1,61 @@ +/* + * 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", + "org.gradlex.testing.NoCrossVersion" + )); + 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..8f608d1 --- /dev/null +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.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.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.jvm.JvmTestSuite; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.testing.base.TestingExtension; +import org.jspecify.annotations.NullMarked; + +import java.util.Map; + +@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 docsSamplesDir = layout.getProjectDirectory().dir("src/docs/samples"); + + var testSuite = testing.getSuites().named("test", JvmTestSuite.class, suite -> { + suite.useJUnitJupiter(); + suite.getTargets().configureEach(target -> + target.getTestTask().configure(testTask -> { + testTask.setMaxParallelForks(4); + if (samplesDir.getAsFile().exists()) { + testTask.getInputs().dir(samplesDir); + } + if (docsSamplesDir.getAsFile().exists()) { + testTask.getInputs().dir(docsSamplesDir); + } + } + ) + ); + suite.dependencies(dependencies-> { + dependencies.getImplementation().add("org.assertj:assertj-core:3.27.6"); + dependencies.getImplementation().add("org.gradle.exemplar:samples-check:1.0.3"); + dependencies.getImplementation().add("org.junit.vintage:junit-vintage-engine"); + }); + }); + + extensions.create(TestingConventionsExtension.NAME, TestingConventionsExtension.class, + testSuite.get(), javaToolchains); + } +} 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..dcae135 --- /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.PluginPublishConventionsPlugin; +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(PluginPublishConventionsPlugin.class); + plugins.apply(SpotlessConventionsPlugin.class); + }); + } +} 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); + }); + } +} From 9f08064f6d91a2b42cdb5a524b6b8abb8755bf5e Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Tue, 21 Oct 2025 09:56:15 +0200 Subject: [PATCH 2/7] Support publishing to Maven Central --- build.gradle.kts | 48 ++++++++--- .../conventions/feature/PluginDefinition.java | 60 ------------- ...va => PublishingConventionsExtension.java} | 54 ++++++------ ....java => PublishingConventionsPlugin.java} | 32 +++++-- .../feature/PublishingDefinition.java | 27 ++++++ .../PublishingMavenCentralDefinition.java | 36 ++++++++ .../PublishingPluginPortalDefinition.java | 84 +++++++++++++++++++ .../GradleXPluginConventionsPlugin.java | 4 +- 8 files changed, 237 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/org/gradlex/conventions/feature/PluginDefinition.java rename src/main/java/org/gradlex/conventions/feature/{PluginPublishConventionsExtension.java => PublishingConventionsExtension.java} (59%) rename src/main/java/org/gradlex/conventions/feature/{PluginPublishConventionsPlugin.java => PublishingConventionsPlugin.java} (70%) create mode 100644 src/main/java/org/gradlex/conventions/feature/PublishingDefinition.java create mode 100644 src/main/java/org/gradlex/conventions/feature/PublishingMavenCentralDefinition.java create mode 100644 src/main/java/org/gradlex/conventions/feature/PublishingPluginPortalDefinition.java diff --git a/build.gradle.kts b/build.gradle.kts index 3f915a8..56d2585 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,51 +11,75 @@ dependencies { 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("gradlexbuild.build-parameters") + pluginId("org.gradlex.internal.gradlex-build-parameters") bool("ci") { - description.set("Whether or not the build is running in a CI environment") + description = "Whether or not the build is running in a CI environment" fromEnvironment() - defaultValue.set(false) + 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.set("Details about artifact signing") + description = "Details about artifact signing" string("key") { - description.set("The ID of the PGP key to use for signing artifacts") + description = "The ID of the PGP key to use for signing artifacts" fromEnvironment() - defaultValue.set("UNSET") + defaultValue = "UNSET" } string("passphrase") { - description.set("The passphrase for the PGP key specified by signing.key") + description = "The passphrase for the PGP key specified by signing.key" fromEnvironment() - defaultValue.set("UNSET") + defaultValue = "UNSET" } } group("pluginPortal") { // The publish-plugin reads these values directly from System.env. We model them here // for completeness and documentation purposes. - description.set("Credentials for publishing to the plugin portal") + description = "Credentials for publishing to the plugin portal" string("key") { - description.set("The Plugin portal key for publishing the plugin") + description = "The Plugin portal key for publishing the plugin" fromEnvironment("GRADLE_PUBLISH_KEY") } string("secret") { - description.set("The Plugin portal secret for publishing the plugin") + 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.gradlex-build-conventions") diff --git a/src/main/java/org/gradlex/conventions/feature/PluginDefinition.java b/src/main/java/org/gradlex/conventions/feature/PluginDefinition.java deleted file mode 100644 index ba56300..0000000 --- a/src/main/java/org/gradlex/conventions/feature/PluginDefinition.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.plugin.devel.GradlePluginDevelopmentExtension; -import org.gradle.plugin.devel.PluginDeclaration; - -import javax.inject.Inject; -import java.util.Arrays; -import java.util.stream.Collectors; - -public abstract class PluginDefinition { - - protected final PluginDeclaration pluginDefinition; - - @Inject - public PluginDefinition(String name, GradlePluginDevelopmentExtension gradlePlugin) { - this.pluginDefinition = gradlePlugin.getPlugins().create(toCamelCase(name).replace("org.gradlex.", "")); - } - - 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 tags(String... tags) { - pluginDefinition.getTags().set(Arrays.asList(tags)); - } - - 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/PluginPublishConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java similarity index 59% rename from src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsExtension.java rename to src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java index 744244b..3ccbdca 100644 --- a/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsExtension.java +++ b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsExtension.java @@ -17,6 +17,7 @@ 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; @@ -25,32 +26,39 @@ import java.util.ArrayList; import java.util.List; -import java.util.Set; @NullMarked -public abstract class PluginPublishConventionsExtension extends PluginDefinition { +public abstract class PublishingConventionsExtension { - public static final String NAME = "pluginPublishConventions"; + 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 PluginPublishConventionsExtension( + public PublishingConventionsExtension( Project project, GradlePluginDevelopmentExtension gradlePlugin ) { - super(project.getName(), gradlePlugin); this.project = project; this.gradlePlugin = gradlePlugin; } - public void additionalPlugin(String id, Action action) { - PluginDefinition plugin = project.getObjects().newInstance(PluginDefinition.class, id, gradlePlugin); - plugin.id(id); - plugin.pluginDefinition.getTags().set(getTags()); - action.execute(plugin); + 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) { @@ -66,31 +74,21 @@ public void developer(Action action) { developers.add(action); } - public Provider getId() { - return project.getProviders().provider(pluginDefinition::getId); + public Provider getGitHub() { + return gradlePlugin.getVcsUrl(); } - public Provider getImplementationClass() { - return project.getProviders().provider(pluginDefinition::getImplementationClass); + public Provider getWebsite() { + return gradlePlugin.getWebsite(); } public Provider getDisplayName() { - return project.getProviders().provider(pluginDefinition::getDisplayName); + if (definitions.isEmpty()) { throw new GradleException("No publication defined"); } + return definitions.get(0).getDisplayName(); } public Provider getDescription() { - return project.getProviders().provider(pluginDefinition::getDescription); - } - - public Provider> getTags() { - return pluginDefinition.getTags(); - } - - public Provider getGitHub() { - return gradlePlugin.getVcsUrl(); - } - - public Provider getWebsite() { - return gradlePlugin.getWebsite(); + if (definitions.isEmpty()) { throw new GradleException("No publication defined"); } + return definitions.get(0).getDescription(); } } diff --git a/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java similarity index 70% rename from src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java rename to src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java index 5ee1fea..f6ed20d 100644 --- a/src/main/java/org/gradlex/conventions/feature/PluginPublishConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/feature/PublishingConventionsPlugin.java @@ -19,6 +19,9 @@ 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; @@ -32,24 +35,28 @@ import static org.gradle.language.base.plugins.LifecycleBasePlugin.CHECK_TASK_NAME; @NullMarked -public abstract class PluginPublishConventionsPlugin implements Plugin { +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( - PluginPublishConventionsExtension.NAME, PluginPublishConventionsExtension.class, + PublishingConventionsExtension.NAME, PublishingConventionsExtension.class, project, gradlePlugin); tasks.named("publishPlugins", task -> task.dependsOn(CHECK_TASK_NAME)); @@ -57,11 +64,24 @@ public void apply(Project project) { java.withJavadocJar(); java.withSourcesJar(); - signing.useInMemoryPgpKeys( - buildParameters.getSigning().getKey(), - buildParameters.getSigning().getPassphrase() - ); + // 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()); 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/plugin/GradleXPluginConventionsPlugin.java b/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java index dcae135..6988e7f 100644 --- a/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/plugin/GradleXPluginConventionsPlugin.java @@ -24,7 +24,7 @@ import org.gradlex.conventions.feature.AsciidoctorConventionsPlugin; import org.gradlex.conventions.feature.CompileConventionsPlugin; import org.gradlex.conventions.feature.JavadocConventionsPlugin; -import org.gradlex.conventions.feature.PluginPublishConventionsPlugin; +import org.gradlex.conventions.feature.PublishingConventionsPlugin; import org.gradlex.conventions.feature.TestingConventionsPlugin; import org.gradlex.conventions.report.DevelocityConventionsPlugin; import org.jspecify.annotations.NullMarked; @@ -52,7 +52,7 @@ public void apply(Settings settings) { plugins.apply(JavadocConventionsPlugin.class); plugins.apply(TestingConventionsPlugin.class); plugins.apply(AsciidoctorConventionsPlugin.class); - plugins.apply(PluginPublishConventionsPlugin.class); + plugins.apply(PublishingConventionsPlugin.class); plugins.apply(SpotlessConventionsPlugin.class); }); } From 96a9573ff92eca11bc3b509f71f859383bdfd3f5 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Tue, 21 Oct 2025 12:10:45 +0200 Subject: [PATCH 3/7] Exclude generated files from formatting --- .../gradlex/conventions/check/SpotlessConventionsPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java index a14b192..33b133c 100644 --- a/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java @@ -42,10 +42,12 @@ public void apply(Project project) { // format the source code spotless.java(java -> { + java.targetExclude("build/**"); java.palantirJavaFormat(); java.licenseHeader("// SPDX-License-Identifier: Apache-2.0\n", "package|import"); }); spotless.format("javaPackageInfoFiles", java -> { + java.targetExclude("build/**"); // add a separate extension due to https://github.com/diffplug/spotless/issues/532 java.target("src/**/package-info.java"); From 33a60d7fced2c0ee71ba55b8f0264fda2d8a349c Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Fri, 24 Oct 2025 13:42:01 +0200 Subject: [PATCH 4/7] Use a separate test suite for sample tests --- .../feature/TestingConventionsExtension.java | 5 +--- .../feature/TestingConventionsPlugin.java | 30 ++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java index e337fed..deb9782 100644 --- a/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsExtension.java @@ -46,10 +46,7 @@ public void testGradleVersions(String... gradleVersions) { testTask.setGroup(VERIFICATION_GROUP); testTask.setDescription("Runs tests against Gradle" + gradleVersionUnderTest); testTask.systemProperty("gradleVersionUnderTest", gradleVersionUnderTest); - testTask.useJUnitPlatform(junit -> junit.excludeTags( - "no-cross-version", - "org.gradlex.testing.NoCrossVersion" - )); + 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 index 8f608d1..bf7e677 100644 --- a/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/feature/TestingConventionsPlugin.java @@ -20,12 +20,11 @@ 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; -import java.util.Map; - @SuppressWarnings("UnstableApiUsage") @NullMarked public abstract class TestingConventionsPlugin implements Plugin { @@ -42,30 +41,41 @@ public void apply(Project project) { var javaToolchains = extensions.getByType(JavaToolchainService.class); var samplesDir = layout.getProjectDirectory().dir("samples"); - var docsSamplesDir = layout.getProjectDirectory().dir("src/docs/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); + testTask.getInputs() + .dir(samplesDir) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("samples"); } - if (docsSamplesDir.getAsFile().exists()) { - testTask.getInputs().dir(docsSamplesDir); + if (snippetsDir.getAsFile().exists()) { + testTask.getInputs() + .dir(snippetsDir) + .withPathSensitivity(PathSensitivity.RELATIVE) + .withPropertyName("snippets"); } } ) ); suite.dependencies(dependencies-> { - dependencies.getImplementation().add("org.assertj:assertj-core:3.27.6"); dependencies.getImplementation().add("org.gradle.exemplar:samples-check:1.0.3"); dependencies.getImplementation().add("org.junit.vintage:junit-vintage-engine"); }); }); - - extensions.create(TestingConventionsExtension.NAME, TestingConventionsExtension.class, - testSuite.get(), javaToolchains); } } From 9d77628d4dcaf4c4b23a5b03f4ee733256b241d6 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Fri, 24 Oct 2025 13:42:27 +0200 Subject: [PATCH 5/7] Update plugin ID --- build.gradle.kts | 7 ++++++- settings.gradle.kts | 4 ++-- .../conventions/base/LifecycleConventionsPlugin.java | 7 ++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 56d2585..c512fe9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,7 +82,7 @@ buildParameters { } pluginPublishConventions { - id("${project.group}.internal.gradlex-build-conventions") + 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.") @@ -98,4 +98,9 @@ pluginPublishConventions { name = "Jendrik Johannes" email = "jendrik@gradlex.org" } + developer { + id = "ljacomet" + name = "Louis Jacomet" + email = "louis@gradlex.org" + } } \ No newline at end of file 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/LifecycleConventionsPlugin.java b/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java index e4ba368..aae9724 100644 --- a/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/base/LifecycleConventionsPlugin.java @@ -58,7 +58,12 @@ public void apply(Project project) { // cleanup 'build' group var unimportantLifecycleTasks = List.of( - BUILD_DEPENDENTS_TASK_NAME, BUILD_NEEDED_TASK_NAME, CLASSES_TASK_NAME, TEST_CLASSES_TASK_NAME); + 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())) { From 9d26b6bbb531c3817dacedf8338dc546e4654108 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Fri, 24 Oct 2025 18:47:20 +0200 Subject: [PATCH 6/7] Update Readme --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f94a729..225293c 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,19 @@ **settings.gradle.kts** ```kotlin plugins { - id("org.gradlex.internal.gradlex-build-conventions") version "0.7" + id("org.gradlex.internal-build-conventions") version "0.7" } ``` **build.gradle.kts** ```kotlin -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") +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") // ... } From fd6345e0858748152b486b8fdf715a3a0185e83d Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Fri, 24 Oct 2025 19:30:55 +0200 Subject: [PATCH 7/7] Explicitly set line endings to avoid using git at configuration time --- build.gradle.kts | 8 +++++++- .../conventions/check/SpotlessConventionsPlugin.java | 11 +++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c512fe9..fe70968 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,13 @@ plugins { version = "0.7" dependencies { - implementation("com.diffplug.spotless:spotless-plugin-gradle:8.0.0") + 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") diff --git a/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java index 33b133c..8eb5e6d 100644 --- a/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java +++ b/src/main/java/org/gradlex/conventions/check/SpotlessConventionsPlugin.java @@ -18,6 +18,7 @@ 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; @@ -40,21 +41,23 @@ public void apply(Project project) { 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/**"); - // add a separate extension due to https://github.com/diffplug/spotless/issues/532 + 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))); + spotless.kotlinGradle(gradle -> + gradle.ktfmt().kotlinlangStyle().configure(conf -> conf.setMaxWidth(120))); } }