Dependency Analysis Gradle Plugin

repository·main·Indexed 24 days ago

https://github.com/autonomousapps/dependency-analysis-gradle-plugin

A Gradle plugin for analyzing project dependencies, providing tools to identify unused or misdeclared dependencies. It includes tasks such as `buildHealth` for global analysis, `projectHealth` for module-specific checks, `fixDependencies` for automatic remediation, and `reason` for dependency advice. The plugin also offers specialized tasks for resolving external dependencies, printing dominator trees, and generating project graphs.

Tokens
7K
Snippets
19
Records
27
Agent score
76%

What's inside Dependency Analysis Gradle Plugin

  1. Understanding the purpose of the kotlin-editor-relocated module

    main
    The kotlin-editor-relocated module is a specialized shadow module used to shade the KotlinEditor project. Its primary purpose is to prevent a non-shaded version of antlr from entering the Dependency Analysis Gradle Plugin (DAGP) runtime. This prevents common and annoying classpath conflicts associated with antlr. By treating this as a separate, stable artifact, the plugin maintains a smaller final JAR size, which optimizes download speeds in CI environments.
  2. How the plugin avoids GradleRunner.withPluginClasspath()

    main

    The plugin provides a workaround for the broken GradleRunner.withPluginClasspath() method.

    Instead of using the standard method, the plugin:

    1. Installs your plugin and all its local project dependencies into a local file repository located at $rootDir/build/functionalTestRepo/.
    2. Passes a reference to this location into your functional test JVM via the system property com.autonomousapps.plugin-under-test.repo.
    3. Makes this repository available to your test suite via Repository.FUNC_TEST.
  3. How variant-artifacts works: Producer and Consumer model

    main

    The variant-artifacts library simplifies sharing non-standard artifacts (like code coverage data or metadata) between Gradle projects. It uses a contract-based approach to avoid cross-project mutation and ensure compatibility with Gradle's isolated projects.

    There are three core components in this model:

    1. ArtifactDescription: Defines the contract between the producer and the consumer. It specifies the Attribute used for matching and a categoryName.
    2. The Producer: Registers a task that produces an output and uses an interProjectPublisher to publish that task's output as an artifact.
    3. The Consumer (Aggregator): Uses an interProjectResolver to find and collect artifacts from other projects. It can then aggregate these files into a single task.

    To link projects safely, the consumer should declare dependencies on the producer projects using the configuration name derived from the ArtifactDescription's Kind enum.

  4. Generate and Publish a GPG Key

    main

    To sign your plugin artifacts, you must generate a GPG key and publish the public portion to a keyserver so Maven Central can verify your signatures.

    1. Generate a key: Use GPG (e.g., via GitHub's guide).
    2. Find your Key ID: Run gpg -K. The Key ID is the last 8 characters of the 40-character fingerprint line.
    3. Publish to a keyserver:
      gpg --send-keys --keyserver keyserver.ubuntu.com <YOUR_KEY_ID>
    4. Verify publication:
      gpg --recv-keys --keyserver keyserver.ubuntu.com <YOUR_KEY_ID>
    $ gpg -K
        /Users/trobalik/.gnupg/pubring.kbx
        ----------------------------------
        sec   rsa4096 2020-01-01 [SC]
              W7WQ5NZWC8S339RVAAOCR0SCMV7T00FKDHG570SZ
        uid           [ultimate] Tony Robalik <you@email.com>
        ssb   rsa4096 2020-01-01 [E]
    
    # Example Key ID: DHG570SZ
    $ gpg --send-keys --keyserver keyserver.ubuntu.com DHG570SZ
  5. Publish Snapshots and Test Consumption

    main

    Before releasing, publish a snapshot version to verify the plugin works in a real project.

    1. Set version to snapshot: version = "0.1.0-SNAPSHOT".
    2. Run publishing tasks:
      ./gradlew publishMyPluginPluginMarkerMavenPublicationToSonatypeRepository publishPluginPublicationToSonatypeRepository
    3. Consume in a test project: In the consumer's settings.gradle (or settings.gradle.kts), you must explicitly add the Sonatype snapshot repository to pluginManagement because the plugins {} block only looks at the Gradle Plugin Portal by default:
      pluginsManagement {
        repositories {
          maven { url "https://central.sonatype.com/repository/maven-snapshots/" }
          gradlePluginPortal()
        }
      }
    4. Apply the plugin:
      plugins {
        id("com.domain.my-plugin") version "0.1.0-SNAPSHOT"
      }
    // In consumer's settings.gradle
    pluginsManagement {
      repositories {
        maven {
          url "https://central.sonatype.com/repository/maven-snapshots/"
        }
        gradlePluginPortal()
      }
    }
  6. Configure Publications for a Gradle Plugin

    main

    When publishing a Gradle plugin, you must publish both the plugin jar and the plugin marker artifact. The marker artifact is required for users to apply your plugin using the plugins {} DSL.

    Required plugins:

    • java-gradle-plugin
    • maven-publish
    • signing

    Key Configuration Requirements:

    • Javadoc and Sources: OSS libraries on Maven Central must include these. Use java.withJavadocJar() and java.withSourcesJar().
    • Plugin Marker: Use afterEvaluate to configure the automatically created ...PluginMarkerMaven publication.
    • POM Metadata: Ensure you provide name, description, URL, licenses, developers, and SCM information.
    • Signing: You must sign both the plugin publication and the pluginMarker publication.
    plugins {
      `java-gradle-plugin` 
      `maven-publish`      
      signing              
    }
    
    java {
      withJavadocJar()
      withSourcesJar()
    }
    
    publishing {
      publications {
        afterEvaluate {
          named<MavenPublication>("myPluginPluginMarkerMaven") {
            pom {
              // ... metadata ...
            }
          }
        }
    
        create<MavenPublication>("plugin") {
          from(components["java"])
          // ... version mapping and pom ...
        }
      }
    
      repositories {
        // Configure Sonatype repositories based on version (SNAPSHOT vs Release)
      }
    }
    
    afterEvaluate {
      signing {
        sign(publishing.publications["plugin"], publishing.publications["myPluginPluginMarkerMaven"])
      }
    }
  7. Promote a Release to Maven Central

    main

    Once snapshots are verified, promote your plugin to a full release.

    1. Remove SNAPSHOT suffix: Set version = "0.1.0".
    2. Publish to Staging: Run the same publishing tasks used for snapshots. Because of the repository logic in the build script, this will target the Sonatype staging repository instead of the snapshots repository.
      ./gradlew publishMyPluginPluginMarkerMavenPublicationToSonatypeRepository publishPluginPublicationToSonatypeRepository
    3. Close in Sonatype UI:
      • Log in to Sonatype Central.
      • Go to Staging Repositories and select your repo.
      • Verify all files (including .asc signature files) are present.
      • Click Close. This triggers validation.
    4. Release: After successful validation, click Release.
    5. Finalize: Comment on your original Jira ticket to notify the human reviewer.
  8. Configure GPG Signing in Gradle

    main

    To allow Gradle to sign your artifacts, you need to export your secret keyring and provide the credentials in your local gradle.properties file.

    1. Export the secret keyring:
      gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg
    2. Configure ~/.gradle/gradle.properties: Add the following keys (using your specific keyId, passphrase, and the absolute path to the exported file):
      signing.keyId=DHG570SZ
      signing.password=secret
      signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg
    $ gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg
  9. Install the Dependency Analysis Gradle Plugin

    main

    The simplest way to add the plugin to your project is to apply the com.autonomousapps.build-health plugin in your settings.gradle.kts file and configure the issues in your root build.gradle.kts.

    Important: If your project uses Kotlin or Android, those plugins must be loaded in the settings script classloader (or a parent).

    // settings.gradle.kts
    plugins {
      id("com.autonomousapps.build-health") version "<<latest_version>>"
    }
    
    // build.gradle.kts (root)
    dependencyAnalysis {
      issues {
        all {
          onAny {
            severity("fail")
          }
        }
      }
    }
  10. Implement a Producer using interProjectPublisher

    main

    To act as a producer, follow these steps:

    1. Define an ArtifactDescription (usually via an enum class Kind : ArtifactDescription<T>).
    2. Create an interProjectPublisher using the Publisher.Companion.interProjectPublisher extension function, passing the current project and the artifactDescription.
    3. Register a task that produces the desired output.
    4. Call publisher.publish(task.outputProperty) to link the task's output to the publisher.
    import com.autonomousapps.artifacts.Publisher.Companion.interProjectPublisher
    
    class ProducerPlugin : Plugin<Project> {
      override fun apply(target: Project): Unit = target.run {
        val publisher = interProjectPublisher(
          project = this,
          artifactDescription = MyArtifacts.Kind.PROJECT_PATH,
        )
    
        val publishPath = tasks.register("publishPath", PublishTask::class.java) {
          t.projectPath.set(target.path)
          t.output.set(layout.buildDirectory.file("path.txt"))
        }
    
        publisher.publish(publishPath.flatMap { it.output })
      }
    }
  11. Add the Gradle TestKit support dependency to your project

    main

    To simplify the creation of test fixtures for use with Gradle TestKit, add the com.autonomousapps:testkit dependency to your project's testImplementation configuration. Replace <<latest release>> with the current version of the library.

    dependencies {
      testImplementation 'com.autonomousapps:testkit:<<latest release>>'
    }