toolbox
toolbox copied to clipboard
Published pom leaks gradle-api dependency
The generated pom & module files leak the gradle-api jar as a dependency when used in a compileOnly configuration. These should not be exported. This behavior differs from the java-gradle-plugin plugin.
Gradle: 6.9.2 Plugin: 1.6.8
build.gradle using dev.gradleplugins.java-gradle-plugin
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'dev.gradleplugins.java-gradle-plugin:dev.gradleplugins.java-gradle-plugin.gradle.plugin:1.6.8'
}
}
group = 'com.myplugin'
version = '1.0.0'
apply plugin: 'dev.gradleplugins.java-gradle-plugin'
apply plugin: 'java'
apply plugin: 'maven-publish'
gradlePlugin {
plugins {
myPlugin {
id = 'com.myplugin'
implementationClass = 'com.myplugin.MyPlugin'
}
}
compatibility {
minimumGradleVersion = '4.10.3'
}
}
dependencies {
compileOnly gradleApi('4.10.3')
}
pom.xml using dev.gradleplugins.java-gradle-plugin
Observe how gradle-api is exported as a compile dependency. It should not be.
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.myplugin</groupId>
<artifactId>gradle_testplugin</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>dev.gradleplugins</groupId>
<artifactId>gradle-api</artifactId>
<version>4.10.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
build.gradle using java-gradle-plugin
group = 'com.myplugin'
version = '1.0.0'
apply plugin: 'java-gradle-plugin'
apply plugin: 'java'
apply plugin: 'maven-publish'
gradlePlugin {
plugins {
myPlugin {
id = 'com.myplugin'
implementationClass = 'com.myplugin.MyPlugin'
}
}
}
dependencies {
compileOnly gradleApi()
}
pom.xml using java-gradle-plugin
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.myplugin</groupId>
<artifactId>gradle_testplugin</artifactId>
<version>1.0.0</version>
</project>
This seems to be because the plugin adds the gradle-api dependency to the compileOnlyApi when using Gradle 6.7+ (see AddGradleApiDependencyToCompileOnlyApiConfiguration).
The compileOnlyApi configuration exports compile dependencies. The compileOnly configuration does not.
I remember changing to compileOnlyApi
because of the following logic, if someone depends on a plugin and builds against it, it should also use the same-ish Gradle API, given the plugin is highly opinionated to Gradle runtime. However, we could argue that this use case should be strictly confined to "plugin libraries", which is not a concept the Gradle team modelled. In theory, we depend on plugins for their API, which in turn uses Gradle APIs.
However, I think I see the problem. When the plugin is consumed, it will fetch dependencies of the compile
scope from the plugin marker POM because that is how Gradle does it. Instead, we should keep the dependency on the module metadata (so straight consumers are happy) but remove the dependency from the plugin marker as it should be provided
. Does that make sense?
I will add some tests to resolve only using the plugin marker and validate that we are pulling the Gradle API. Then, I will add configuration to the plugin marker.
One thing to note, there is no need to add compileOnly gradleApi(<minimum-gradle-version>)
. The plugin already adds the information under compileOnlyApi
based on the minimumGradleVersion
. You can force a specific API version by using gradleApiVersion
(which is derived by default from minimumGradleVersion
).
Here is a workaround:
publishing {
publications {
pluginMaven(MavenPublication) {
// Workaround for https://github.com/gradle-plugins/toolbox/issues/89
pom.withXml {
def parent = asElement().getElementsByTagName("dependencies").item(0)
def deps = parent.getElementsByTagName("dependency")
def gradleApi = deps.find {
def groupId = it.getElementsByTagName("groupId").item(0).firstChild.nodeValue
def artifactId = it.getElementsByTagName("artifactId").item(0).firstChild.nodeValue
groupId.equals('dev.gradleplugins') && artifactId.equals('gradle-api')
}
parent.removeChild(gradleApi)
}
}
}
}