You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/09/22 14:57:06 UTC
(grails-gradle-publish) branch fix/issue36-stale-javadoc-output created (now e54351e)
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a change to branch fix/issue36-stale-javadoc-output
in repository https://gitbox.apache.org/repos/asf/grails-gradle-publish.git
at e54351e Keep a disabled javadoc task's stale output out of the javadoc jar
This branch includes the following new commits:
new e54351e Keep a disabled javadoc task's stale output out of the javadoc jar
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(grails-gradle-publish) 01/01: Keep a disabled javadoc task's stale output out of the javadoc jar
Posted by jd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch fix/issue36-stale-javadoc-output
in repository https://gitbox.apache.org/repos/asf/grails-gradle-publish.git
commit e54351eccfdeceefe4ec69ec1a3ca16516f6387c
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Tue Sep 22 10:56:52 2026 -0400
Keep a disabled javadoc task's stale output out of the javadoc jar
`withJavadocJar()` registers `javadocJar` with `from(javadoc)`, and the plugin
then disables `javadoc` and adds the groovydoc directory to the same jar. The
standard tasks are kept in place deliberately, so that the rest of Gradle can
depend on them, and only the jar's content is meant to be swapped - but the
`from(javadoc)` was never taken away, and a disabled task does not clean its
outputs. Anything an earlier build left in `build/docs/javadoc` was therefore
still packaged next to the groovydoc: stale pages shipped silently, and the
jar failed outright for consumers that set `DuplicatesStrategy.FAIL`.
A javadoc task that does not run has nothing to contribute, so its destination
directory is now excluded from the jar whenever it is disabled. Both the flag
and the directory are read lazily, since the task is disabled after the jar has
been configured. An enabled javadoc is packaged exactly as before.
The javadoc jar is now groovydoc-only in practice as well as in intent, so the
README no longer promises both.
Fixes #36
Fixes #23
---
README.md | 2 +-
.../gradle/publish/GrailsPublishPluginSpec.groovy | 51 +++++++++++++++++
.../stale-javadoc-output/build.gradle | 64 ++++++++++++++++++++++
.../stale-javadoc-output/gradle.properties | 15 +++++
.../stale-javadoc-output/settings.gradle | 20 +++++++
.../groovy/org/grails/example/MyProject.groovy | 27 +++++++++
.../src/main/java/TestJava.java | 11 ++++
.../publish/GrailsPublishGradlePlugin.groovy | 23 +++++++-
8 files changed, 210 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index a61642d..ba19f45 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ limitations under the License.
Apache Grails - Gradle Plugin - Grails Publish
========
-Grails Publish is a Gradle plugin to ease publishing with the maven publish plugin or the nexus publish plugin. Artifacts published by this plugin include sources, the jar file, and a javadoc jar (that contains both the groovydoc & javadoc). This plugin is expected to be used with `snapshot` and `release` builds and assumes `releases` will require signed artifacts.
+Grails Publish is a Gradle plugin to ease publishing with the maven publish plugin or the nexus publish plugin. Artifacts published by this plugin include sources, the jar file, and a javadoc jar (generated from the groovydoc for projects with Groovy sources, and from the javadoc otherwise). This plugin is expected to be used with `snapshot` and `release` builds and assumes `releases` will require signed artifacts.
Limitations
---
diff --git a/plugin/src/functionalTest/groovy/org/apache/grails/gradle/publish/GrailsPublishPluginSpec.groovy b/plugin/src/functionalTest/groovy/org/apache/grails/gradle/publish/GrailsPublishPluginSpec.groovy
index ff1bd90..7bc9275 100644
--- a/plugin/src/functionalTest/groovy/org/apache/grails/gradle/publish/GrailsPublishPluginSpec.groovy
+++ b/plugin/src/functionalTest/groovy/org/apache/grails/gradle/publish/GrailsPublishPluginSpec.groovy
@@ -781,6 +781,13 @@ class GrailsPublishPluginSpec extends GradleSpecification {
}
}
+ String readJarFileEntry(String path, File file) {
+ try (JarFile jarFile = new JarFile(file)) {
+ def entry = jarFile.getEntry(path)
+ return entry == null ? null : jarFile.getInputStream(entry).getText('UTF-8')
+ }
+ }
+
def "source artifact test - java already configured"() {
given:
File tempDir = File.createTempDir("java-already-configured")
@@ -1124,4 +1131,48 @@ class GrailsPublishPluginSpec extends GradleSpecification {
classesJar
findJarFileEntry("org/grails/example/MyProject.class", classesJar)
}
+
+ def "source artifact test - stale javadoc output is kept out of the javadoc jar"() {
+ given:
+ File tempDir = File.createTempDir("stale-javadoc-output")
+ toCleanup << tempDir
+
+ and:
+ GradleRunner runner = setupTestResourceProject('other-artifacts', 'stale-javadoc-output')
+
+ and: 'the disabled javadoc task\'s destination directory still holds output from an earlier build'
+ String staleMarker = 'stale javadoc left behind by an earlier build'
+ File staleJavadocDir = new File(runner.projectDir, 'build/docs/javadoc')
+ new File(staleJavadocDir, 'removed').mkdirs()
+ // groovydoc generates a help-doc.html too, so this one collides
+ new File(staleJavadocDir, 'help-doc.html').text = staleMarker
+ // ... and this is a page for a class that no longer exists, which collides with nothing
+ new File(staleJavadocDir, 'removed/DeletedClass.html').text = staleMarker
+
+ runner = setGradleProperty("projectVersion", "0.0.1-SNAPSHOT", runner)
+ runner = setGradleProperty("mavenPublishUrl", tempDir.toPath().toAbsolutePath().toString(), runner)
+ runner = addEnvironmentVariable("GRAILS_PUBLISH_RELEASE", "false", runner)
+
+ when:
+ def result = executeTask("publish", ["--info"], runner)
+
+ then: 'the jar builds, even though a duplicate entry is a failure for it'
+ assertTaskSuccess("javadocJar", result)
+ assertTaskSuccess("groovydoc", result)
+ assertBuildSuccess(result, ["compileJava", "compileGroovy", "processResources", "classes", "jar", "groovydoc", "javadoc", "javadocJar", "sourcesJar", "grailsPublishValidation", "requireMavenPublishUrl", "generateMetadataFileForMavenPublication", "generatePomFileForMavenPublication", "publishMavenPublicationToMavenLocal", "publishToMavenLocal"])
+
+ and:
+ Path artifactDir = tempDir.toPath().resolve("org/grails/example/stale-javadoc-output/0.0.1-SNAPSHOT")
+ Files.exists(artifactDir)
+ File javadocJar = artifactDir.toFile().listFiles().find { it.name.endsWith("javadoc.jar") }
+ javadocJar
+
+ and: 'the groovydoc is packaged'
+ findJarFileEntry("org/grails/example/MyProject.html", javadocJar)
+ findJarFileEntry("DefaultPackage/TestJava.html", javadocJar)
+
+ and: 'nothing from the stale javadoc directory is'
+ !findJarFileEntry("removed/DeletedClass.html", javadocJar)
+ readJarFileEntry("help-doc.html", javadocJar) != staleMarker
+ }
}
diff --git a/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/build.gradle b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/build.gradle
new file mode 100644
index 0000000..9f4d2eb
--- /dev/null
+++ b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+buildscript {
+ repositories {
+ maven { url "${System.getenv('LOCAL_MAVEN_PATH')}" }
+ maven { url = 'https://repo.grails.org/grails/restricted' }
+ }
+ dependencies {
+ classpath "org.apache.grails.gradle:grails-publish:$grailsGradlePluginVersion"
+ }
+}
+
+allprojects {
+ repositories {
+ maven { url = 'https://repo.grails.org/grails/restricted' }
+ }
+}
+
+version = projectVersion
+group = 'org.grails.example'
+
+apply plugin: 'java-library'
+apply plugin: 'groovy'
+
+dependencies {
+ implementation "org.apache.groovy:groovy:$groovyVersion"
+}
+
+apply plugin: 'org.apache.grails.gradle.grails-publish'
+grailsPublish {
+ githubSlug = 'apache/grails-cores'
+ license {
+ name = 'Apache-2.0'
+ }
+ title = 'Grails Gradle Plugin - Example Project'
+ desc = 'A testing project for the grails gradle plugin'
+ developers = [
+ jdaugherty: 'James Daugherty',
+ ]
+}
+
+// As apache/grails-core does for every jar it builds: a duplicate entry is a build failure,
+// not something to resolve silently. Registered with `configureEach` because the publish plugin
+// registers `javadocJar` in an `afterEvaluate`, so the task does not exist yet at this point.
+tasks.withType(Jar).configureEach {
+ duplicatesStrategy = DuplicatesStrategy.FAIL
+}
diff --git a/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/gradle.properties b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/gradle.properties
new file mode 100644
index 0000000..2290586
--- /dev/null
+++ b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/gradle.properties
@@ -0,0 +1,15 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+projectVersion=0.0.1-SNAPSHOT
\ No newline at end of file
diff --git a/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/settings.gradle b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/settings.gradle
new file mode 100644
index 0000000..09f275c
--- /dev/null
+++ b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/settings.gradle
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+rootProject.name = 'stale-javadoc-output'
diff --git a/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/groovy/org/grails/example/MyProject.groovy b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/groovy/org/grails/example/MyProject.groovy
new file mode 100644
index 0000000..f703d61
--- /dev/null
+++ b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/groovy/org/grails/example/MyProject.groovy
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.grails.example
+
+class MyProject {
+
+ def sayHello() {
+ println('Hello from SubProject2')
+ }
+}
\ No newline at end of file
diff --git a/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/java/TestJava.java b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/java/TestJava.java
new file mode 100644
index 0000000..fe48756
--- /dev/null
+++ b/plugin/src/functionalTest/resources/publish-projects/other-artifacts/stale-javadoc-output/src/main/java/TestJava.java
@@ -0,0 +1,11 @@
+/**
+ * Some example javadoc
+ */
+public class TestJava {
+ /**
+ * An example method
+ */
+ public void sayHelloFromJava() {
+ System.out.println("Hello");
+ }
+}
\ No newline at end of file
diff --git a/plugin/src/main/groovy/org/apache/grails/gradle/publish/GrailsPublishGradlePlugin.groovy b/plugin/src/main/groovy/org/apache/grails/gradle/publish/GrailsPublishGradlePlugin.groovy
index 1ced46f..eb3b088 100644
--- a/plugin/src/main/groovy/org/apache/grails/gradle/publish/GrailsPublishGradlePlugin.groovy
+++ b/plugin/src/main/groovy/org/apache/grails/gradle/publish/GrailsPublishGradlePlugin.groovy
@@ -38,6 +38,7 @@ import org.gradle.api.component.SoftwareComponent
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.FileTreeElement
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.api.plugins.JavaPlatformExtension
@@ -826,16 +827,34 @@ Note: if project properties are used, the properties must be defined prior to ap
}
final TaskContainer tasks = project.tasks
- tasks.named('javadoc').configure {
+ TaskProvider<Javadoc> javadocTask = tasks.named('javadoc', Javadoc)
+ javadocTask.configure { Javadoc javadoc ->
if (tasks.names.contains('groovydoc')) {
project.rootProject.logger.info('Configuring javadocJar task for project {} to include groovydoc', project.name)
- it.enabled = false
+ javadoc.enabled = false
}
}
tasks.named('javadocJar', Jar).configure { Jar jar ->
configureReproducibleJar(jar)
+ // `withJavadocJar()` above wires the `javadoc` task's output into this jar, and that
+ // wiring cannot be removed - which is deliberate, since keeping the standard tasks in
+ // place is what lets the rest of Gradle depend on them. Only the jar's *content* is
+ // swapped for the groovydoc. A disabled `javadoc` never runs, but it never cleans its
+ // output directory either, so whatever an earlier build left in `build/docs/javadoc`
+ // would still be packaged next to the groovydoc - silently shipping stale pages, and
+ // failing the jar outright for consumers that set `DuplicatesStrategy.FAIL`. A javadoc
+ // task that does not run has nothing to contribute, so keep its destination directory
+ // out of the jar whenever it is disabled. Both values are read lazily, because the
+ // task is disabled after this block has configured the jar.
+ Provider<Boolean> javadocRuns = project.provider { javadocTask.get().enabled }
+ Provider<File> javadocDir = project.provider { javadocTask.get().destinationDir }
+ jar.exclude { FileTreeElement element ->
+ !javadocRuns.get() && javadocDir.orNull != null &&
+ element.file.absoluteFile.toPath().startsWith(javadocDir.get().absoluteFile.toPath())
+ }
+
Groovydoc groovyDocTask = tasks.findByName('groovydoc') as Groovydoc
if (groovyDocTask) {
jar.dependsOn(groovyDocTask)