<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gradle Community</title>
    <description>The latest articles on DEV Community by Gradle Community (@gradle-community).</description>
    <link>https://dev.to/gradle-community</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F8896%2Fb4081fd6-a132-46b5-986c-748a6759f4d7.png</url>
      <title>DEV Community: Gradle Community</title>
      <link>https://dev.to/gradle-community</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gradle-community"/>
    <language>en</language>
    <item>
      <title>Centralized POM Configuration Management with Maven Central utility plugins for Gradle</title>
      <dc:creator>Yongjun Hong</dc:creator>
      <pubDate>Fri, 22 Aug 2025 16:06:02 +0000</pubDate>
      <link>https://dev.to/gradle-community/centralized-pom-configuration-management-with-kotlin-pom-gradle-1kap</link>
      <guid>https://dev.to/gradle-community/centralized-pom-configuration-management-with-kotlin-pom-gradle-1kap</guid>
      <description>&lt;p&gt;As part of GSoC, I am developing a &lt;a href="https://github.com/YongGoose/Maven-Central-utility-plugins-for-Gradle" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; that helps developers configure POM more easily. My work mainly focuses on reducing duplicated code and adding validation so that developers can use it more conveniently.&lt;/p&gt;

&lt;p&gt;My ultimate goal is to enable developers to configure POM using my plugin and then publish their work through tools such as &lt;a href="https://github.com/vanniktech/gradle-maven-publish-plugin" rel="noopener noreferrer"&gt;vanniktech/maven-publish&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Hierarchical Configuration Management
&lt;/h3&gt;

&lt;p&gt;In multi-module Gradle projects, managing POM configurations can become a maintenance nightmare if you publish projects to Maven Central or another Maven repository. The primary challenge arises when each module and artifact require their own POM definition - even a single configuration change requires updating POM information across all modules throughout the project.&lt;/p&gt;

&lt;p&gt;To address this issue, in the new &lt;a href="https://plugins.gradle.org/plugin/io.github.yonggoose.maven.central.utility.plugin.setting" rel="noopener noreferrer"&gt;https://plugins.gradle.org/plugin/io.github.yonggoose.maven.central.utility.plugin.setting&lt;/a&gt; and &lt;a href="https://plugins.gradle.org/plugin/io.github.yonggoose.maven.central.utility.plugin.project" rel="noopener noreferrer"&gt;https://plugins.gradle.org/plugin/io.github.yonggoose.maven.central.utility.plugin.project&lt;/a&gt; plugins, I implemented a hierarchical management structure where POM configuration is defined once in the root project and automatically inherited by all submodules. This approach maintains consistency while providing flexibility - submodules can override specific properties when needed, ensuring both structural integrity and adaptability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root Project Configuration
&lt;/h3&gt;

&lt;p&gt;Here's how you can set up centralized POM management in your root project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Project's build.gradle.kts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.yonggoose.maven.central.utility.plugin.project"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"0.1.7"&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"maven-publish"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;rootProjectPom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"io.github.yonggoose"&lt;/span&gt;
    &lt;span class="n"&gt;artifactId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"organization-defaults"&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;

    &lt;span class="nf"&gt;organization&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YongGoose"&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/YongGoose"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;mailingLists&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mailingList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Developers"&lt;/span&gt;
            &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dev-subscribe@example.org"&lt;/span&gt;
            &lt;span class="n"&gt;unsubscribe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dev-unsubscribe@example.org"&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dev@example.org"&lt;/span&gt;
            &lt;span class="n"&gt;archive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://example.org/archive"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this root project configuration, all POM settings are automatically inherited by submodules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying Inherited Configuration in Submodules
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sub Project's build.gradle.kts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"verifyPom"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doLast&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extraProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mergedDefaults"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;GradleException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"there is no merged defaults"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extraProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mergedDefaults"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;OrganizationDefaults&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"io.github.yonggoose"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artifactId&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"organization-defaults"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"YongGoose"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/YongGoose"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Developers"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"dev-subscribe@example.org"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsubscribe&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"dev-unsubscribe@example.org"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"dev@example.org"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mailingLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"https://example.org/archive"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As demonstrated above, submodules can access inherited POM properties through mergedDefaults, confirming that the root project's POM attributes are successfully propagated. Additionally, submodules retain the flexibility to override specific properties when necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration with Vanniktech Maven Publish Plugin
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/vanniktech/gradle-maven-publish-plugin" rel="noopener noreferrer"&gt;Gradle Maven Publish Plugin (vanniktech)&lt;/a&gt; is a popular choice for publishing Android and Kotlin libraries to Maven Central, JCenter, and Nexus repositories. Here's how Maven-Central-utility-plugins-for-Gradle seamlessly integrates with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.github.yonggoose.organizationdefaults.OrganizationDefaults&lt;/span&gt;

&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.yonggoose.maven.central.utility.plugin.project"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"0.1.7"&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.vanniktech.maven.publish"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"0.34.0"&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"maven-publish"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;rootProjectPom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"io.github.yonggoose"&lt;/span&gt;
    &lt;span class="n"&gt;artifactId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"organization-defaults"&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;afterEvaluate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mergedPom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extraProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mergedDefaults"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;OrganizationDefaults&lt;/span&gt;

    &lt;span class="nf"&gt;mavenPublishing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;artifactId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;pom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mergedPom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This integration demonstrates how Maven-Central-utility-plugins-for-Gradle's centralized configuration management works seamlessly with existing publishing workflows, eliminating the need for repetitive POM configuration across multiple modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;p&gt;If you're interested, please check out the links below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://kotlinlang.org/docs/gsoc-2025.html#maven-central-publishing-plugin-for-gradle-with-new-apis-medium-175-hrs" rel="noopener noreferrer"&gt;GSoC maven-central-publishing-plugin-for-gradle-with-new-apis&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repository : &lt;a href="https://github.com/YongGoose/Maven-Central-utility-plugins-for-Gradle" rel="noopener noreferrer"&gt;https://github.com/YongGoose/Maven-Central-utility-plugins-for-Gradle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discussion Channel: maven-central on the &lt;a href="https://slack.gradle.org/" rel="noopener noreferrer"&gt;Gradle Community Slack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo video : &lt;a href="https://drive.google.com/file/d/1McNXyBdIQpEPqTn2ZRjnYJ4E8JNwHMZE/view" rel="noopener noreferrer"&gt;GSoC 2025 - Yongjun - Maven Central Publishing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;This GSoC project was carried out under the guidance of my mentor, &lt;a href="https://github.com/oleg-nenashev" rel="noopener noreferrer"&gt;Oleg&lt;/a&gt;, to whom I am deeply grateful for his support and valuable insights. (Best mentor ever!!)&lt;br&gt;
