Programmatically determine Gradle version from within Gradle plugin - gradle

How can I retrieve the version of Gradle itself programmatically from within a Gradle plugin?

Just found out one can get at it using either
getProject().getGradle().getGradleVersion()
Javadoc and DSL reference for the Gradle core type which contains the version. Accordinng to the javadoc getVersion will never return null.
or
Plugin.class.getPackage().getImplementationVersion()
This is plain groovy/java class-based and depends on the classloader and provided Manifest.

This question appeared as the first result when I Googled for it.
There's a class - GradleVersion that'll do the job as well. So
GradleVersion.current();
will return the same as
getProject().getGradle().getGradleVersion()

Related

Load Gradle plugin from custom plugin, dynamically

I am developing a Gradle plugin (https://github.com/hkhc/jarbird), which apply some other plugins in the code according to different scenarios.
I can do that by putting the plugin components as implementation dependencies in my plugin project. Then apply the project with project.apply() method with plugin ID or plugin class object.
However, this means unnecessary downloads of plugin components when I don't need those plugins. So I am finding a way to resolve the plugins dynamically.
I tried to do that by adding the dependency as compileOnly in the build script of my custom plugin, and load it in project.apply() of my plugin.
val artifactoryConfiguration = project.buildscript.configurations.detachedConfiguration(
DefaultExternalModuleDependency(
"org.jfrog.buildinfo",
"build-info-extractor-gradle",
"4.23.4"
)
)
artifactoryConfiguration.resolve()
When I made the coordinate wrong intentionally, I got ModuleVersionNotFoundException. So I am sure the resolve did take place. However, project.apply(ArtifactoryPlugin::class.java) still cause ClassNotFoundException when executing the plugin. It seems Gradle cannot load the plugin from a random detached configuration.
I got stuck at this point, and don't know how to make Gradle load a plugin that I resolve dynamically in my custom plugin.
Do I get the direction wrong or I missed something that makes this approach work?

Opening bean declared in Configuration class

Recently I encountered strange problem with Spring, Kotlin and Spock. I have very simple project (spring-boot, spring-web). I have one Controller with few Beans injected to this Controller. Everything works just fine. Problem is in test. I am not able to mock any of those Beans. kotlin-spring/kotlin-allopen does not add open signature to beans defined in Configuration class. On the other hand if I change this declaration to #Component everything works fine.
Here is my build.gradle.kts plugin listing
plugins {
id("idea")
id("groovy")
id("maven-publish")
id("org.springframework.cloud.contract") version "2.2.5.RELEASE"
id("org.springframework.boot") version "2.4.1"
id("io.spring.dependency-management") version "1.0.10.RELEASE"
kotlin("jvm") version "1.4.21"
kotlin("plugin.spring") version "1.4.21"
kotlin("plugin.allopen") version "1.4.21"
}
This is error message:
Caused by: org.spockframework.mock.CannotCreateMockException: Cannot create mock for class *** because Java mocks cannot mock final classes. If the code under test is written in Groovy, use a Groovy mock.
I know that it says that I can use GroovyMock but I wanted to design my base class in test and I wanted to use #TestConfiguration class. So to mock those classes I wanted to use DetachedMockFactory.
Is there a way to configure Spock to be able to mock final classes from Kotlin? Or is there a way to tell kotlin-spring/kotlin-allopen to open classes defined as beans in Configuration class?
Edit:
My example project is here:
https://github.com/czyzniek/bank/tree/with-spock
I cannot help you fix the spock-mockable problem, maybe you want to ask the author for help. Recently I fixed a Maven issue in that project already, but now it seems like there is a ByteBuddy issue, trying to redefine an already loaded class which is impossible in the JVM. I am sure the maintainer can help you with that.
Meanwhile, like I suggested in a previous comment, switching to Sarek solves the problem, though. I created a corresponding pull request for you.
For now it uses the full Sarek agent, which is the default. If you add sarek-unfinal as a dependency instead of sarek and also make sure that you set the system property dev.sarek.agent.type=unfinal in your Gradle configuration for Spock tests, you can use the smaller unfinal agent without the rest of the Sarek functionality instead. As a Gradle noob I do not know how to configure that, though.
Update: There is no more need for the additional system property mentioned above. The latest Sarek snapshot auto-detects the unfinal agent (or any other of the 4 types of Sarek agents) when it is on the class path. I know you have decided to use spock-mockable, but I want to keep this answer up to date for reference. See this diff for what the pull request would look like now, especially this simple commit.
Please let me know if you have any issues using the snapshot version for now. If this is a commercial project and you need to build a release in which snapshot versions are forbidden at some point, I can publish an 1.0 or maybe a 0.8 or whatever on Maven Central. For now I just added the Maven Central snapshot repository (Sonatype OSS) to your build.
Thanks for help everybody! #kriegaex your solution works like a charm, thanks for that!
Regarding spock-mockable, I talked with author of this library and he figured out why spock-mockable could not work with my setup (GH issue). It was because of spock-spring extension. The root cause is that spock-spring extension was loaded before spock-mockable. When Spring/Spock wanted to create mocks from #TestConfiguration class it couldn't, because kotlin-spring plugin does not open classes declared as beans in a #Configuration class and spock-mockable was not loaded at this time. joke changed the way his extension is loaded, so right now both solutions spock-mockable and sarek work.
I believe that concludes my problem ;)

