Get Gradle Plugins programmatically with Gradle tooling api - gradle

I am using Gradle tooling API to get different insights about Gradle projects such as project tasks, Gradle version, and more.
Part of this analysis requires me to know what plugins were applied (directly and transitively) by the project.
I could not find a way to get Project’s plugins from the tooling API.
Is there a way to do it?

The tooling API is limited in how you can query a build and there's no API to list build plugins, but the tooling API allows you to add your own plugins & models which you can use to query the build.
It's a little complex, but you need to add an init script to the build (eg via init.gradle) to provide your custom model & plugin to query the build.
This is a good repo that demonstrates how to do it: https://github.com/melix/gradle-tapi-demo-artifacts
Here's some code from that repo that demonstrates how to list build artifacts with a custom plugin & model:
ModelBuilder<OutgoingArtifactsModel> customModelBuilder = connection.model(OutgoingArtifactsModel.class);
customModelBuilder.withArguments("--init-script", copyInitScript().getAbsolutePath());
OutgoingArtifactsModel model = customModelBuilder.get();
for (File artifact : model.getArtifacts()) {
System.out.println("artifact = " + artifact);
}
If you want to list plugins you can use the project.getPlugins() API.

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?

How to manually add dependencies to Gradle's MavenPom/MavenPublication?

I am working on a plugin. This plugin gets attached to a project that does not apply the java plugin nor the java-library plugin but which should functionally "look" like a Java project[1]. Which means that it should publish a POM including dependencies. The exact dependencies are known and have been collected in a Configuration.
However, I cannot figure out how to manually attach dependencies to the MavenPublication such that they make it into the published pom (aside from directly editing the pom xml).
MavenPublication shadowMavenPublication = publishingExtension.getPublications().create( "mavenShadowArtifacts", MavenPublication.class );
// `shadowPublishArtifact` is a class defined in the plugin
shadowMavenPublication.artifact(
shadowPublishArtifact.getFile(),
(mavenArtifact) -> {
mavenArtifact.setClassifier( shadowPublishArtifact.getClassifier() );
mavenArtifact.setExtension( shadowPublishArtifact.getExtension() );
}
);
So at this point I have the MavenPublication and added my custom artifact to it. Internally this MavenPublication contains a number of "dependencies" as instances of MavenDependency. E.g. DefaultMavenPublication#runtimeDependencies, DefaultMavenPublication#apiDependencies, ... But those are things defined on internal-only contracts.
Using just public APIs, how can I add dependencies to get added to the pom?
P.S. As a bonus, answer the question on the Gradle forums and get points there too! :D
P.S.S. These dependencies come from another project (hibernate-core) in a multi-project build. The user has configured those dependencies themselves. I just "consume" those dependencies with a series of "dependency substitutions". That "source project" defines some exclusions to its dependencies. How can I access those exclusions do be able to transfer them to the dependencies I am creating for this copy project (hibernate-core-jakarta)?
Thanks!
[1] Its a long back-story, but the gist is that this plugin integrates the JakartaTransformer. The project is completely generated using the transformer. The tasks added by those 2 plugins cause problems.
MavenPublication class has pom property - You need to construct (or provide in Your plugin some API for that purpose) pom with all necessary dependencies. It will be published alongside with artifact.
As far as I know, dependencies are attached to the POM by evaluating the configurations of a software component: MavenPublication.from(SoftwareComponent) (source: DefaultMavenPublication).
The idea would be to provide a customized software component. This is only possible through a custom plugin, according to Creating and publishing custom components.

Custom Gradle plugin - sharing data beetwen plugins

I developed a custom Gradle plugin to read some data from a file but I would like to develop the second one plugin to sending this data to the external server(plugin to sending data should depend on the plugin to reading data). I know that one Gradle plugin can detect the second plugin if there are using in the application. I don't have any idea how to pass or get data(string or dto) from one to another plugin. Is this even possible?
The plugin should provide the reading data logic in a form of a task: readData. Then the second plugin should create a task like sendData.
Once both tasks are created and are working independently of one another, then configure them to depend on one another:
tasks.named("sendData") {
dependsOn(tasks.named("readData")
}
In the above, in order to send data, the reading part needs to happen first.
Reference: https://docs.gradle.org/current/userguide/more_about_tasks.html
You could put all of your DTO's and tasks etc into a "base" plugin. This plugin does not add any tasks to the model, it simply makes them available on the classpath and possibly adds a common extension object to the model
Your "reader" plugin applies the "base" plugin and adds the "reader" tasks to the model
Your "publisher" plugin applies the "base" plugin and adds the "publisher" to tasks the model. These tasks will depend on any "reader" tasks in the model (eg via tasks.withType(...))
Note: Gradle will only ever apply a plugin once to a project. So if "reader" and "publisher" are applied to the same project, the "base" plugin will only be applied once
Gradle uses this pattern internally. Many plugins apply the "base" plugin which adds the clean, check and assemble lifecycle tasks. All the java plugins apply the "java-base" plugin which adds the sourceSets to the model and also applies the "base" plugin

adding metadata to artifactory

The problem:
I need to be able to use artifacts/versions that passed specific version CI of 3 different project.
For instance project A version x.y.z passed the pipeline with B version x2.y2.z2 and when building C I need to be able to get the artifact of A and B that passed a specific versions.
Therefore I was thinking of pushing additional metadata to the stored artifact in artifactory and when building C to query the artifactory with the data.
How can I add custom data to artifact stored in artifactory as per the CI pipeline with Jenkins
You can use Artifactory's REST api or the JFrog CLI to easily set properties on an Artifact or an entire Folder.
You can then query Items/Artifacts based on the properties using REST
or CLI using a spec file
for more advanced querying capabilities you can use Artifactory's AQL
Use Maven Artifactory Plugin
https://www.jfrog.com/confluence/display/RTF/Maven+Artifactory+Plugin
to specify custom metadata for the deployment.

Can I use the gradle connector to get arbitrary configuration information from a build.gradle file?

I'm using the Gradle connector with a Gradle project that is downloaded from a service. I want to do some Gradle operations on that project but need some information from the project.
The downloaded project's build.gradle has some properties that I would like to extract :
group = "value0"
archivesBaseName = "value1"
version = "value2"
If I use
ProjectConnection.getModel(GradleProject.class)
I can get some values from the model but not those ones that I want (perhaps I am using it wrong?). Is there a way to extract those specific values out of the project (perhaps a different model)? I can also just do some text parsing on the build file, but I'd like that to be my last option.
The Gradle tooling API only exposes a subset of the build script information, using its own models. As far as I can tell, the properties that you are interested in are not exposed by default. However, you can expose your own custom model. For an example, see samples/toolingApi/customModel in the full Gradle distribution.

Resources