I would also like to acknowledge other mentors, such as &lt;a href="https://github.com/martinbonnin" rel="noopener noreferrer"&gt;martin&lt;/a&gt;, who have provided helpful feedback along the way!!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>gradle</category>
      <category>gsoc</category>
      <category>mavencentral</category>
    </item>
    <item>
      <title>Gradle Convention Plugin for Developing Jenkins Plugins</title>
      <dc:creator>aaravmahajanofficial</dc:creator>
      <pubDate>Mon, 18 Aug 2025 10:51:34 +0000</pubDate>
      <link>https://dev.to/gradle-community/gradle-convention-plugin-for-developing-jenkins-plugins-4n1d</link>
      <guid>https://dev.to/gradle-community/gradle-convention-plugin-for-developing-jenkins-plugins-4n1d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Building Jenkins plugins with Gradle just got easier. Meet the Jenkins Gradle Convention Plugin → modern, Kotlin-first, convention-based. Plugin development made effortless 🏄‍♂️🏎️&lt;br&gt;
🌀 Try it today on &lt;a href="https://plugins.gradle.org/plugin/io.github.aaravmahajanofficial.jenkins-gradle-convention-plugin" rel="noopener noreferrer"&gt;Gradle Plugin Portal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Today’s Jenkins Plugin Development landscape
&lt;/h3&gt;

&lt;p&gt;Apache Maven remains the primary and officially recommended build tool for Jenkins plugin development, powering the vast majority of plugins in the ecosystem. Its strong integration with the Jenkins plugin parent POM, extensive documentation, and mature lifecycle support make it the safest and default choice for most developers.&lt;/p&gt;

&lt;p&gt;Gradle, while technically capable and offering a modern, flexible build system widely adopted in other Java ecosystems, has seen limited adoption in the Jenkins community. It brings powerful advantages, including extensible build logic via code rather than rigid XML, blazing build speeds thanks to features like &lt;em&gt;incremental builds&lt;/em&gt;, &lt;em&gt;build caching&lt;/em&gt;, and the &lt;em&gt;Gradle Daemon&lt;/em&gt;, a developer-friendly experience with DSLs and interactive tooling. Despite these strengths, Jenkins development continues to be dominated by Maven, largely because the core build infrastructure, tooling ecosystem, and CI pipelines are deeply aligned with Maven conventions.&lt;/p&gt;

&lt;p&gt;The following highlights some key differences between Maven and Gradle in the context of Jenkins plugin development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkkcfyi26nr9vzhx2iey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkkcfyi26nr9vzhx2iey.png" alt="Comparison" width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Origin and motivation
&lt;/h3&gt;

&lt;p&gt;Back in late 2022, the Jenkins community identified major gaps in Gradle support for plugin hosting and automation. New OSS plugins using Gradle were even blocked until hosting requirements were met. This sparked discussions and initiatives to close those gaps and the Jenkins Gradle Convention Plugin is a direct outcome of that effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  But, where the Gradle JPI Plugin falls short?
&lt;/h3&gt;

&lt;p&gt;The Gradle JPI Plugin offers a Maven-free path for Jenkins plugin development, but its limitations prevent it from matching the Maven-based experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key issues:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Non-compliance with Jenkins hosting requirements&lt;/em&gt; → blocks acceptance&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Limited Plugin Compatibility Tester (PCT) support&lt;/em&gt; → weak cross-version testing&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Dependency management &amp;amp; BOM gaps&lt;/em&gt; → unreliable builds&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Outdated Groovy-based codebase&lt;/em&gt; → harder to maintain&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;No convention plugin&lt;/em&gt; → more configuration burden&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Meet the Jenkins Gradle Convention Plugin
&lt;/h3&gt;

&lt;p&gt;A Kotlin-first, Gradle Convention Plugin that works like a Maven Parent POM equivalent for Gradle, providing a unified build flow and opinionated defaults. It extends the well-established &lt;a href="https://github.com/jenkinsci/gradle-jpi-plugin" rel="noopener noreferrer"&gt;gradle-jpi-plugin&lt;/a&gt; and builds on top of it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What about JPI2?&lt;br&gt;
A new “JPI2” variant of the Gradle JPI Plugin was introduced by Rahul and Steve, adding support for Gradle 8+, improved dependency handling, and a modern architecture. The convention plugin is designed to be &lt;em&gt;forward-compatible&lt;/em&gt;, once JPI2 APIs stabilize, we plan to migrate the convention plugin to leverage it for an even better experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Built by me, &lt;a href="https://github.com/aaravmahajanofficial/" rel="noopener noreferrer"&gt;Aarav Mahajan&lt;/a&gt; as part of Google Summer of Code 2025 in collaboration with Gradle, Netflix, and the Kotlin Foundation, under the mentorship of &lt;a href="https://github.com/oleg-nenashev" rel="noopener noreferrer"&gt;Oleg Nenashev&lt;/a&gt;, &lt;a href="https://github.com/sghill" rel="noopener noreferrer"&gt;Steve Hill&lt;/a&gt; and &lt;a href="https://github.com/rahulsom" rel="noopener noreferrer"&gt;Rahul Somasunderam&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  What it brings:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Kotlin-First &amp;amp; Convention-Based&lt;/em&gt; – Minimal/Zero config, maximum productivity.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Jenkins Hosting Compliance&lt;/em&gt; – PCT support, metadata, and structure all handled.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;BOM Integration&lt;/em&gt; – Auto-managed Jenkins Core &amp;amp; plugin BOMs to solve the problem of dependency hell caused due to transitive dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Built-in Quality Gates&lt;/em&gt; – Spotless, SpotBugs, PMD, Detekt, Checkstyle, JaCoCo/Kover, Dokka etc., all pre-wired with Jenkins-aligned defaults.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Future-Proof&lt;/em&gt; – Embraces Gradle 9 best practices like Configuration Cache, Version Catalogs, and caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting started in seconds
&lt;/h3&gt;

