gradle/github-dependency-graph-gradle-plugin

SimpleDependencyGraphPlugin does not capture plugin dependencies

Closed this issue ยท 11 comments

Following the example in the readme, when executing the following, plugin/buildscript dependencies are not being captured.

gradle -I init.gradle --no-configuration-cache --no-configure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies

It would appear that support for this was added to the GithubDependencyGraphPlugin here.

Why do you believe these dependencies are not being captured? My testing indicates that they are.

Can you please provide a project that allows me to reproduce this issue?

Hi @bigdaz, thanks for responding. I'll put together a reproducer.

@bigdaz here's a repo reproducing what I'm seeing and explaining what I think should be happening https://github.com/brianmcgee/github-dependency-graph-plugin-bug

Thanks for the reproducer. I'm not sure what's happening on your end, but when I run the exact command against that repo I do see the Spotless Gradle plugin listed in the dependencies:

image

You might find it useful to add --scan to your invocation: the generated build scan captures all resolved dependencies and is a good way to share your results.

I updated my reproducer to include the report I'm generating, which matches what you listed above. I also added a second plugin to demonstrate the issue better.

The report above shows an entry for com.diffplug.spotless:spotless-plugin-gradle:6.25.0 which is sourced from https://repo.maven.org.

But it does not include an entry for com.diffplug.spotless:com.diffplug.spotless.gradle.plugin:6.25.0 which is sourced from https://plugins.gradle.org.

Similarly, you will see in my updated example that com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:8.1.1 is not included in the report.

Here is a scan report for the latest run: https://scans.gradle.com/s/iitusscge3aao

The specific dependencies you mention are referred to as plugin marker dependencies. The don't contain any code, and simply help gradle resolve a plugin id to an actual plugin dependency artifact.

These are deliberately and explicitly excluded from the report: #111

Is there a reason that you want to have these included in the report? Or were you just concerned that not all plugin dependencies are reported and weren't sure which ones were missing?

Ah, that makes sense.

Essentially, if you have a dependency of the form <group>:<module>-plugin-gradle:<version> you can reasonably assume the plugin marker to be <group>:<group>.gradle.plugin:<version>.

In answer to why I'm interested in these, I'm using the dependency report from the plugin to construct a lock file for use with Nix that allows me to construct a local maven repository containing all the build dependencies so the build can be run in offline mode within the Nix sandbox.

This was the last piece of the puzzle and it looks like I have it working.

Thanks for your help ๐Ÿ™

Essentially, if you have a dependency of the form :-plugin-gradle: you can reasonably assume the plugin marker to be :.gradle.plugin:.

Unfortunately, I don't think it's quite this straightforward. The plugin marker artifact has:

group = "${plugin-id}"
artifact = "${plugin-id}.gradle.plugin"
version = "${version}"

This can point to a jarfile dependency with arbitrary coordinates. For example you could publish a single jarfile with coordinates com.gradle:plugins:1.0 containing multiple plugins like com.gradle.awesomeplugin and com.gradle.otherplugin.

I don't think there's any way to determine the plugin id given the dependency coordinates.

If it's important to you, I'd be open to a PR that made it possible to retain the plugin marker artifacts via some sort of env var.
Here's where the code is:

private fun traversePluginMarker(rawComponent: ResolvedComponentResult): ResolvedComponentResult? {
if (rawComponent.id is ModuleComponentIdentifier) {
val componentId = rawComponent.id as ModuleComponentIdentifier
if (componentId.module == componentId.group + ".gradle.plugin") {
if (rawComponent.dependencies.isEmpty()) {
return null
}
if (rawComponent.dependencies.size == 1) {
val pluginDep = rawComponent.dependencies.iterator().next() as? ResolvedDependencyResult
return pluginDep?.selected
}
}
}
return rawComponent
}

Any PR would require test coverage.

I think my immediate use case is covered, but I'm happy to look at creating a PR to capture them properly.

I'll try and find some time next week to give it a go.