Can gradle-release-plugin be configured to use SemVer?

I have a Spring-Boot Gradle 4.10.3 project which is currently working fine. It uses gradle-release plugin for releases and version management.
However, I now have a new requirement that the artifacts generated for this project adhere to SemVer conventions. I know there are SemVer Gradle plugins, but I don't want to retool my entire release process if I don't have to. It would be great if gradle-release would let me specify a filename pattern.
The config docs mention a parameter called versionPatterns, which seems like it might be helpful. But I can't figure out how to modify the example given:
versionPatterns = [
/(\d+)([^\d]*$)/: { Matcher m, Project p -> m.replaceAll("${(m0 as int) + 1}${m[0][2]}") }
]
So, what I need is my file name to change from:
AppName-1.2.3-SNAPSHOT.jar
To:
AppName.1.2.3-SNAPSHOT.jar
So really it seems like simply a matter of replacing the first dash with a dot.
Can this be done with gradle-release config? If not, is there an easier way? I would like to continue using gradle-release plugin, because it is already wired in for all of my processes.
I seem to have achieved my goal by configuring the bootJar section.
I'm using an older Gradle version, so I am using some deprecated properties. But adding the following to bootJar seems to be doing the trick. Still interested in other/better ideas.
archiveName = "$baseName.$version.$extension"

How can I access plugin-specific properties of a Gradle project using the Kotlin DSL?

Many Gradle plugins define project properties. For instance, the Base Plugin defines the properties archivesBaseName, distsDirName, and libsDirName.
It is my understanding that using Groovy, I'd simply access them as project.archivesBaseName and so on. But how can I access these properties using the typesafe Kotlin DSL?
Many Gradle plugins define project properties
This isn't entirely true. When you do project.someProperty, Gradle will do an exhaustive lookup as noted here.
Now let's assume a very basic Java project using the Kotlin DSL:
plugins {
java
}
repositories {
jcenter()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.4.2")
}
Applying the java plugin applies the following key plugins:
Applies the JavaBasePlugin here
The JavaBasePlugin applies the Base plugin here
As noted in the docs here:
The Base Plugin only adds conventions related to the creation of archives, such as ZIPs, TARs and JARs
In order words, the Base plugin set defaults for all tasks that are of type AbstractArchiveTask as seen here.
As of Gradle 5.5.1, those subclasses (tasks) are:
So back to your original question:
how can I access these properties using the typesafe Kotlin DSL?
Simply retrieve a reference to the task you are trying to configure or reference:
val jar by tasks.getting(Jar::class)
println(jar.archiveBaseName.get())
val baseConvention = convention.getPlugin(BasePluginConvention::class)
println(baseConvention.libsDirName)
println(baseConvention.distsDirName)
The reason for the extra call for .get() is due to Lazy Configuration.
For any other third party plugin, you would need to either:
Grab a reference to their extension
Grab a reference to the tasks they create

How to determine plugin id for 3rd party gradle plugin library?

For integration test requirement I want to use gradle plugin 'gradle-processes':
https://github.com/johnrengelman/gradle-processes
So I have added version(0.3.0 is only version I see without our organization) in classpath dependency :
dependencies {
classpath 'com.github.jengelman.gradle.plugins:gradle-processes:0.3.0'
}
along with
apply plugin: 'com.github.jengelman.gradle-processes'
But gradle run fails stating that:
Plugin with id 'com.github.jengelman.gradle-processes' not found.
In my local machine repo I can see the jar get downloaded properly, only issue is I am unable to understand what plugin Id I should apply.
Even I went through
https://github.com/johnrengelman/gradle-processes but couldn't get the answer.
Any help to understand how plugin id get determined for a plugin will be really helpful.
Thanks,
Understood the plugin Id concept, so may be useful reference for someone else
https://guides.gradle.org/writing-gradle-plugins/#declare_a_plugin_identifier

Resources