&lt;p&gt;Add the plugin in your &lt;code&gt;build.gradle.kts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.aaravmahajanofficial.jenkins-gradle-convention-plugin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"latest-version"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, add this in your &lt;code&gt;settings.gradle.kts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;dependencyResolutionManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;versionCatalogs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"libs"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.aaravmahajanofficial:version-catalog:&amp;lt;latest-version&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it 😉 No messy boilerplate. Now you have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quality tools pre-configured&lt;/li&gt;
&lt;li&gt;BOMs automatically synced&lt;/li&gt;
&lt;li&gt;PCT support&lt;/li&gt;
&lt;li&gt;Compliance checks ready&lt;/li&gt;
&lt;li&gt;A clean, consistent developer experience&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Roadmap
&lt;/h3&gt;

&lt;p&gt;Next steps for plugin delivery and integration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support continuous delivery for Jenkins plugins&lt;/li&gt;
&lt;li&gt;Integrate seamlessly with Jenkins pipelines (e.g., &lt;a href="https://github.com/jenkins-infra/pipeline-library/blob/master/vars/buildPluginWithGradle.groovy" rel="noopener noreferrer"&gt;buildPluginWithGradle&lt;/a&gt; for CI)&lt;/li&gt;
&lt;li&gt;Migrate to JPI2 when new APIs are available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I invite all the Jenkins community developers and, especially, maintainers of Gradle-based plugins, to try it out, provide feedback, and help refine it into a stable toolchain that benefits all. Contributions, real-world testing, and discussions are very welcome :)&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/gsoc-2025.html#gradle-convention-plugin-for-developing-jenkins-plugins-easy-to-hard-90-hrs-to-350-hrs" rel="noopener noreferrer"&gt;Project Idea Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.gradle.org/events/gsoc/2025/jenkins-plugins-toolchain/" rel="noopener noreferrer"&gt;My Project Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.google.com/document/d/1W-_rDWrnHSgV3fGdQWSryOmym15e9TEoHlBvQJysJgw/edit?usp=sharing" rel="noopener noreferrer"&gt;My Proposal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jenkinsci/gradle-convention-plugin" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Official Slack Channel - &lt;a href="https://gradle-community.slack.com/archives/C08S0GKMB5G" rel="noopener noreferrer"&gt;&lt;em&gt;#jenkins-plugin-toolchain&lt;/em&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Concluding with the Elephant 🐘
&lt;/h3&gt;

&lt;p&gt;This plugin isn’t just another build tool tweak—it’s about making Gradle a first-class citizen in Jenkins plugin development. Let’s bring modern productivity engineering to the Jenkins ecosystem. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading! ⭐ If you find this project useful, consider starring the repo or contributing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlpkknvmdwibstik1378.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlpkknvmdwibstik1378.png" alt="Kodee Mascot" width="220" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>kotlin</category>
      <category>jenkins</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Enhanced Kotlin Code Quality Reporting with the Gradle Problems API</title>
      <dc:creator>Vanessa Johnson</dc:creator>
      <pubDate>Mon, 04 Aug 2025 23:58:47 +0000</pubDate>
      <link>https://dev.to/gradle-community/enhanced-kotlin-code-quality-reporting-with-the-gradle-problems-api-56bo</link>
      <guid>https://dev.to/gradle-community/enhanced-kotlin-code-quality-reporting-with-the-gradle-problems-api-56bo</guid>
      <description>&lt;h2&gt;
  
  
  Improving Detekt's Quality Reporting with the Problems API
&lt;/h2&gt;

&lt;p&gt;Static analysis tools like Detekt and Ktlint are popular and invaluable tools that are used for maintaining high code quality in Kotlin projects. An issue that affects a lot of developers is that the reporting of issues found in a project often speak different languages in a sense. You may see warnings in one format, errors in another, and diagnostics somewhere else. This results in developers juggling multiple outputs, CI jobs having to parse different report styles, and IDEs may struggle to present a cohesive view of issues.&lt;/p&gt;

&lt;p&gt;We can unify these outputs via the Gradle Problems API. The Problems API reports rich structured information about problems happening during the build. This information can be used by different user interfaces such as Gradle’s console output, Build Scans, or IDEs to communicate problems to the user in the most appropriate way. &lt;/p&gt;

&lt;p&gt;In my &lt;strong&gt;Google Summer of Code 2025&lt;/strong&gt; project, &lt;strong&gt;Enhanced Kotlin Code Quality Reporting with the Gradle Problems API&lt;/strong&gt;, I built a proof of concept integration into Detekt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing a ProblemsApiConsoleReport
&lt;/h3&gt;

&lt;p&gt;There are a few different types of reports that could have been considered which are the OutputReport and the ConsoleReport. However, since the Problems API surfaces structured problems and we don’t want to duplicate a formatted report, ConsoleReport is the appropriate tool for translating Detekt issues to the Problems API reporter.&lt;/p&gt;

