How can a custom gradle plugin determine its own version

Hello,

I need my gradle plugin to determine its own version when applied in the user project so that it can further configure the project with some dependencies accordingly. How can I achieve this in the best way?

More context on the plugin - it’s a supplementary plugin for an internal library and will be released together with the same version, just like spring-boot gradle plugin.

Thanks!

1 Like

Hello,

You can obtain the artifact and its metadata added to the build classpath with the following source code (known to work with Gradle 6.5, both with plugins applied with legacy buildscript and plugins blocks:

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ModuleVersionIdentifier;

public class Foo implements Plugin<Project> {

	@Override
	public void apply(Project project) {
		final Configuration classpath = project.getBuildscript().getConfigurations().getByName("classpath");
		final String version = classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
			.map(artifact -> artifact.getModuleVersion().getId())
			.filter(id -> "org.example".equals(id.getGroup()) && "foo".equals(id.getName()))
			.findAny()
			.map(ModuleVersionIdentifier::getVersion)
			.orElseThrow(() -> new IllegalStateException("foo plugin has been deployed with wrong coordinates: expected group to be 'org.example' and name to be 'foo'"));
		project.getLogger().lifecycle("applied plugin with version [{}]", version);
	}
}
1 Like

You could include a file (eg a property file) inside your plugin’s jar which contains the project version. This file could be generated dynamically as part of your build.

Eg: myplugin/build.gradle

task generateResources {
   ext {
      propFile = file("$buildDir/generated/myplugin.properties")
   } 
   outputs.file propFile 
   doLast {
      mkdir propFile.parentFile
      propFile.text = "version=$project.version" 
   } 
} 
processResources {
   from files(generateResources) 
} 

Then in your plugin

Properties props = new Properties() 
props.load(getClass().getClassloader().getResourceAsStream("myplugin.properties")) 
String version = props.getProperty("version") 
1 Like

Thanks both (@Pierre1 and @Lance) for your suggestions!

I implemented the approach suggested by @Pierre1 first and as he mentioned it worked fine. However, it fails for a multi-module gradle project where the plugin is added in the root projects as apply false, then applied without a version in the subproject. In this case, the plugin artifact isn’t found in the project buildscript classpath. So I tried to search for it in the parent projects recursively as below:

def retrievePluginVersion(Project project) {
    def pluginArtifact = findPluginArtifact(project)

    assert pluginArtifact != null: 'Plugin missing in the buildscript classpath'
    assert pluginArtifact.version != null: 'Plugin version is missing'
    return pluginArtifact.version
  }

  def findPluginArtifact(Project project) {
    def pluginArtifact = project.buildscript.configurations.classpath.resolvedConfiguration.resolvedArtifacts
            .collect { it.moduleVersion.id }
            .find { it.group == 'org.example' && it.name == 'artifact-id' }

    if (pluginArtifact != null || project.parent == null) {
      return pluginArtifact
    }

    return findPluginArtifact(project.parent)
  }

This works fine for the failure scenario described above. However, in this case, gradle complains that the project is trying to resolve artifacts of a different project.

Therefore, I switched to the second approach suggested by @Lance. This approach makes the version inference logic a lot simpler in the plugin source code with a bit of setup in build.gradle. Another important aspect of this approach is that, it’s very friendly with functional tests with gradle-test-kit. Thus, it’s possible to write a functional test simulating the failure scenario and make sure it actually works.

1 Like