&lt;p&gt;Key behaviors of the custom report:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Injects Gradle’s &lt;code&gt;Problems&lt;/code&gt; service.&lt;/li&gt;
&lt;li&gt;Iterates over &lt;code&gt;Detektion.issues&lt;/code&gt;, creating a &lt;code&gt;ProblemId&lt;/code&gt; for each.&lt;/li&gt;
&lt;li&gt;Populates metadata (file location, line number, severity, details) on the &lt;code&gt;ProblemSpec&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Collects a fallback textual summary for output
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;detektion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProblemGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"validation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"detekt issue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProblemId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ruleInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="n"&gt;reporter&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
               &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
               &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;line&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;
               &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineInFileLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;details&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapSeverity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
               &lt;span class="n"&gt;reportLines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                   &lt;span class="s"&gt;"${issue.location.path}:${issue.location.source.line} [${issue.ruleInstance.id}] ${issue.message}"&lt;/span&gt;
               &lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reportLines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joinToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;separator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineSeparator&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After publishing locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew publishToMavenLocal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;I built the plugin jar and injected it into a temporary project via DslGradleRunner with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew :detekt-report-problems-api:jar 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The jar file was used in the dependencies of the mainBuildFileContent of the DslGradleRunner in the functional test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;mainBuildFileContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
           plugins {
               id("io.gitlab.arturbosch.detekt")
           }
           repositories {
               mavenLocal()
               mavenCentral()
           }
           dependencies {
               detektPlugins(files("${pluginJar.replace('\\', '/')}"))
               detektPlugins(gradleApi())
           }
           detekt {
               allRules = true
               ignoreFailures = false
           }
       """&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimIndent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
       &lt;span class="n"&gt;dryRun&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
   &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setupProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;badClass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;projectFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main/kotlin/BadClass.kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;badClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parentFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="n"&gt;badClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"class badClassName"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test sets up a deliberately bad Kotlin source class badClassName and expects Detekt to fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="n"&gt;gradleRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runTasksAndExpectFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"detekt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
       &lt;span class="nf"&gt;assertThat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The file does not contain a package declaration."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate the Problems API wiring more directly several tests were written, for example this unit test mocks Detektion and captures the ProblemSpec to verify that the correct fields (details, severity, file/line) are populated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="nf"&gt;whenever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detektion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detektion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;assertThat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isEqualTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;specCaptor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argumentCaptor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProblemSpec&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
   &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;problemReporter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;specCaptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ProblemSpec&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;specCaptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;details&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Class name should match the pattern..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GradleSeverity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lineInFileLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fileLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://scans.gradle.com/s/bzc3beuwqrpik" rel="noopener noreferrer"&gt;Problems API Branch Performance Build Scan&lt;/a&gt;&lt;br&gt;
&lt;a href="https://scans.gradle.com/s/wgfdxboo5nose" rel="noopener noreferrer"&gt;Main Branch Build Scan&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Problems API integration does not seem to drastically change overall performance.&lt;/p&gt;

&lt;p&gt;If you want to stay connected about the Problems API, join the Problems API channel in the &lt;a href="https://gradle-community.slack.com/join/shared_invite/zt-3am8016b6-ZtPRYZF_nfyOefUtF8JNPQ#/shared-invite/email" rel="noopener noreferrer"&gt;gradle community slack&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;You can also see the project page &lt;a href="https://community.gradle.org/events/gsoc/2025/kotlin-code-quality-with-problems-api/" rel="noopener noreferrer"&gt;here&lt;/a&gt; which is still in progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Upcoming&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Problems API will be integrated in Ktlint and will be in a future post!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Vanessa Johnson (GSoC 2025 Contributor)&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>opensource</category>
      <category>kotlin</category>
      <category>gsoc</category>
    </item>
    <item>
      <title>Supporting Configuration Cache - my learnings from the Nebula Lint Plugin</title>
      <dc:creator>Nouran</dc:creator>
      <pubDate>Thu, 17 Jul 2025 17:28:19 +0000</pubDate>
      <link>https://dev.to/gradle-community/unlocking-configuration-cache-with-gsoc-contributor-374l</link>
      <guid>https://dev.to/gradle-community/unlocking-configuration-cache-with-gsoc-contributor-374l</guid>
      <description>&lt;h3&gt;
  
  
  Project Context &amp;amp; Links
&lt;/h3&gt;

&lt;p&gt;This post details the work done to add Gradle Configuration Cache compatibility to the &lt;code&gt;gradle-lint-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plugin:&lt;/strong&gt; &lt;a href="https://github.com/nebula-plugins/gradle-lint-plugin" rel="noopener noreferrer"&gt;gradle-lint-plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/nebula-plugins/gradle-lint-plugin/pull/433" rel="noopener noreferrer"&gt;PR #433&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Refactoring &lt;code&gt;LintGradleTask&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Refactoring &lt;code&gt;LintService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Refactoring &lt;code&gt;LintRuleRegistry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Guiding Principles for Other Classes&lt;/li&gt;
&lt;li&gt;Technical Notes&lt;/li&gt;
&lt;li&gt;Strategic Plan for Future Work&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;A hands-on journey into Gradle’s Configuration Cache , lessons learned, obstacles tackled, and contributions made during Google Summer of Code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Welcome! This document details my journey improving Configuration Cache compatibility in Gradle plugins as a Google Summer of Code contributor. My goal is to offer a practical reference for plugin developers, Gradle users, and future GSoC applicants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;To demonstrate how to add this compatibility, we'll use my work on the gradle-lint-plugin as a real-world example of refactoring a plugin that relies heavily on the Project object.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LintGradleTask
&lt;/h2&gt;

&lt;p&gt;The Problem: Accessing project at Execution Time&lt;br&gt;
This is violate the Configuration Cache rules in a few key places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Direct project access in the task action: The line&lt;br&gt;
&lt;br&gt;
&lt;code&gt;new LintService().lint(project, ...)&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
inside the @TaskAction directly used the project object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Passing project to services: The GradleLintPatchAction and GradleLintInfoBrokerAction were created in the constructor with a direct reference to the project object, which they held onto until the task executed.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@TaskAction
    void lint() {
        //TODO: address Invocation of Task.project at execution time has been deprecated.
        DeprecationLogger.whileDisabled {
            def violations = new LintService().lint(project, onlyCriticalRules.get()).violations
                    .unique { v1, v2 -&amp;gt; v1.is(v2) ? 0 : 1 }

            (getListeners() + new GradleLintPatchAction(project) + new GradleLintInfoBrokerAction(project) + consoleOutputAction).each {
                it.lintFinished(violations)
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Solution:
&lt;/h2&gt;

&lt;p&gt;The Configuration Cache works by recording the state of all tasks after the configuration phase and saving it. When you run the build again, Gradle can restore this saved state instead of re-configuring the entire project, leading to significant performance gains. This process fails if a task holds a direct reference to the live Project model, which is not serializable.&lt;/p&gt;

&lt;p&gt;We solved this by creating simple, serializable data containers &lt;code&gt;ProjectInfo and ProjectTree&lt;/code&gt; that act as a "projection" of the data we need from the Project object.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of the key changes and the "why" behind them:&lt;/p&gt;

&lt;p&gt;1) Create Serializable Data Containers (ProjectInfo &amp;amp; ProjectTree) &lt;br&gt;
The first step was to define classes that could hold the project data we needed, but in a simple, serializable way.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ProjectInfo&lt;/code&gt;: It stores primitive data like name, path, File objects, and serializable collections like Map. It safely carry information from the configuration phase to the execution phase.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ProjectTree&lt;/code&gt;: This class holds a list of ProjectInfo objects, representing the entire project structure needed for the linting process. It is also serializable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: If you need data from the Project object during execution, create a dedicated, Serializable class to hold that data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * A CC-compatible projection of project data.
 */
class ProjectInfo implements Serializable{
    String name
    String path
    File rootDir
    File buildFile
    File projectDir
    File buildDirectory
    GradleLintExtension extension
    Map&amp;lt;String, Object&amp;gt; properties
    Supplier&amp;lt;Project&amp;gt; projectSupplier

    static ProjectInfo from(Task task, Project subproject) {
        String subprojectPath = subproject.path
        return build(subproject, { task.project.project(subprojectPath) })
    }

    static ProjectInfo from(Task task) {
        return build(task.project, task::getProject)
    }

    @VisibleForTesting
    private static ProjectInfo build(Project project, Supplier&amp;lt;Project&amp;gt; projectSupplier) {
        GradleLintExtension extension =
                project.extensions.findByType(GradleLintExtension) ?:
                project.rootProject.extensions.findByType(GradleLintExtension)
        Map&amp;lt;String, Object&amp;gt; properties = [:]
        if (project.hasProperty('gradleLint.rules')) {
            properties['gradleLint.rules'] = project.property('gradleLint.rules')
        }
        if (project.hasProperty('gradleLint.excludedRules')) {
            properties['gradleLint.excludedRules'] = project.property('gradleLint.excludedRules')
        }

        return new ProjectInfo(
                name:project.name,
                path:project.path,
                rootDir:project.rootDir,
                buildFile: project.buildFile,
                projectDir:project.projectDir,
                extension: extension,
                properties: properties,
                projectSupplier: projectSupplier,
                buildDirectory : project.buildDir
        )

    }
}

class ProjectTree{
    List&amp;lt;ProjectInfo&amp;gt; allProjects

    ProjectTree(List&amp;lt;ProjectInfo&amp;gt; allProjects){
        this.allProjects = allProjects
    }

    /**
     * Returns the base project this tree was built from.
     */
    ProjectInfo getBaseProject() {
        return allProjects.head()
    }

    /**
     * Build a project tree based on the given task's project.
     *
     * @return a project tree reflecting information and the structure of the given task's project
     */
    static from(Task task) {
        def baseProject = task.project
        List&amp;lt;ProjectInfo&amp;gt; projectInfos = [ProjectInfo.from(task)] + baseProject.subprojects.collect { Project p -&amp;gt; ProjectInfo.from(task, p) }
        return new ProjectTree(projectInfos)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Capture Project Data During the Configuration Phase
The task constructor is part of the configuration phase, so it's the perfect place to safely access the project object and extract our data. We use Gradle's Provider API to do this lazily.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LintGradleTask() {
        failOnWarning.convention(false)
        onlyCriticalRules.convention(false)
        projectTree.set(project.provider {ProjectTree.from(this) })
        projectInfo.convention(projectTree.map(ProjectTree::getBaseProject))
        projectRootDir.set(project.rootDir)
        infoBrokerAction = new GradleLintInfoBrokerAction(this)
        patchAction = new GradleLintPatchAction(getProjectInfo().get())
        group = 'lint'
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;&lt;code&gt;project.provider { ... }&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;: This creates a Provider. The code inside the closure is executed by Gradle during the configuration phase. It accesses the project and creates our serializable ProjectTree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.set(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;: We set this Provider as the value for our projectTree property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.map(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;: We then create a derived provider for the projectInfo by transforming the result of the projectTree provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: Use the Provider API inside your task's constructor to capture and transform project data without breaking task-configuration avoidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  LintService
&lt;/h2&gt;

&lt;p&gt;We transform LintService from a class deeply integrated with Gradle's live project model into a stateless service. The new design makes it compatible with the Configuration Cache by ensuring it operates exclusively on simple, serializable data during the task's execution phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: A service that is associated with the Project instance
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/Nouran-11/gradle-lint-plugin/blob/main/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy" rel="noopener noreferrer"&gt;original LintService&lt;/a&gt; was not compatible with the Configuration Cache because its methods required a live Project object as a parameter.&lt;/p&gt;

&lt;p&gt;Methods like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lint(Project project, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruleSetForProject(Project p, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;directly manipulated Project objects.&lt;/p&gt;

&lt;p&gt;It would read configuration dynamically by calling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p.extensions.getByType(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p.property(...)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design meant the service could only function when connected to the live Gradle build model, making it impossible for Gradle to serialize the task that uses it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: A stateless service using pre-existing configuration data from the Project object
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Nouran-11/gradle-lint-plugin/blob/fixing-cc-nebula/src/main/groovy/com/netflix/nebula/lint/plugin/LintService.groovy" rel="noopener noreferrer"&gt;Updated LintService Full Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The refactored LintService is now completely decoupled from the Project object at execution time. It operates like a pure function: it receives all the data it needs as input (ProjectTree and ProjectInfo) and produces a result.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accepting Data Projections Instead of Project Objects
The most significant change is in the method signatures.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lint(Project project, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lint(ProjectTree projectTree, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service no longer receives the Project object. Instead, it gets the ProjectTree ,our serializable snapshot of the entire project structure. All subsequent operations, like iterating over subprojects, are done using this simple data object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(projectTree.allProjects.each { ... })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Reading Configuration from Snapshots
The service now gets all its configuration from the ProjectInfo data transfer object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before: It actively queried the project for properties and extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p.hasProperty(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p.extensions.getByType(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After: It passively reads pre-extracted data from the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projectInfo.properties[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projectInfo.extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the service doesn't need to communicate with the live build model. All the decisions were already made during the configuration phase, and the results were stored in ProjectInfo.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Isolating Operations That Require the Live Model
The new code cleverly handles rules that must access the live project model &lt;strong&gt;ModelAwareGradleLintRule&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It first checks if any such rule is present&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(if (containsModelAwareRule))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only if it's true does it use the&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;supplier (Project project = p.projectSupplier.get())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to re-acquire the Project object &lt;strong&gt;when it really needed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is an advanced pattern that minimizes the impact on caching. The non-cache-friendly code path is isolated and only executed when absolutely necessary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: Services called by a cacheable task must also be cache-friendly. They should be designed to be stateless, receiving all necessary information through their method arguments and avoiding any direct interaction with the Project object during the execution phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  LintRuleRegistry
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Problem: Directly Injecting the Project Object
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/Nouran-11/gradle-lint-plugin/blob/main/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy" rel="noopener noreferrer"&gt;original LintRuleRegistery&lt;/a&gt; created a direct and hard dependency on the Project object, which is a blocker for the Configuration Cache.&lt;/p&gt;

&lt;p&gt;The buildRules method took a Project as a parameter and, for certain rules, assigned it directly to a field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(r as ModelAwareGradleLintRule).project = project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This meant that any ModelAwareGradleLintRule instance created by the registry held a reference to the non-serializable Project object, making it impossible for Gradle to cache any task that used this registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Lazily Access Project with a Supplier when it needed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Nouran-11/gradle-lint-plugin/blob/fixing-cc-nebula/src/main/groovy/com/netflix/nebula/lint/plugin/LintRuleRegistry.groovy" rel="noopener noreferrer"&gt;Updated LintRuleRegistery &lt;/a&gt;&lt;br&gt;
The solution was to stop passing the Project object itself and instead pass a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Supplier&amp;lt;Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a lightweight, serializable object that knows how to get the Project object later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Injecting the Supplier, Not the Project&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
A new, primary buildRules method was introduced with a new signature.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;buildRules(String ruleId, Project project, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; buildRules(String ruleId, Supplier&amp;lt;Project&amp;gt; projectSupplier, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical line inside was changed to inject this new supplier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(r as ModelAwareGradleLintRule).projectSupplier = projectSupplier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;This is the core of the fix. Instead of giving the rule the Project object directly, we are now giving it a "recipe" to get the project if and when it actually needs it. The Supplier is serializable, so the entire process becomes cache-friendly.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Learning :&lt;/em&gt;&lt;/strong&gt; Object creation logic is a common source of Configuration Cache issues. When an object you are creating needs access to the Project model, inject a Supplier instead of the Project object itself. This defers the access and allows the object and its creation process to be serializable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guiding Principle for the Remaining Classes
&lt;/h2&gt;

&lt;p&gt;The rest of the refactoring follows a single, consistent rule: Any class used during the task's execution phase must be decoupled from the Project object.&lt;/p&gt;

&lt;p&gt;This is achieved by applying one of two patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Operating on Data (ProjectInfo): If a class needs to read data from the project, its methods are changed to accept a ProjectInfo or ProjectTree object as a parameter. All internal logic is then updated to read from this serializable data instead of the live Project object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deferring Access (Supplier): If a class absolutely must interact with the live project model (the "escape hatch" scenario), it's given a Supplier. This allows it to re-acquire the Project object on-demand, a process that is compatible with the Configuration Cache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Nouran-11/gradle-lint-plugin" rel="noopener noreferrer"&gt; Original Source Code (Before Changes): &lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Nouran-11/gradle-lint-plugin" rel="noopener noreferrer"&gt;Updated Source Code (After CC Compatibility Changes): &lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Notes
&lt;/h2&gt;

&lt;h2&gt;
  
  
  findByType() vs. getByType()
&lt;/h2&gt;

&lt;p&gt;When working with Gradle extensions, there are two common methods to retrieve an extension by its class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;findByType()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getByType()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key difference lies in how they behave when an extension is not found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getByType(Class&amp;lt;T&amp;gt; type)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Returns&lt;/em&gt;&lt;/strong&gt;: The extension instance if it exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Throws&lt;/em&gt;&lt;/strong&gt;: An UnknownDomainObjectException if the extension does not exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use this when you consider the extension's presence mandatory for your plugin's logic to proceed.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;findByType(Class&amp;lt;T&amp;gt; type)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Returns :&lt;/em&gt;&lt;/strong&gt; The extension instance if it exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Returns :&lt;/em&gt;&lt;/strong&gt; null if the extension does not exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use this when the extension is optional, and your code needs to safely handle cases where it might be missing.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on Gradle TestKit
&lt;/h2&gt;

&lt;p&gt;For those new to testing Gradle plugins, the Gradle TestKit is an essential tool. It allows you to run real builds as part of your tests, giving you confidence that your plugin behaves correctly for end-users.&lt;/p&gt;

&lt;p&gt;I started by learning from the  &lt;a href="https://docs.gradle.org/current/userguide/test_kit.html" rel="noopener noreferrer"&gt;official TestKit documentation &lt;/a&gt; and by studying the extensive examples in the nebula-lint-plugin's existing test suite. I also found the talk &lt;a href="https://www.youtube.com/watch?v=P7SvwkRXjSU" rel="noopener noreferrer"&gt;"Testing the Build with Testkit"&lt;/a&gt; to be very helpful for understanding the different types of tests.&lt;/p&gt;

&lt;p&gt;Here are some key takeaways:&lt;/p&gt;

&lt;p&gt;Types of Tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Unit Tests:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Focus on a single class in isolation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The class under test does not use the Gradle API.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Integration Tests:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The class(es) under test use Gradle APIs, often interacting with a Project instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;These do not execute a full build but test the integration with Gradle's model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Functional Tests (The focus of TestKit):&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Executes a build script from an end-user's perspective.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Examines the build outcome, output, and any created files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Runs in a fully isolated test environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Structure of a TestKit Test&lt;br&gt;
Most functional tests follow a clear "Given-When-Then" structure, which makes them easy to read and understand:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;given block:&lt;/em&gt;&lt;/strong&gt; Sets up the test environment. This is where you create a temporary project directory and write the build.gradle or other files needed for the test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;when block:&lt;/em&gt;&lt;/strong&gt; Executes a Gradle task using the GradleRunner. This is where you run the build (e.g., ./gradlew lintGradle).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;then block:&lt;/em&gt;&lt;/strong&gt; Verifies the outcome. Here, you use assertions to check if the build succeeded or failed, inspect the build output, or verify that files were created or modified as expected.&lt;/p&gt;


&lt;h2&gt;
  
  
  Strategic Plan for Future Work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The following notes serve as a technical blueprint for completing the Configuration Cache compatibility work in the Nebula Lint plugin or for tackling similar challenges in other Gradle plugins. This strategic plan breaks down the complex problem into manageable steps.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1. Understanding the Challenge&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
What the Plugin Does: The plugin works by building an Abstract Syntax Tree (AST) of a build script and then traversing it with a set of rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The Core Issue:&lt;/em&gt;&lt;/strong&gt; Many of these rules rely on querying Project.configurations at runtime to analyze dependency setups. This dynamic querying is precisely what breaks the Gradle Configuration Cache, which forbids accessing the live Project model during the execution phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2. Defining the Goal&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
The objective is to refactor the plugin so that rules operate on pre-computed, serializable data instead of the live Project object.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Separate Configuration Queries: Decouple rules from Project.configurations by having them work with a "data projection"—a snapshot of the configuration data taken at the right time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable Configuration-Time Setup: Move all data gathering to Gradle's configuration phase, avoiding runtime queries entirely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactor Incrementally: Start with simpler, purely syntactical rules (like SpaceAssignmentRule and DependencyParenthesesRule) to build momentum and prove the approach.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3. The Step-by-Step Plan&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identify Configuration Usage: First, examine each rule to understand how it interacts with Project.configurations. Does it read dependencies? Resolve configurations? Check attributes?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract Configuration Data: Create a simple data object (e.g., ConfigurationInfo) to hold only the necessary information. This data should be computed once during the configuration phase using Gradle's Provider API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplify Rule Dependencies: Refactor the rules to depend on the new ConfigurationInfo data object instead of accessing Project.configurations directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify the Data Flow: Enhance the main ProjectInfo data object to include this new ConfigurationInfo structure, making it available to all rules that need it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test Incrementally: After refactoring each rule, test it thoroughly with various build scripts to ensure its original functionality remains intact and that it no longer violates Configuration Cache rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following this iterative plan, anyone can progressively improve a plugin’s compatibility with the Gradle Configuration Cache while ensuring it remains stable and functional.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Heartfelt Thank You
&lt;/h2&gt;

&lt;p&gt;A sincere thank you to the Google Summer of Code program for this experience. I am especially grateful to my mentors for their exceptional support and guidance throughout the project. &lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;&lt;em&gt;Written by Nouran Atef, GSoC 2025 Contributor &lt;/em&gt;&lt;/strong&gt;
&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>gsoc</category>
      <category>nebula</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Announcing Dependency Analysis Gradle Plugin 2.0.0!</title>
      <dc:creator>Tony Robalik</dc:creator>
      <pubDate>Tue, 27 Aug 2024 21:24:18 +0000</pubDate>
      <link>https://dev.to/gradle-community/announcing-dependency-analysis-gradle-plugin-200-426c</link>
      <guid>https://dev.to/gradle-community/announcing-dependency-analysis-gradle-plugin-200-426c</guid>
      <description>&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@honeypoppet?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Sandie Clarke&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/person-holding-brown-and-black-frog-q13Zq1Jufks?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Dependency Analysis Plugin v2.0.0 has just been released!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 2.0.0?
&lt;/h2&gt;

&lt;p&gt;Breaking changes, of course. The ABI has been updated, which is relevant for anyone accessing the analysis results programmatically. There has also been an important behavioral change, which will be relevant to all users.&lt;/p&gt;

&lt;h3&gt;
  
  
  ABI changes
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PluginAdvice&lt;/code&gt; class has been moved from the &lt;code&gt;com.autonomousapps.advice&lt;/code&gt; package to the &lt;code&gt;com.autonomousapps.model&lt;/code&gt; package, where all the other public model classes live.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;FindInlineMembersTask&lt;/code&gt; has been renamed to &lt;code&gt;FindKotlinMagicTask&lt;/code&gt;, to better reflect what it actually does. This is unlikely to impact any users, but since it has public visibility, it might.&lt;/p&gt;

&lt;p&gt;Finally, the deprecated &lt;code&gt;ignoreKtx()&lt;/code&gt; option has been removed from the &lt;code&gt;issues&lt;/code&gt; block. It can now only be configured from the &lt;code&gt;structure&lt;/code&gt; block.&lt;/p&gt;

&lt;h3&gt;
  
  
  Behavioral changes
&lt;/h3&gt;

&lt;p&gt;The plugin no longer automatically applies itself to every subproject in your build. Therefore the &lt;code&gt;dependency.analysis.autoapply&lt;/code&gt; flag is now a no-op, and using it will either emit a warning (when the value is set to false) or fail your build (if the value is set to true, which was the old default). Users now have two choices if they want to analyze subprojects in their build:&lt;/p&gt;

&lt;p&gt;First, you can apply the plugin directly to every module you want to analyze. This has long been the best practice for how to use this plugin.&lt;/p&gt;

&lt;p&gt;Second, you can use the new &lt;code&gt;com.autonomousapps.build-health&lt;/code&gt; plugin! Apply it to your settings.gradle[.kts] like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// settings.gradle[.kts]&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.autonomousapps.build-health"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"2.0.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// And you can configure it from the settings&lt;/span&gt;
&lt;span class="c1"&gt;// script, too!&lt;/span&gt;
&lt;span class="nf"&gt;dependencyAnalysis&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new plugin is only available from Gradle 8.8. See &lt;a href="https://github.com/autonomousapps/dependency-analysis-gradle-plugin/wiki/Adding-to-your-project#via-the-build-health-settings-plugin" rel="noopener noreferrer"&gt;the wiki&lt;/a&gt; for more information, &lt;em&gt;especially&lt;/em&gt; if you also use Android or Kotlin.&lt;/p&gt;

&lt;p&gt;In order to assist users who might update to 2.0.0 without reading the release notes, the plugin has been updated to attempt to detect the case where the plugin has been applied only to the root project but not to any subproject; this would almost certainly be a user error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy building!
&lt;/h2&gt;

&lt;p&gt;Thanks for being a user, and please keep &lt;a href="https://github.com/autonomousapps/dependency-analysis-gradle-plugin/issues" rel="noopener noreferrer"&gt;filing issues&lt;/a&gt; if you run into any bugs.&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>android</category>
      <category>jvm</category>
    </item>
    <item>
      <title>Kradle 9.0: Revolutionizing the JVM Ecosystem with Kotlin at its Core!</title>
      <dc:creator>Oleg Nenashev</dc:creator>
      <pubDate>Mon, 01 Apr 2024 16:03:26 +0000</pubDate>
      <link>https://dev.to/gradle-community/kradle-90-revolutionizing-the-jvm-ecosystem-with-kotlin-at-its-core-4l44</link>
      <guid>https://dev.to/gradle-community/kradle-90-revolutionizing-the-jvm-ecosystem-with-kotlin-at-its-core-4l44</guid>
      <description>&lt;p&gt;We're thrilled to unveil &lt;strong&gt;Kradle 9.0&lt;/strong&gt;, a groundbreaking release that marks a significant leap forward in the Java ecosystem. With Kradle 9.0, we're embracing Kotlin as the default language for build definitions, starting a new era of simplicity, efficiency, and developer empowerment.&lt;/p&gt;

&lt;p&gt;Hence, the &lt;strong&gt;Gradle Build Tool&lt;/strong&gt; now becomes the &lt;strong&gt;Kradle Build Tool&lt;/strong&gt; and gets a new logo!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfpokq8rqrjr0u1wqg0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfpokq8rqrjr0u1wqg0z.png" alt="Kradle Build Tool 9.0" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Highlights
&lt;/h2&gt;

&lt;p&gt;🌟 &lt;strong&gt;Kotlin as Default Language&lt;/strong&gt;: Say goodbye to verbosity and complexity! Kotlin is now front and center for defining builds in the JVM ecosystem. In Gradle 8.2 &lt;a href="https://blog.gradle.org/kotlin-dsl-is-now-the-default-for-new-gradle-builds" rel="noopener noreferrer"&gt;we made it&lt;/a&gt; the the default and recommended choice for new build definitions. In Kradle 9.0, the Kotlin DSL Plugin is now a built-in part of the core distribution, while Gradle support is detached to a plugin, which we will keep maintaining. Now, developers can leverage the elegance and expressiveness of Kotlin to streamline their build processes effortlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbwnsrl2jhg5bzmzp77r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbwnsrl2jhg5bzmzp77r.png" alt="Kotlin in the Gradle ecosystem" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Enhanced Developer Experience&lt;/strong&gt;: We've doubled down on developer experience, prioritizing ease of use and seamless integration by projects like &lt;a href="https://github.com/gradle/declarative-gradle" rel="noopener noreferrer"&gt;Declarative Gradle&lt;/a&gt;. With Kradle 9.0, you'll enjoy a more intuitive and enjoyable development journey, thanks to Kotlin's concise syntax, powerful features, and great DSL customization capabilities through idiomatic definitions.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Developer Productivity Engineering&lt;/strong&gt;: At Gradle, we understand the importance of developer productivity. That's why we've engineered Kradle 9.0 to supercharge your productivity like never before. Whether building small projects or large-scale applications, Kradle 9.0 empowers you to accomplish more in less time with the help of the new Krataouille framework that allows you to implement build definitions in a wholly asynchronous and functional way based on Kotlin coroutines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94isji0r6utx148g0xzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94isji0r6utx148g0xzo.png" alt="Krataouille Example" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔧 &lt;strong&gt;Rewritten in Kotlin&lt;/strong&gt;: As part of our commitment to embracing Kotlin, we've completely rewritten Gradle Build Tool itself in Kotlin. This monumental effort ensures seamless compatibility, improved performance, and a more cohesive development experience across the board. We target full support for the K2 compiler soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;And let’s talk about the elephant in the room! As one of the insiders shared, future releases of the Kradle are rumored to be based on Starlark (formerly known as Skylark), the language used in Bazel. &lt;/p&gt;

&lt;p&gt;Code named &lt;strong&gt;Gradlark&lt;/strong&gt;, this new version will be another step towards the long-awaited complete Python implementation of the Kradle Build Tool, and it should attract more Python developers and monorepo fans.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkxkql87q6lzlzsopfxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkxkql87q6lzlzsopfxm.png" alt="Gradlark - Evolution Plan" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started!
&lt;/h2&gt;

&lt;p&gt;Whether you're a seasoned veteran or just getting started, Kradle 9.0 is your gateway to a brighter, more efficient future in the JVM ecosystem. Join the Future of JVM Development with Kradle 9.0!&lt;/p&gt;

&lt;p&gt;Upgrade today and experience the next evolution in build automation and developer productivity engineering. And join the &lt;a href="https://gradle.org/slack-invite" rel="noopener noreferrer"&gt;Gradle Community Slack&lt;/a&gt; to follow more community news and Gradle memes!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn8u5fx0cnnpdqz57etb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn8u5fx0cnnpdqz57etb.png" alt="The Gradlemperor announcing Kradle 9.0" width="501" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>kotlin</category>
      <category>java</category>
      <category>dpe</category>
    </item>
  </channel>
</rss>
