From f66f7d7fd71bbfb7a4ca953acc9391bcba725c65 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 11 Dec 2023 20:54:05 -0700 Subject: [PATCH 01/92] Prepare for next release --- release/changes.md | 4 +--- release/version.txt | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/release/changes.md b/release/changes.md index fc47cd8..709c732 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,3 +1 @@ -First stable release of the GitHub Dependency Graph Gradle Plugin. - -Contains only minor refactorings and dependency updates from v0.4.1. \ No newline at end of file +Patch release \ No newline at end of file diff --git a/release/version.txt b/release/version.txt index 3eefcb9..7dea76e 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.0.0 +1.0.1 From b42c03b850dde85b349edb83523279b264bfd46f Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Tue, 12 Dec 2023 17:10:36 +0100 Subject: [PATCH 02/92] Avoid eager configuration of the `KotlinCompile` task Align with other task configurations and follow best practices by also configuring the `KotlinCompile` task lazily, see [1]. [1]: https://docs.gradle.org/current/userguide/task_configuration_avoidance.html#sec:old_vs_new_configuration_api_overview Signed-off-by: Sebastian Schuberth --- plugin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index d722920..bc0353f 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -51,7 +51,7 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } -tasks.withType { +tasks.withType().configureEach { compilerOptions { apiVersion.set(KotlinVersion.KOTLIN_1_3) languageVersion.set(KotlinVersion.KOTLIN_1_3) From 00f4c3da99b0880d44152552694c6a49219840c3 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Tue, 12 Dec 2023 17:42:10 +0100 Subject: [PATCH 03/92] Consistently use (incubating) property assignment Signed-off-by: Sebastian Schuberth --- plugin/build.gradle.kts | 28 +++++++++---------- .../java-included-builds/app/build.gradle.kts | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index bc0353f..78050c4 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -53,9 +53,9 @@ java { tasks.withType().configureEach { compilerOptions { - apiVersion.set(KotlinVersion.KOTLIN_1_3) - languageVersion.set(KotlinVersion.KOTLIN_1_3) - jvmTarget.set(JvmTarget.JVM_1_8) + apiVersion = KotlinVersion.KOTLIN_1_3 + languageVersion = KotlinVersion.KOTLIN_1_3 + jvmTarget = JvmTarget.JVM_1_8 } } @@ -77,7 +77,7 @@ tasks.withType().configureEach { } val shadowJarTask = tasks.named("shadowJar") { - archiveClassifier.set("") + archiveClassifier = "" configurations = listOf(shadowImplementation) val projectGroup = project.group doFirst { @@ -133,8 +133,8 @@ tasks.named("jar").configure { * Configuration for publishing the plugin, locally and to the Gradle Plugin Portal. */ gradlePlugin { - website.set("https://github.com/gradle/github-dependency-graph-gradle-plugin") - vcsUrl.set("https://github.com/gradle/github-dependency-graph-gradle-plugin") + website = "https://github.com/gradle/github-dependency-graph-gradle-plugin" + vcsUrl = "https://github.com/gradle/github-dependency-graph-gradle-plugin" plugins { create("dependencyGraphPlugin") { @@ -162,8 +162,8 @@ publishing { } tasks.withType(ValidatePlugins::class).configureEach { - failOnWarning.set(true) - enableStricterValidation.set(true) + failOnWarning = true + enableStricterValidation = true } signing { @@ -189,16 +189,16 @@ fun loadReleaseNotes():String { } val createReleaseTag = tasks.register("createReleaseTag") { - tagName.set(releaseTag) + tagName = releaseTag } githubRelease { setToken(System.getenv("GITHUB_DEPENDENCY_GRAPH_GIT_TOKEN") ?: "") - owner.set("gradle") - repo.set("github-dependency-graph-gradle-plugin") - releaseName.set(releaseVersion) - tagName.set(releaseTag) - body.set(releaseNotes) + owner = "gradle" + repo = "github-dependency-graph-gradle-plugin" + releaseName = releaseVersion + tagName = releaseTag + body = releaseNotes } tasks.named("githubRelease").configure { diff --git a/sample-projects/java-included-builds/app/build.gradle.kts b/sample-projects/java-included-builds/app/build.gradle.kts index b16dfc9..b53d4f0 100644 --- a/sample-projects/java-included-builds/app/build.gradle.kts +++ b/sample-projects/java-included-builds/app/build.gradle.kts @@ -15,7 +15,7 @@ dependencies { } application { - mainClass.set("org.example.java.app.app.App") + mainClass = "org.example.java.app.app.App" } tasks.named("test") { From 1b8f6417069041838b66335cd5c5e202a51e8d33 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Tue, 12 Dec 2023 17:45:31 +0100 Subject: [PATCH 04/92] Do not use the deprecated `project.buildDir` Signed-off-by: Sebastian Schuberth --- .../gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt index 616b974..f748010 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt @@ -36,7 +36,7 @@ abstract class AbstractDependencyExtractorPlugin : Plugin { gradle.rootProject { project -> dependencyExtractorProvider .get() - .rootProjectBuildDirectory = project.buildDir + .rootProjectBuildDirectory = project.layout.buildDirectory.get().asFile } // Register the service to listen for Build Events From a99ecd6eea46075d5eb6bff6253d44a58bafa1fc Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Tue, 12 Dec 2023 17:47:32 +0100 Subject: [PATCH 05/92] Upgrade to Gradle 8.5 --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46671ac..db8c3ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 1e39046f8ef8026bacb9d905eaee5236ce8f4afd Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Thu, 14 Dec 2023 08:40:44 -0700 Subject: [PATCH 06/92] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc659e1..0428d6e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A Gradle plugin for generating a GitHub dependency graph for a Gradle build, which can be uploaded to the [GitHub Dependency Submission API](https://docs.github.com/en/rest/dependency-graph/dependency-submission). ## Usage -This plugin is designed to be used in a GitHub Actions workflow, with support coming in a future release of the [Gradle Build Action](https://github.com/gradle/gradle-build-action). +This plugin is designed to be used in a GitHub Actions workflow, an is tightly integrated into the [Gradle Build Action](https://github.com/gradle/gradle-build-action#github-dependency-graph-support). For other uses, the [core plugin](https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin) (`org.gradle.github.GitHubDependencyGraphPlugin`) should be applied to the `Gradle` instance via a Gradle init script as follows: From afdf38ac35a5771b641183922e7feb417e01604d Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 26 Dec 2023 18:38:27 -0700 Subject: [PATCH 07/92] Make sample app build compatible with older Gradle versions --- sample-projects/java-included-builds/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample-projects/java-included-builds/app/build.gradle.kts b/sample-projects/java-included-builds/app/build.gradle.kts index b53d4f0..b16dfc9 100644 --- a/sample-projects/java-included-builds/app/build.gradle.kts +++ b/sample-projects/java-included-builds/app/build.gradle.kts @@ -15,7 +15,7 @@ dependencies { } application { - mainClass = "org.example.java.app.app.App" + mainClass.set("org.example.java.app.app.App") } tasks.named("test") { From 086baaac32fc8902bdc6e4e6bcdbc054b1fa2330 Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 26 Dec 2023 18:03:24 -0700 Subject: [PATCH 08/92] Bump dependency versions --- gradle/libs.versions.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1a30801..fe50b91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,9 +8,9 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.0" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.1" } -github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.4.1" } +github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } ### Test dependencies @@ -21,10 +21,10 @@ junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.19" } json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.0.87" } -jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.0.1" } +jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } [plugins] shadow-jar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"} plugin-publish = { id = "com.gradle.plugin-publish", version = "1.2.1" } -github-release = { id = "com.github.breadmoirai.github-release", version = "2.4.1"} +github-release = { id = "com.github.breadmoirai.github-release", version = "2.5.2"} From 0a08369ecf309af72d7e92b1f1cc0ac641627a7f Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 26 Dec 2023 18:04:39 -0700 Subject: [PATCH 09/92] Bump action versions --- .github/workflows/gradle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ecbfc81..b5051a5 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 11 distribution: temurin @@ -40,7 +40,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 11 distribution: temurin @@ -59,7 +59,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 11 distribution: temurin @@ -78,7 +78,7 @@ jobs: GITHUB_DEPENDENCY_GRAPH_WORKSPACE: ${{ github.workspace }} - name: Save plugin JSON report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: plugin-json path: build/reports/dependency-graph-snapshots/plugin-self-test.json From 5d3976f2447e61e473cd94437298b14051aebe73 Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 2 Jan 2024 12:16:39 -0700 Subject: [PATCH 10/92] Bump to Gradle 8.6-rc-1 --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew.bat | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index db8c3ba..1b5ee57 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionSha256Sum=a2da4ba435f6728b43554c5845f6f88f79589c3e0018c29ab33eb23bd781255b +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-rc-1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 6b22903782c17960c8002be2c0012060ff1902d0 Mon Sep 17 00:00:00 2001 From: daz Date: Wed, 10 Jan 2024 21:38:03 -0700 Subject: [PATCH 11/92] Don't generate empty snapshots with config-cache reuse With configuration-cache enabled, no dependencies will be resolved when the configuration is loaded from the cache. In this case, we avoid generating a dependency graph snapshot so that we don't incorrectly report an empty graph. Fixes #96 Fixes #98 --- .../dependencygraph/BaseExtractorTest.groovy | 20 ++++++++++--------- .../DependencyExtractorConfigTest.groovy | 10 ++++++++++ .../extractor/DependencyExtractor.kt | 12 +++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy index d0ee634..ea1041f 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy @@ -6,7 +6,6 @@ import com.networknt.schema.* import groovy.json.JsonSlurper import groovy.transform.CompileDynamic import groovy.transform.CompileStatic -import groovy.transform.Memoized import org.gradle.github.dependencygraph.fixture.TestConfig import org.gradle.internal.hash.Hashing import org.gradle.test.fixtures.SimpleGradleExecuter @@ -49,11 +48,14 @@ abstract class BaseExtractorTest extends Specification { SimpleGradleExecuter createExecuter() { // Create a new JsonManifestLoader for each invocation of the executer - File manifestFile = reportDir.file("dummy-job-correlator.json") - loader = new JsonRepositorySnapshotLoader(manifestFile) + loader = new JsonRepositorySnapshotLoader(dependencyGraphFile) return createExecuter(testGradleVersion) } + File getDependencyGraphFile() { + reportDir.file("dummy-job-correlator.json") + } + static String getTestGradleVersion() { return System.getProperty("testGradleVersion", GradleVersion.current().version) } @@ -219,20 +221,20 @@ abstract class BaseExtractorTest extends Specification { @CompileStatic protected static class JsonRepositorySnapshotLoader { private static final String SCHEMA = "schema/github-repository-snapshot-schema.json" - private final File manifestFile + private final File dependencyGraphFile - JsonRepositorySnapshotLoader(File manifestFile) { - this.manifestFile = manifestFile + JsonRepositorySnapshotLoader(File dependencyGraphFile) { + this.dependencyGraphFile = dependencyGraphFile } protected Map jsonRepositorySnapshot() { def jsonSlurper = new JsonSlurper() - println(manifestFile.text) + println(dependencyGraphFile.text) JsonSchema schema = createSchemaValidator() ObjectMapper mapper = new ObjectMapper() - JsonNode node = mapper.readTree(manifestFile) + JsonNode node = mapper.readTree(dependencyGraphFile) validateAgainstJsonSchema(schema, node) - return jsonSlurper.parse(manifestFile) as Map + return jsonSlurper.parse(dependencyGraphFile) as Map } private static void validateAgainstJsonSchema(JsonSchema schema, JsonNode json) { diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy index 196fb72..5ff6015 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy @@ -76,6 +76,16 @@ class DependencyExtractorConfigTest extends BaseExtractorTest { manifest.assertResolved([ "org.test:foo:1.0": [package_url: purlFor(foo)] ]) + + // Execute again to test loading from config-cache + when: + dependencyGraphFile.delete() + executer.withArgument("--configuration-cache") + def buildResult = run() + + then: + buildResult.output.contains("Gradle build state was reused from the configuration-cache: Dependency Graph file will not be generated.") + !dependencyGraphFile.exists() } def "fails gracefully if configuration values not set"() { diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 878f19d..0c6e2b0 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -29,6 +29,8 @@ abstract class DependencyExtractor : private val pluginParameters = PluginParameters() + private var settingsEvaluated = false + private val resolvedConfigurations = Collections.synchronizedList(mutableListOf()) private val thrownExceptions = Collections.synchronizedList(mutableListOf()) @@ -112,6 +114,7 @@ abstract class DependencyExtractor : open fun extractSettings( details: EvaluateSettingsBuildOperationType.Details ) { + settingsEvaluated = true val settingsFile = details.settingsFile if (settingsFile != null) { buildLayout.addSettings(details.buildPath, settingsFile) @@ -296,6 +299,15 @@ abstract class DependencyExtractor : thrownExceptions ) } + + // We use the absence of Settings Evaluated to determine if the build was loaded from the configuration-cache + if (!settingsEvaluated) { + LOGGER.lifecycle( + "Gradle build state was reused from the configuration-cache: " + + "Dependency Graph file will not be generated." + ) + return + } try { writeDependencyGraph() } catch (e: RuntimeException) { From 6fa9c84e45bba8d86cc307085a71565035342af2 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 11 Jan 2024 14:06:54 -0700 Subject: [PATCH 12/92] Record dependency graph file as GitHub Step output Previously, this has been done in an init-script in the `gradle-build-action`. However, the `GITHUB_OUTPUT` env var is different on every invocation, and reading this value will invalidate the configuration cache. Moving it to the plugin should make the plugin config-cache compatible. --- .../github/dependencygraph/GitHubDependencyGraphRenderer.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt index 966642c..b04f611 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt @@ -26,6 +26,12 @@ class GitHubDependencyGraphRenderer() : DependencyGraphRenderer { val outputFile = File(outputDirectory, "${snapshotParams.dependencyGraphJobCorrelator}.json") writeDependencySnapshot(snapshot, outputFile) + + // Write the output file as a GitHub Actions step output + val githubOutput = System.getenv("GITHUB_OUTPUT") + if (githubOutput !== null && File(githubOutput).isFile) { + File(githubOutput).appendText("dependency-graph-file=${outputFile.absolutePath}\n") + } } private fun writeDependencySnapshot(graph: GitHubRepositorySnapshot, manifestFile: File) { From 2b4b82a3282eadd9934647ebfddfa115ac5f97fd Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 11 Jan 2024 14:30:26 -0700 Subject: [PATCH 13/92] Rename `DependencySource` -> `DependencyOrigin` Fixes #97 --- .../extractor/DependencyExtractor.kt | 28 +++++++++---------- ...ependencySource.kt => DependencyOrigin.kt} | 4 +-- .../model/ResolvedConfiguration.kt | 2 +- .../model/ResolvedDependency.kt | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) rename plugin/src/main/kotlin/org/gradle/dependencygraph/model/{DependencySource.kt => DependencyOrigin.kt} (75%) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 0c6e2b0..f47221f 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -161,20 +161,20 @@ abstract class DependencyExtractor : } val rootId = if (projectIdentityPath == null) "build $rootPath" else componentId(rootComponent) - val rootSource = DependencySource(rootId, rootPath) - val resolvedConfiguration = ResolvedConfiguration(rootSource, details.configurationName) + val rootOrigin = DependencyOrigin(rootId, rootPath) + val resolvedConfiguration = ResolvedConfiguration(rootOrigin, details.configurationName) for (dependencyComponent in getResolvedDependencies(rootComponent)) { val directDep = createComponentNode( componentId(dependencyComponent), - rootSource, + rootOrigin, true, dependencyComponent, repositoryLookup ) resolvedConfiguration.addDependency(directDep) - walkComponentDependencies(dependencyComponent, directDep.source, repositoryLookup, resolvedConfiguration) + walkComponentDependencies(dependencyComponent, directDep.origin, repositoryLookup, resolvedConfiguration) } resolvedConfigurations.add(resolvedConfiguration) @@ -182,44 +182,44 @@ abstract class DependencyExtractor : private fun walkComponentDependencies( component: ResolvedComponentResult, - parentSource: DependencySource, + parentOrigin: DependencyOrigin, repositoryLookup: RepositoryUrlLookup, resolvedConfiguration: ResolvedConfiguration ) { - val componentSource = getSource(component, parentSource) - val direct = componentSource != parentSource + val componentOrigin = getOrigin(component, parentOrigin) + val direct = componentOrigin != parentOrigin for (dependencyComponent in getResolvedDependencies(component)) { val dependencyId = componentId(dependencyComponent) if (!resolvedConfiguration.hasDependency(dependencyId)) { val dependencyNode = - createComponentNode(dependencyId, componentSource, direct, dependencyComponent, repositoryLookup) + createComponentNode(dependencyId, componentOrigin, direct, dependencyComponent, repositoryLookup) resolvedConfiguration.addDependency(dependencyNode) - walkComponentDependencies(dependencyComponent, componentSource, repositoryLookup, resolvedConfiguration) + walkComponentDependencies(dependencyComponent, componentOrigin, repositoryLookup, resolvedConfiguration) } } } - private fun getSource(component: ResolvedComponentResult, source: DependencySource): DependencySource { + private fun getOrigin(component: ResolvedComponentResult, parentOrigin: DependencyOrigin): DependencyOrigin { val componentId = component.id if (componentId is DefaultProjectComponentIdentifier) { - return DependencySource(componentId(component), componentId.identityPath.path) + return DependencyOrigin(componentId(component), componentId.identityPath.path) } - return source + return parentOrigin } private fun getResolvedDependencies(component: ResolvedComponentResult): List { return component.dependencies.filterIsInstance().map { it.selected }.filter { it != component } } - private fun createComponentNode(componentId: String, source: DependencySource, isDirectDependency: Boolean, component: ResolvedComponentResult, repositoryLookup: RepositoryUrlLookup): ResolvedDependency { + private fun createComponentNode(componentId: String, origin: DependencyOrigin, isDirectDependency: Boolean, component: ResolvedComponentResult, repositoryLookup: RepositoryUrlLookup): ResolvedDependency { val componentDependencies = component.dependencies.filterIsInstance().map { componentId(it.selected) } val repositoryUrl = repositoryLookup.doLookup(component) val isProjectDependency = component.id is ProjectComponentIdentifier return ResolvedDependency( componentId, - source, + origin, isDirectDependency, isProjectDependency, coordinates(component), diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencySource.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyOrigin.kt similarity index 75% rename from plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencySource.kt rename to plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyOrigin.kt index feda310..1adc7f0 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencySource.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyOrigin.kt @@ -1,10 +1,10 @@ package org.gradle.dependencygraph.model /** - * The source of a dependency declaration, representing where the direct dependency is declared, + * The origin of a dependency declaration, representing where the direct dependency is declared, * or where the parent dependency is declared for transitive dependencies. * In most cases, this will be the project component that declares the dependency, * but may also be a Version Catalog or the build as a whole. * We attempt to map this to an actual source file location when building a dependency report. */ -data class DependencySource(val id: String, val path: String) +data class DependencyOrigin(val id: String, val path: String) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt index c0206d7..9bfcc7f 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt @@ -1,7 +1,7 @@ package org.gradle.dependencygraph.model data class ResolvedConfiguration( - val rootSource: DependencySource, + val rootOrigin: DependencyOrigin, val configurationName: String, val allDependencies: MutableList = mutableListOf() ) { diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedDependency.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedDependency.kt index 362ff35..28e2832 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedDependency.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedDependency.kt @@ -6,7 +6,7 @@ private const val DEFAULT_MAVEN_REPOSITORY_URL = "https://repo.maven.apache.org/ data class ResolvedDependency( val id: String, - val source: DependencySource, + val origin: DependencyOrigin, val isDirect: Boolean, val isProject: Boolean, val coordinates: DependencyCoordinates, From d08faa635cec81e85136eec898043cd047feaa4c Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 11 Jan 2024 14:32:44 -0700 Subject: [PATCH 14/92] Prepare for v1.1.0 release --- release/changes.md | 5 ++++- release/version.txt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/release/changes.md b/release/changes.md index 709c732..c435d09 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1 +1,4 @@ -Patch release \ No newline at end of file +Improves configuration-cache support + +- Do not generate an empty dependency graph on configuration-cache reuse (#98) +- Write the report file location to the GITHUB_OUTPUT file if it exists diff --git a/release/version.txt b/release/version.txt index 7dea76e..9084fa2 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.0.1 +1.1.0 From 91ae67bf41deb094be258bbf2ea6bd950c43e5f4 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 11 Jan 2024 20:52:30 -0700 Subject: [PATCH 15/92] Fail build when run with unsupported Gradle version - Test more gradle versions, including minimum supported - Fail build when run with Gradle < 5.2 - Fail build when run with Gradle 7.0 - Update README.md for Gradle compatibility --- .github/workflows/gradle.yml | 6 ++--- README.md | 27 ++++++++++++------- ...ampleProjectDependencyExtractorTest.groovy | 4 +-- .../github/GitHubDependencyGraphPlugin.kt | 8 ++++++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b5051a5..7a5381a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -30,9 +30,9 @@ jobs: strategy: fail-fast: false matrix: - # Test latest version 5.x, 6.x and 7.x, as well as all patched minor versions of 8.x - # Latest 8.x is tested in 'quick-check' job - gradle-version: [ "5.6.4", "6.9.4", "7.6.3", "8.0.2", "8.1.1", "8.2.1", "8.3"] + # Test earliest and latest supported version of 5.x, 6.x and 7.x, as well as all patched minor versions of 8.x + # Latest 8.x is tested in 'quick-check' job using the wrapper + gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.3", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5"] runs-on: ubuntu-latest env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} diff --git a/README.md b/README.md index 0428d6e..9e485cb 100644 --- a/README.md +++ b/README.md @@ -53,16 +53,23 @@ You can provide this value via the `DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS` env ### Gradle compatibility -The plugin should be compatible with all versions of Gradle >= 5.0, and has been tested against -Gradle versions "5.6.4", "6.9.4", "7.0.2", "7.6.2", "8.0.2" and the current Gradle release. - -The plugin is compatible with running Gradle with the configuration-cache enabled. However, this support is -limited to Gradle "8.1.0" and later: -- With Gradle "8.0", the build should run successfully, but an empty dependency graph will be generated. -- With Gradle <= "7.6.4", the plugin will cause the build to fail with configuration-cache enabled. - -To use this plugin with versions of Gradle older than "8.1.0", you'll need to invoke Gradle with the -configuration-cache disabled. +The plugin should be compatible with most versions of Gradle >= 5.2, and has been tested against +Gradle versions "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1" and "7.6.3", as well as all patched versions of Gradle 8.x. + +The plugin is compatible with running Gradle with the configuration-cache enabled: this support is +limited to Gradle "8.1.0" and later. Earlier Gradle versions will not work with `--configuration-cache`. +Note that no dependency graph will be generated when configuration state is loaded from the configuration-cache. + +| Gradle version | Compatible | Compatible with configuration-cache | +| -------------- | ------- | ------------------------ | +| 1.x - 4.x | :x: | :x: | +| 5.0 - 5.1.1 | :x: | :x: | +| 5.2 - 5.6.4 | ✅ | :x: | +| 6.0 - 6.9.4 | ✅ | :x: | +| 7.0 - 7.0.2 | :x: | :x: | +| 7.1 - 7.6.3 | ✅ | :x: | +| 8.0 - 8.0.2 | ✅ | :x: | +| 8.1+ | ✅ | ✅ | ## Building/Testing diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy index d3474c9..a0fc398 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy @@ -4,8 +4,8 @@ import org.apache.commons.io.FileUtils import org.gradle.util.GradleVersion import spock.lang.IgnoreIf -// Samples aren't designed to run on Gradle 5.x -@IgnoreIf({ GradleVersion.version(testGradleVersion) < GradleVersion.version("6.0") }) +// Samples aren't designed to run on Gradle < 6.4 +@IgnoreIf({ GradleVersion.version(testGradleVersion) < GradleVersion.version("6.4") }) class SampleProjectDependencyExtractorTest extends BaseExtractorTest { def setup() { applyDependencyGraphPlugin() diff --git a/plugin/src/main/kotlin/org/gradle/github/GitHubDependencyGraphPlugin.kt b/plugin/src/main/kotlin/org/gradle/github/GitHubDependencyGraphPlugin.kt index ff1962d..5d48f69 100644 --- a/plugin/src/main/kotlin/org/gradle/github/GitHubDependencyGraphPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/github/GitHubDependencyGraphPlugin.kt @@ -1,8 +1,10 @@ package org.gradle.github +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.invocation.Gradle import org.gradle.forceresolve.ForceDependencyResolutionPlugin +import org.gradle.util.GradleVersion /** * A plugin that collects all resolved dependencies in a Gradle build for submission to the @@ -11,6 +13,12 @@ import org.gradle.forceresolve.ForceDependencyResolutionPlugin @Suppress("unused") class GitHubDependencyGraphPlugin : Plugin { override fun apply(gradle: Gradle) { + val gradleVersion = GradleVersion.current().baseVersion + if (gradleVersion < GradleVersion.version("5.2") || + (gradleVersion >= GradleVersion.version("7.0") && gradleVersion < GradleVersion.version("7.1"))) { + throw GradleException("${this.javaClass.simpleName} is not supported for $gradleVersion.") + } + // Only apply the dependency extractor to the root build if (gradle.parent == null) { gradle.pluginManager.apply(GitHubDependencyExtractorPlugin::class.java) From 618910bf25d478b202da059255ab1087782c567c Mon Sep 17 00:00:00 2001 From: daz Date: Fri, 12 Jan 2024 07:47:47 -0700 Subject: [PATCH 16/92] Prepare for v1.1.1 release --- release/changes.md | 5 +---- release/version.txt | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/release/changes.md b/release/changes.md index c435d09..c0b8a7a 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,4 +1 @@ -Improves configuration-cache support - -- Do not generate an empty dependency graph on configuration-cache reuse (#98) -- Write the report file location to the GITHUB_OUTPUT file if it exists +- Improve failure when run with unsupported Gradle version diff --git a/release/version.txt b/release/version.txt index 9084fa2..524cb55 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.1.0 +1.1.1 From f65114893d02698f4b81f0323d1b3bf1c84cc06a Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 17:57:15 +0100 Subject: [PATCH 17/92] Make properties private to address inspection hints These properties are never accessed from outside the class. Signed-off-by: Sebastian Schuberth --- .../dependencygraph/AbstractDependencyExtractorPlugin.kt | 2 +- .../dependencygraph/extractor/ResolvedConfigurationFilter.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt index f748010..fba417c 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt @@ -20,7 +20,7 @@ abstract class AbstractDependencyExtractorPlugin : Plugin { */ abstract fun getRendererClassName(): String - internal lateinit var dependencyExtractorProvider: Provider + private lateinit var dependencyExtractorProvider: Provider override fun apply(gradle: Gradle) { val gradleVersion = GradleVersion.current() diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt index 4c9d7af..3a9f060 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt @@ -1,8 +1,8 @@ package org.gradle.dependencygraph.extractor class ResolvedConfigurationFilter(projectFilter: String?, configurationFilter: String?) { - val projectRegex = projectFilter?.toRegex() - val configurationRegex = configurationFilter?.toRegex() + private val projectRegex = projectFilter?.toRegex() + private val configurationRegex = configurationFilter?.toRegex() fun include(projectPath: String, configurationName: String): Boolean { if (projectRegex != null && !projectRegex.matches(projectPath)) { From 079501473f15d6d78f4570d6b14cda75f214ddba Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 17:58:51 +0100 Subject: [PATCH 18/92] Remove an unused property in `GradleExtensions` Signed-off-by: Sebastian Schuberth --- .../org/gradle/dependencygraph/util/GradleExtensions.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/util/GradleExtensions.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/util/GradleExtensions.kt index 450bd2c..0ad6314 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/util/GradleExtensions.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/util/GradleExtensions.kt @@ -7,10 +7,6 @@ import org.gradle.api.provider.ProviderFactory import org.gradle.internal.operations.BuildOperationListenerManager internal abstract class GradleExtensions { - - inline val Gradle.objectFactory: ObjectFactory - get() = service() - inline val Gradle.providerFactory: ProviderFactory get() = service() From 72a7750477a724249df5879cac9ba2408056f84a Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 17:59:50 +0100 Subject: [PATCH 19/92] Remove unused imports Signed-off-by: Sebastian Schuberth --- .../gradle/dependencygraph/simple/SimpleDependencyGraphPlugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphPlugin.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphPlugin.kt index 940d6c0..9aa1853 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphPlugin.kt @@ -4,7 +4,6 @@ import org.gradle.api.Plugin import org.gradle.api.invocation.Gradle import org.gradle.dependencygraph.AbstractDependencyExtractorPlugin import org.gradle.forceresolve.ForceDependencyResolutionPlugin -import org.gradle.github.GitHubDependencyExtractorPlugin @Suppress("unused") class SimpleDependencyGraphPlugin : Plugin { From dae6a6a741a6bf9e9088dcb231bc4785c681e779 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 18:00:55 +0100 Subject: [PATCH 20/92] Remove an empty primary constructor in `GitHubDependencyGraphRenderer` Signed-off-by: Sebastian Schuberth --- .../github/dependencygraph/GitHubDependencyGraphRenderer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt index b04f611..bc237da 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyGraphRenderer.kt @@ -8,7 +8,7 @@ import org.gradle.dependencygraph.model.ResolvedConfiguration import org.gradle.dependencygraph.util.* import java.io.File -class GitHubDependencyGraphRenderer() : DependencyGraphRenderer { +class GitHubDependencyGraphRenderer : DependencyGraphRenderer { override fun outputDependencyGraph( pluginParameters: PluginParameters, From 8f78e986be532fcf446a675f708ef1e159f8d5fe Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 18:01:44 +0100 Subject: [PATCH 21/92] Remove redundant curly braces in a string template Signed-off-by: Sebastian Schuberth --- .../org/gradle/dependencygraph/extractor/DependencyExtractor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index f47221f..e4ac3c0 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -156,7 +156,7 @@ abstract class DependencyExtractor : val rootPath = projectIdentityPath ?: details.buildPath if (!configurationFilter.include(rootPath, details.configurationName)) { - LOGGER.debug("Ignoring resolved configuration: ${rootPath} - ${details.configurationName}") + LOGGER.debug("Ignoring resolved configuration: $rootPath - ${details.configurationName}") return } From 3dec09e227ac1cd489f6b01b86d8b8a5b3f993ac Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 20 Jan 2024 18:02:28 +0100 Subject: [PATCH 22/92] Make a constructor parameter not a property This is never accessed as a member property. Signed-off-by: Sebastian Schuberth --- .../org/gradle/github/dependencygraph/GitHubSnapshotParams.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt index 83066c4..e00aa0d 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt @@ -14,7 +14,7 @@ const val PARAM_GITHUB_SHA = "GITHUB_DEPENDENCY_GRAPH_SHA" */ const val PARAM_GITHUB_WORKSPACE = "GITHUB_DEPENDENCY_GRAPH_WORKSPACE" -class GitHubSnapshotParams(private val pluginParameters: PluginParameters) { +class GitHubSnapshotParams(pluginParameters: PluginParameters) { val dependencyGraphJobCorrelator: String = pluginParameters.load(PARAM_JOB_CORRELATOR) val dependencyGraphJobId: String =pluginParameters.load(PARAM_JOB_ID) val gitSha: String = pluginParameters.load(PARAM_GITHUB_SHA) From 5cad2276ac3607c44f9d36b4528cf80b71e049a1 Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 12:44:20 -0700 Subject: [PATCH 23/92] Run GitHub Actions on PR submission --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 7a5381a..a29bf79 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -4,6 +4,7 @@ on: push: paths-ignore: - 'release/**' + pull_request: jobs: quick-check: From 4969b727990cfe35e3fae0f0394167c72619d5e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:51:08 +0000 Subject: [PATCH 24/92] Bump com.networknt:json-schema-validator from 1.0.87 to 1.2.0 Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.0.87 to 1.2.0. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.0.87...1.2.0) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fe50b91..ceb5902 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.1" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.19" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.0.87" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.2.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } From edb2d4d042ff06e752d0befffc950e6a91c6b88b Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 13:05:09 -0700 Subject: [PATCH 25/92] Display nested cause on failure to write dependency-graph file Gradle provides special handling for certain exception types. We now use `DefaultMultiCauseException` consistently to ensure consistent failure reporting. --- .../dependencygraph/extractor/DependencyExtractor.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index f47221f..f05a96d 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -12,6 +12,7 @@ import org.gradle.dependencygraph.model.* import org.gradle.dependencygraph.util.* import org.gradle.initialization.EvaluateSettingsBuildOperationType import org.gradle.initialization.LoadProjectsBuildOperationType +import org.gradle.internal.exceptions.Contextual import org.gradle.internal.exceptions.DefaultMultiCauseException import org.gradle.internal.operations.* import java.io.File @@ -311,15 +312,11 @@ abstract class DependencyExtractor : try { writeDependencyGraph() } catch (e: RuntimeException) { - throw GradleException( - "The dependency-graph extractor plugin encountered errors while writing the dependency snapshot json file. " + - "Please report this issue at: https://github.com/gradle/github-dependency-graph-gradle-plugin/issues", - e - ) + throw DefaultMultiCauseException("Failed to write dependency-graph to file", e) } } companion object { private val LOGGER = Logging.getLogger(DependencyExtractor::class.java) } -} \ No newline at end of file +} From 27edd3e2758719a3bc5db6148f22990ca636e71f Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 13:29:31 -0700 Subject: [PATCH 26/92] Rethrow failures in FinishEvent Fixes #103 --- .../extractor/DependencyExtractor.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index f05a96d..5f449af 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -101,14 +101,16 @@ abstract class DependencyExtractor : val details: D? = buildOperation.details.let { if (it is D) it else null } - val result: R? = finishEvent.result.let { - if (it is R) it else null + if (details == null) { + return // Ignore other build operation types } - if (details == null && result == null) { - return - } else if (details == null || result == null) { - throw IllegalStateException("buildOperation.details & finishedEvent.result were unexpected types") + + val failure = finishEvent.failure + if (failure != null) { + throw failure } + + val result: R = finishEvent.result as R handler(details, result) } From 08107b11cede0ebe09a98b1acc0a6217b2475cd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:46:00 +0000 Subject: [PATCH 27/92] Bump org.codehaus.groovy:groovy-json from 3.0.19 to 3.0.20 Bumps [org.codehaus.groovy:groovy-json](https://github.com/apache/groovy) from 3.0.19 to 3.0.20. - [Commits](https://github.com/apache/groovy/commits) --- updated-dependencies: - dependency-name: org.codehaus.groovy:groovy-json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ceb5902..8205a18 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.re junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.1" } -groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.19" } +groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.20" } json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.2.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } From 745be333f7d898a10a7f92583628f09c9337c89f Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 13:45:19 -0700 Subject: [PATCH 28/92] Fix test to correctly assert failure reporting --- .../github/dependencygraph/DependencyExtractorConfigTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy index 5ff6015..6d49b6a 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy @@ -96,6 +96,6 @@ class DependencyExtractorConfigTest extends BaseExtractorTest { def result = executer.runWithFailure() then: - result.output.contains("'GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR' must be set") + result.output.contains("> The configuration parameter 'GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR' must be set") } } From 7f93238dc4b9bfb71e4d85255605d77a0287e4ca Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 14:07:09 -0700 Subject: [PATCH 29/92] Use gradle/actions/setup-gradle@v3 --- .github/workflows/gradle.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a29bf79..ce8584b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -20,8 +20,8 @@ jobs: java-version: 11 distribution: temurin - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Execute Gradle Build run: ./gradlew build @@ -46,10 +46,11 @@ jobs: java-version: 11 distribution: temurin + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + - name: Execute Gradle Build - uses: gradle/gradle-build-action@v2 - with: - arguments: -S build -DtestGradleVersion=${{ matrix.gradle-version }} + run: ./gradlew -S build -DtestGradleVersion=${{ matrix.gradle-version }} self-test: needs: quick-check @@ -65,8 +66,8 @@ jobs: java-version: 11 distribution: temurin - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Self Test :plugin run: ./plugin-self-test ForceDependencyResolutionPlugin_resolveAllDependencies From 727cc5aff9170081212543765c339ff2666b98d3 Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 07:30:45 -0700 Subject: [PATCH 30/92] Add a new 'simple' report to assist tracking down dependency origin --- .../simple/SimpleDependencyGraphRenderer.kt | 66 ++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt index 858de70..fa0268c 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt @@ -21,18 +21,64 @@ class SimpleDependencyGraphRenderer : DependencyGraphRenderer { resolvedConfigurations: List, outputDirectory: File ) { - val graphOutputFile = File(outputDirectory, "dependency-graph.json") - val graphJson = JacksonJsonSerializer.serializeToJson(resolvedConfigurations) - graphOutputFile.writeText(graphJson) - - val listOutputFile = File(outputDirectory, "dependency-list.txt") - val dependencyList = resolvedConfigurations.flatMap { it -> - it.allDependencies.map { - "${it.coordinates.group}:${it.coordinates.module}:${it.coordinates.version}" + outputDependencyGraph(outputDirectory, resolvedConfigurations) + outputDependencyScopes(outputDirectory, resolvedConfigurations) + outputDependencyList(outputDirectory, resolvedConfigurations) + } + + private fun outputDependencyGraph( + outputDirectory: File, + resolvedConfigurations: List + ) { + val outputFile = File(outputDirectory, "dependency-graph.json") + val jsonContent = JacksonJsonSerializer.serializeToJson(resolvedConfigurations) + outputFile.writeText(jsonContent) + } + + private fun outputDependencyScopes( + outputDirectory: File, + resolvedConfigurations: List + ) { + val outputFile = File(outputDirectory, "dependency-scopes.json") + val dependencyList: MutableMap> = mutableMapOf() + for (config in resolvedConfigurations) { + for (dependency in config.allDependencies) { + val dependencyOrigins = dependencyList.getOrPut(dependency.id) { mutableSetOf() } + dependencyOrigins.add( + SimpleDependencyOrigin( + config.rootOrigin.path, + config.configurationName + ) + ) + } + } + + val simpleDependencies = dependencyList.map { (id, origins) -> + SimpleDependency(id, origins.toList()) + } + val jsonContent = JacksonJsonSerializer.serializeToJson(simpleDependencies) + outputFile.writeText(jsonContent) + } + + private fun outputDependencyList( + outputDirectory: File, + resolvedConfigurations: List + ) { + val outputFile = File(outputDirectory, "dependency-list.txt") + val dependencyList = resolvedConfigurations.flatMap { config -> + config.allDependencies.map { + "${it.coordinates.group}:${it.coordinates.module}:${it.coordinates.version} ---- ${config.rootOrigin.id} ${config.rootOrigin.path} ${config.configurationName}" } }.distinct().sorted() val listTxt = dependencyList.joinToString(separator = "\n") - listOutputFile.writeText(listTxt) + outputFile.writeText(listTxt) } -} \ No newline at end of file +} + +data class SimpleDependency( + val dependency: String, + val resolvedBy: List +) + +data class SimpleDependencyOrigin(val path: String, val configuration: String) From 244b61cd6359cb68dfd084ad6bf2e9d4dee8b8dd Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 12:31:30 -0700 Subject: [PATCH 31/92] Assign dependency scope based on regex filters Add new filter variables that can be used to categorize a dependency as 'runtime'. If not specified or not matched, a 'development' scope is assumed. --- .../extractor/DependencyExtractor.kt | 23 +++++++++++++--- .../extractor/ResolvedConfigurationFilter.kt | 8 +++--- .../dependencygraph/model/DependencyScope.kt | 17 ++++++++++++ .../model/ResolvedConfiguration.kt | 1 + .../simple/SimpleDependencyGraphRenderer.kt | 25 +++++++++++------- .../GitHubRepositorySnapshotBuilder.kt | 26 +++++++++++++++---- .../dependencygraph/model/GitHubDependency.kt | 4 +++ .../internal/ResolvedConfigurationTest.groovy | 14 ++++++++++ 8 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 9477980..f4ef939 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -9,6 +9,8 @@ import org.gradle.api.internal.artifacts.configurations.ResolveConfigurationDepe import org.gradle.api.logging.Logging import org.gradle.dependencygraph.DependencyGraphRenderer import org.gradle.dependencygraph.model.* +import org.gradle.dependencygraph.model.DependencyScope.RUNTIME +import org.gradle.dependencygraph.model.DependencyScope.DEVELOPMENT import org.gradle.dependencygraph.util.* import org.gradle.initialization.EvaluateSettingsBuildOperationType import org.gradle.initialization.LoadProjectsBuildOperationType @@ -21,6 +23,9 @@ import java.util.* const val PARAM_INCLUDE_PROJECTS = "DEPENDENCY_GRAPH_INCLUDE_PROJECTS" const val PARAM_INCLUDE_CONFIGURATIONS = "DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS" +const val PARAM_RUNTIME_PROJECTS = "DEPENDENCY_GRAPH_RUNTIME_PROJECTS" +const val PARAM_RUNTIME_CONFIGURATIONS = "DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS" + const val PARAM_REPORT_DIR = "DEPENDENCY_GRAPH_REPORT_DIR" @@ -42,13 +47,20 @@ abstract class DependencyExtractor : // Properties are lazily initialized so that System Properties are initialized by the time // the values are used. This is required due to a bug in older Gradle versions. (https://github.com/gradle/gradle/issues/6825) - private val configurationFilter by lazy { + private val includeFilter by lazy { ResolvedConfigurationFilter( pluginParameters.loadOptional(PARAM_INCLUDE_PROJECTS), pluginParameters.loadOptional(PARAM_INCLUDE_CONFIGURATIONS) ) } + private val runtimeFilter by lazy { + ResolvedConfigurationFilter( + pluginParameters.loadOptional(PARAM_RUNTIME_PROJECTS), + pluginParameters.loadOptional(PARAM_RUNTIME_CONFIGURATIONS) + ) + } + private val dependencyGraphReportDir by lazy { pluginParameters.loadOptional(PARAM_REPORT_DIR) } @@ -157,15 +169,18 @@ abstract class DependencyExtractor : // It is possible to do better. By tracking the current build operation context, we can assign more precisely. // See the Gradle Enterprise Build Scan Plugin: `ConfigurationResolutionCapturer_5_0` val rootPath = projectIdentityPath ?: details.buildPath + val configurationName = details.configurationName - if (!configurationFilter.include(rootPath, details.configurationName)) { - LOGGER.debug("Ignoring resolved configuration: $rootPath - ${details.configurationName}") + if (!includeFilter.include(rootPath, configurationName)) { + LOGGER.debug("Ignoring resolved configuration: $rootPath - $configurationName") return } + val scope = if (runtimeFilter.include(rootPath, configurationName)) RUNTIME else DEVELOPMENT + val rootId = if (projectIdentityPath == null) "build $rootPath" else componentId(rootComponent) val rootOrigin = DependencyOrigin(rootId, rootPath) - val resolvedConfiguration = ResolvedConfiguration(rootOrigin, details.configurationName) + val resolvedConfiguration = ResolvedConfiguration(rootOrigin, configurationName, scope) for (dependencyComponent in getResolvedDependencies(rootComponent)) { val directDep = createComponentNode( diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt index 3a9f060..0e84b78 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt @@ -1,14 +1,14 @@ package org.gradle.dependencygraph.extractor class ResolvedConfigurationFilter(projectFilter: String?, configurationFilter: String?) { - private val projectRegex = projectFilter?.toRegex() - private val configurationRegex = configurationFilter?.toRegex() + private val projectFilter = projectFilter?.toRegex() + private val configurationFilter = configurationFilter?.toRegex() fun include(projectPath: String, configurationName: String): Boolean { - if (projectRegex != null && !projectRegex.matches(projectPath)) { + if (projectFilter != null && !projectFilter.matches(projectPath)) { return false } - if (configurationRegex != null && !configurationRegex.matches(configurationName)) { + if (configurationFilter != null && !configurationFilter.matches(configurationName)) { return false } return true diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt new file mode 100644 index 0000000..563836f --- /dev/null +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt @@ -0,0 +1,17 @@ +package org.gradle.dependencygraph.model + +/** + * Represents the scope of a resolved dependency. + * At this point, the scopes are limited to those exposed in the GitHub DependencySubmission API. + * Later development may extend this to a richer set of scopes. + */ +enum class DependencyScope { + DEVELOPMENT, RUNTIME; + + companion object { + fun getEffectiveScope(scopes: List): DependencyScope { + if (scopes.contains(RUNTIME)) return RUNTIME + return DEVELOPMENT + } + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt index 9bfcc7f..2261239 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/ResolvedConfiguration.kt @@ -3,6 +3,7 @@ package org.gradle.dependencygraph.model data class ResolvedConfiguration( val rootOrigin: DependencyOrigin, val configurationName: String, + val scope: DependencyScope, val allDependencies: MutableList = mutableListOf() ) { fun addDependency(component: ResolvedDependency) { diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt index fa0268c..40979f5 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt @@ -3,6 +3,7 @@ package org.gradle.dependencygraph.simple import org.gradle.dependencygraph.DependencyGraphRenderer import org.gradle.dependencygraph.model.BuildLayout import org.gradle.dependencygraph.model.ResolvedConfiguration +import org.gradle.dependencygraph.model.DependencyScope import org.gradle.dependencygraph.util.JacksonJsonSerializer import org.gradle.dependencygraph.util.PluginParameters import java.io.File @@ -40,21 +41,24 @@ class SimpleDependencyGraphRenderer : DependencyGraphRenderer { resolvedConfigurations: List ) { val outputFile = File(outputDirectory, "dependency-scopes.json") - val dependencyList: MutableMap> = mutableMapOf() + val dependencyList: MutableMap> = mutableMapOf() for (config in resolvedConfigurations) { for (dependency in config.allDependencies) { - val dependencyOrigins = dependencyList.getOrPut(dependency.id) { mutableSetOf() } - dependencyOrigins.add( - SimpleDependencyOrigin( + if (dependency.isProject) continue + + val dependencyScopes = dependencyList.getOrPut(dependency.id) { mutableSetOf() } + dependencyScopes.add( + SimpleDependencyResolution( config.rootOrigin.path, - config.configurationName + config.configurationName, + config.scope ) ) } } - val simpleDependencies = dependencyList.map { (id, origins) -> - SimpleDependency(id, origins.toList()) + val simpleDependencies = dependencyList.map { (id, resolutions) -> + SimpleDependency(id, DependencyScope.getEffectiveScope(resolutions.map {it.scope}), resolutions.toList()) } val jsonContent = JacksonJsonSerializer.serializeToJson(simpleDependencies) outputFile.writeText(jsonContent) @@ -67,7 +71,7 @@ class SimpleDependencyGraphRenderer : DependencyGraphRenderer { val outputFile = File(outputDirectory, "dependency-list.txt") val dependencyList = resolvedConfigurations.flatMap { config -> config.allDependencies.map { - "${it.coordinates.group}:${it.coordinates.module}:${it.coordinates.version} ---- ${config.rootOrigin.id} ${config.rootOrigin.path} ${config.configurationName}" + "${it.coordinates.group}:${it.coordinates.module}:${it.coordinates.version}" } }.distinct().sorted() @@ -78,7 +82,8 @@ class SimpleDependencyGraphRenderer : DependencyGraphRenderer { data class SimpleDependency( val dependency: String, - val resolvedBy: List + val effectiveScope: DependencyScope, + val resolvedBy: List ) -data class SimpleDependencyOrigin(val path: String, val configuration: String) +data class SimpleDependencyResolution(val path: String, val configuration: String, val scope: DependencyScope) diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt index ceffc06..25dc476 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt @@ -3,6 +3,7 @@ package org.gradle.github.dependencygraph import org.gradle.dependencygraph.model.ResolvedDependency import org.gradle.dependencygraph.model.ResolvedConfiguration import org.gradle.dependencygraph.model.BuildLayout +import org.gradle.dependencygraph.model.DependencyScope import org.gradle.github.dependencygraph.model.* class GitHubRepositorySnapshotBuilder( @@ -21,12 +22,12 @@ class GitHubRepositorySnapshotBuilder( fun buildManifest(manifestName: String, resolvedConfigurations: List, buildLayout: BuildLayout): GitHubManifest { val dependencyCollector = DependencyCollector() - for (resolutionRoot in resolvedConfigurations) { - for (dependency in resolutionRoot.allDependencies) { + for (configuration in resolvedConfigurations) { + for (dependency in configuration.allDependencies) { // Ignore project dependencies (transitive deps of projects will be reported with project) if (dependency.isProject) continue - dependencyCollector.addResolved(dependency) + dependencyCollector.addResolved(dependency, determineGitHubScope(configuration)) } } @@ -37,6 +38,13 @@ class GitHubRepositorySnapshotBuilder( ) } + private fun determineGitHubScope(configuration: ResolvedConfiguration): GitHubDependency.Scope { + return when(configuration.scope) { + DependencyScope.DEVELOPMENT -> GitHubDependency.Scope.development + DependencyScope.RUNTIME -> GitHubDependency.Scope.runtime + } + } + /** * Manifest file is the root build settings file if it exists, or the root build file if not. */ @@ -67,11 +75,12 @@ class GitHubRepositorySnapshotBuilder( /** * Merge each resolved component with the same ID into a single GitHubDependency. */ - fun addResolved(component: ResolvedDependency) { + fun addResolved(component: ResolvedDependency, scope: GitHubDependency.Scope) { val dep = dependencyBuilders.getOrPut(component.id) { GitHubDependencyBuilder(component.packageUrl()) } dep.addRelationship(relationship(component)) + dep.addScope(scope) dep.addDependencies(component.dependencies) } @@ -89,6 +98,7 @@ class GitHubRepositorySnapshotBuilder( private class GitHubDependencyBuilder(val package_url: String) { var relationship: GitHubDependency.Relationship = GitHubDependency.Relationship.indirect + var scope: GitHubDependency.Scope = GitHubDependency.Scope.development val dependencies = mutableListOf() fun addRelationship(newRelationship: GitHubDependency.Relationship) { @@ -98,6 +108,12 @@ class GitHubRepositorySnapshotBuilder( } } + fun addScope(newScope: GitHubDependency.Scope) { + if (scope == GitHubDependency.Scope.development) { + scope = newScope + } + } + fun addDependencies(newDependencies: List) { // Add any dependencies that are not in the existing set for (newDependency in newDependencies.subtract(dependencies.toSet())) { @@ -106,7 +122,7 @@ class GitHubRepositorySnapshotBuilder( } fun build(): GitHubDependency { - return GitHubDependency(package_url, relationship, dependencies) + return GitHubDependency(package_url, relationship, scope, dependencies) } } } diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt index 419de31..5b69749 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt @@ -3,9 +3,13 @@ package org.gradle.github.dependencygraph.model data class GitHubDependency( val package_url: String, val relationship: Relationship, + val scope: Scope, val dependencies: List ) { enum class Relationship { indirect, direct } + enum class Scope { + runtime, development + } } diff --git a/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy b/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy index a2db614..a7659c7 100644 --- a/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy +++ b/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy @@ -95,4 +95,18 @@ class ResolvedConfigurationTest extends Specification { !filter.include(":proj-a:proj-b:", "") !filter.include("parent-proj:proj-a", "") } + + def "filters on excluded project path"() { + when: + def filter = new ResolvedConfigurationFilter(/^:(?!buildSrc).*/, null) + + then: + filter.include(":proj-a", "") + filter.include(":proj-a:proj-b", "") + filter.include(":proj-a:buildSrc", "") + + !filter.include(":buildSrc", "") + !filter.include(":buildSrc:", "") + !filter.include(":buildSrc:proj-b:", "") + } } From b5fdd60c7ac1055d2171a59ff90d4fc6fb8ac4d0 Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 13:57:52 -0700 Subject: [PATCH 32/92] Add test coverage for runtime scope --- .../dependencygraph/BaseExtractorTest.groovy | 1 + ...rationFilterDependencyExtractorTest.groovy | 242 ++++++++++++++++++ ...MultiProjectDependencyExtractorTest.groovy | 67 ----- 3 files changed, 243 insertions(+), 67 deletions(-) create mode 100644 plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy index ea1041f..daa6eeb 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy @@ -208,6 +208,7 @@ abstract class BaseExtractorTest extends Specification { } assert actual.relationship == (expected.relationship ?: "direct") assert actual.dependencies == (expected.dependencies ?: []) + assert actual.scope == (expected.scope ?: "runtime") } return true diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy new file mode 100644 index 0000000..da6234e --- /dev/null +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy @@ -0,0 +1,242 @@ +package org.gradle.github.dependencygraph + + +import org.gradle.test.fixtures.maven.MavenModule + +class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { + private MavenModule foo + private MavenModule bar + private MavenModule baz + + private File settingsFile + private File buildFile + + def setup() { + applyDependencyGraphPlugin() + establishEnvironmentVariables() + + foo = mavenRepo.module("org.test", "foo", "1.0").publish() + bar = mavenRepo.module("org.test", "bar", "1.0").publish() + baz = mavenRepo.module("org.test", "baz", "1.0").dependsOn(bar).publish() + + settingsFile = file("settings.gradle") << """ + rootProject.name = 'parent' + """ + + buildFile = file("build.gradle") << """ + allprojects { + group "org.test" + version "1.0" + + repositories { + maven { url "${mavenRepo.uri}" } + } + } + """ + } + + def "can filter projects to extract dependencies"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + api 'org.test:bar:1.0' + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_PROJECTS=:b") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:bar:1.0": [package_url: purlFor(bar)] + ]) + } + + def "can filter configurations to extract dependencies"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + testImplementation 'org.test:baz:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + implementation 'org.test:bar:1.0' + testImplementation 'org.test:baz:1.0' + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS=compileClasspath") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:foo:1.0": [package_url: purlFor(foo)], + "org.test:bar:1.0": [package_url: purlFor(bar)] + ]) + } + + def "can filter runtime projects to determine scope"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + implementation 'org.test:bar:1.0' + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_PROJECTS=:a") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], + "org.test:bar:1.0": [package_url: purlFor(bar), scope: "development"], + ]) + } + + def "can filter runtime configurations to determine scope"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + testImplementation 'org.test:baz:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + implementation 'org.test:bar:1.0' + testImplementation 'org.test:baz:1.0' + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], + "org.test:bar:1.0": [package_url: purlFor(bar), scope: "runtime"], + "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + ]) + } + + def "can filter runtime configurations to determine scope"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + testImplementation 'org.test:baz:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + implementation 'org.test:bar:1.0' + testImplementation 'org.test:baz:1.0' + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], + "org.test:bar:1.0": [package_url: purlFor(bar), scope: "runtime"], + "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + ]) + } + + def "can filter runtime projects and configurations to determine scope"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + testImplementation 'org.test:baz:1.0' + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + api 'org.test:bar:1.0' + testImplementation 'org.test:baz:1.0' + } + } + """ + + when: + executer + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_PROJECTS=:a") + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], + "org.test:bar:1.0": [package_url: purlFor(bar), scope: "development"], + "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + ]) + } + +} diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy index 2f6f3b2..a9d70f7 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy @@ -250,71 +250,4 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest { "org.test:foo:1.0": [package_url: purlFor(foo)] ]) } - - def "can filter projects to extract dependencies"() { - given: - settingsFile << "include 'a', 'b'" - - buildFile << """ - project(':a') { - apply plugin: 'java-library' - dependencies { - api 'org.test:foo:1.0' - } - } - project(':b') { - apply plugin: 'java-library' - dependencies { - api 'org.test:bar:1.0' - } - } - """ - - when: - executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_PROJECTS=:b") - run() - - then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:bar:1.0": [package_url: purlFor(bar)] - ]) - } - - def "can filter configurations to extract dependencies"() { - given: - settingsFile << "include 'a', 'b'" - - buildFile << """ - project(':a') { - apply plugin: 'java-library' - dependencies { - api 'org.test:foo:1.0' - testImplementation 'org.test:baz:1.0' - } - } - project(':b') { - apply plugin: 'java-library' - dependencies { - implementation 'org.test:bar:1.0' - testImplementation 'org.test:baz:1.0' - } - } - """ - - when: - executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS=compileClasspath") - run() - - then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo)], - "org.test:bar:1.0": [package_url: purlFor(bar)] - ]) - } - - } From 7b2d3929b0f8d494aadb2311a6dc4793e7043cc0 Mon Sep 17 00:00:00 2001 From: daz Date: Sat, 27 Jan 2024 15:25:10 -0700 Subject: [PATCH 33/92] Only add dependency scope if configured To avoid assigned all dependencies to either 'runtime' or 'development' scope, we leave the current behaviour of assigning no scopes unless the relevant input parameters are configured. --- README.md | 19 +++++++++++++++++++ .../dependencygraph/BaseExtractorTest.groovy | 2 +- .../extractor/DependencyExtractor.kt | 15 ++++++++++++--- .../extractor/ResolvedConfigurationFilter.kt | 4 ++++ .../dependencygraph/model/DependencyScope.kt | 5 +++-- .../GitHubRepositorySnapshotBuilder.kt | 12 +++++++----- .../dependencygraph/model/GitHubDependency.kt | 2 +- 7 files changed, 47 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9e485cb..4fed133 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,25 @@ You can provide this value via the `DEPENDENCY_GRAPH_INCLUDE_PROJECTS` environme To restrict which Gradle configurations contribute to the report, you can filter configurations by name using a regular expression. You can provide this value via the `DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS` environment variable or system property. +### Controlling the scope of dependencies in the dependency graph + +The GitHub dependency graph allows a scope to be assigned to each reported dependency. +The only permissible values for scope are 'runtime' and 'development'. + +By default, no scope is assigned to dependencies in the graph. To enable scopes in the generated dependency graph, +at least one of `DEPENDENCY_GRAPH_RUNTIME_PROJECTS` or `DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS` must be configured. + +To restrict which Gradle subprojects contribute 'runtime' dependencies to the report, specify which projects to include via a regular expression. +You can provide this value via the `DEPENDENCY_GRAPH_RUNTIME_PROJECTS` environment variable or system property. +For a project not matching this filter, all dependencies will be scoped 'development'. + +To restrict which Gradle configurations contribute 'runtime' dependencies to the report, you can filter configurations by name using a regular expression. +You can provide this value via the `DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS` environment variable or system property. +Dependencies resolved by a matching configuration will be scoped 'runtime': all other dependencies will be scoped 'development'. + +For dependencies that are resolved in multiple projects and/or multiple configurations, only a single 'runtime' scoped resolution +is required for that dependency to be scoped 'runtime'. + ### Gradle compatibility The plugin should be compatible with most versions of Gradle >= 5.2, and has been tested against diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy index daa6eeb..0ab6574 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy @@ -208,7 +208,7 @@ abstract class BaseExtractorTest extends Specification { } assert actual.relationship == (expected.relationship ?: "direct") assert actual.dependencies == (expected.dependencies ?: []) - assert actual.scope == (expected.scope ?: "runtime") + assert actual.scope == expected.scope } return true diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index f4ef939..2ebc97e 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -9,8 +9,7 @@ import org.gradle.api.internal.artifacts.configurations.ResolveConfigurationDepe import org.gradle.api.logging.Logging import org.gradle.dependencygraph.DependencyGraphRenderer import org.gradle.dependencygraph.model.* -import org.gradle.dependencygraph.model.DependencyScope.RUNTIME -import org.gradle.dependencygraph.model.DependencyScope.DEVELOPMENT +import org.gradle.dependencygraph.model.DependencyScope.* import org.gradle.dependencygraph.util.* import org.gradle.initialization.EvaluateSettingsBuildOperationType import org.gradle.initialization.LoadProjectsBuildOperationType @@ -176,7 +175,7 @@ abstract class DependencyExtractor : return } - val scope = if (runtimeFilter.include(rootPath, configurationName)) RUNTIME else DEVELOPMENT + val scope = dependencyScope(rootPath, configurationName) val rootId = if (projectIdentityPath == null) "build $rootPath" else componentId(rootComponent) val rootOrigin = DependencyOrigin(rootId, rootPath) @@ -198,6 +197,16 @@ abstract class DependencyExtractor : resolvedConfigurations.add(resolvedConfiguration) } + private fun dependencyScope( + rootPath: String, + configurationName: String + ): DependencyScope { + if (runtimeFilter.isConfigured()) { + return if (runtimeFilter.include(rootPath, configurationName)) RUNTIME else DEVELOPMENT + } + return UNKNOWN + } + private fun walkComponentDependencies( component: ResolvedComponentResult, parentOrigin: DependencyOrigin, diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt index 0e84b78..6d5de2f 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt @@ -13,4 +13,8 @@ class ResolvedConfigurationFilter(projectFilter: String?, configurationFilter: S } return true } + + fun isConfigured(): Boolean { + return projectFilter != null || configurationFilter != null + } } \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt index 563836f..cb0f4e3 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt @@ -6,12 +6,13 @@ package org.gradle.dependencygraph.model * Later development may extend this to a richer set of scopes. */ enum class DependencyScope { - DEVELOPMENT, RUNTIME; + UNKNOWN, DEVELOPMENT, RUNTIME; companion object { fun getEffectiveScope(scopes: List): DependencyScope { if (scopes.contains(RUNTIME)) return RUNTIME - return DEVELOPMENT + if (scopes.contains(DEVELOPMENT)) return DEVELOPMENT + return UNKNOWN } } } \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt index 25dc476..43af25b 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt @@ -38,10 +38,11 @@ class GitHubRepositorySnapshotBuilder( ) } - private fun determineGitHubScope(configuration: ResolvedConfiguration): GitHubDependency.Scope { + private fun determineGitHubScope(configuration: ResolvedConfiguration): GitHubDependency.Scope? { return when(configuration.scope) { DependencyScope.DEVELOPMENT -> GitHubDependency.Scope.development DependencyScope.RUNTIME -> GitHubDependency.Scope.runtime + DependencyScope.UNKNOWN -> null } } @@ -75,7 +76,7 @@ class GitHubRepositorySnapshotBuilder( /** * Merge each resolved component with the same ID into a single GitHubDependency. */ - fun addResolved(component: ResolvedDependency, scope: GitHubDependency.Scope) { + fun addResolved(component: ResolvedDependency, scope: GitHubDependency.Scope?) { val dep = dependencyBuilders.getOrPut(component.id) { GitHubDependencyBuilder(component.packageUrl()) } @@ -98,7 +99,7 @@ class GitHubRepositorySnapshotBuilder( private class GitHubDependencyBuilder(val package_url: String) { var relationship: GitHubDependency.Relationship = GitHubDependency.Relationship.indirect - var scope: GitHubDependency.Scope = GitHubDependency.Scope.development + var scope: GitHubDependency.Scope? = null val dependencies = mutableListOf() fun addRelationship(newRelationship: GitHubDependency.Relationship) { @@ -108,8 +109,9 @@ class GitHubRepositorySnapshotBuilder( } } - fun addScope(newScope: GitHubDependency.Scope) { - if (scope == GitHubDependency.Scope.development) { + fun addScope(newScope: GitHubDependency.Scope?) { + if (newScope == null) return + if (scope == null || scope == GitHubDependency.Scope.development) { scope = newScope } } diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt index 5b69749..bf2deb1 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDependency.kt @@ -3,7 +3,7 @@ package org.gradle.github.dependencygraph.model data class GitHubDependency( val package_url: String, val relationship: Relationship, - val scope: Scope, + val scope: Scope?, val dependencies: List ) { enum class Relationship { From 9945b32b09ffdc1bb7dd0eaa1fceff5c63af9c8e Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 28 Jan 2024 03:42:10 -0700 Subject: [PATCH 34/92] Filter by exclusion as well as inclusion Using a Regex include filter can be problematic when trying to _exclude_ a matching entry (or entries). Adding specific exclude filters matching the current include filters makes configuration more straightforward. --- .../dependencygraph/BaseExtractorTest.groovy | 10 ++ ...rationFilterDependencyExtractorTest.groovy | 167 +++++++++++------- .../extractor/DependencyExtractor.kt | 34 ++-- .../extractor/ResolvedConfigurationFilter.kt | 48 +++-- .../internal/ResolvedConfigurationTest.groovy | 112 ------------ 5 files changed, 165 insertions(+), 206 deletions(-) delete mode 100644 plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy index 0ab6574..d3ba3cf 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy @@ -113,6 +113,10 @@ abstract class BaseExtractorTest extends Specification { } apply plugin: GitHubDependencyGraphPlugin """.stripMargin() + resetArguments() + } + + protected SimpleGradleExecuter resetArguments() { getExecuter().withArguments("--init-script", "init.gradle") } @@ -194,6 +198,12 @@ abstract class BaseExtractorTest extends Specification { return (manifestData.file as Map).source_location } + def assertResolved(List expectedResolved) { + def resolved = manifestData.resolved as Map + assert resolved.keySet() == expectedResolved as Set + return true + } + def assertResolved(Map expectedResolved = [:]) { def resolved = manifestData.resolved as Map diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy index da6234e..b4da125 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy @@ -37,7 +37,7 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { def "can filter projects to extract dependencies"() { given: - settingsFile << "include 'a', 'b'" + settingsFile << "include 'a', 'b', 'c'" buildFile << """ project(':a') { @@ -52,6 +52,12 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { api 'org.test:bar:1.0' } } + project(':c') { + apply plugin: 'java-library' + dependencies { + api 'org.test:baz:1.0' + } + } """ when: @@ -59,11 +65,32 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:bar:1.0": [package_url: purlFor(bar)] - ]) + gitHubManifest().assertResolved(["org.test:bar:1.0"]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_PROJECTS=:[ab]") + run() + + then: + gitHubManifest().assertResolved(["org.test:foo:1.0", "org.test:bar:1.0"]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_EXCLUDE_PROJECTS=:[bc]") + run() + + then: + gitHubManifest().assertResolved(["org.test:foo:1.0"]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_INCLUDE_PROJECTS=:[ab]") + executer.withArgument("-DDEPENDENCY_GRAPH_EXCLUDE_PROJECTS=:b") + run() + + then: + gitHubManifest().assertResolved(["org.test:foo:1.0"]) } def "can filter configurations to extract dependencies"() { @@ -92,12 +119,15 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo)], - "org.test:bar:1.0": [package_url: purlFor(bar)] - ]) + gitHubManifest().assertResolved(["org.test:foo:1.0", "org.test:bar:1.0"]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS=test(Compile|Runtime)Classpath") + run() + + then: + gitHubManifest().assertResolved(["org.test:foo:1.0", "org.test:bar:1.0"]) } def "can filter runtime projects to determine scope"() { @@ -120,50 +150,36 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { """ when: - executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_PROJECTS=:a") + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS=:a") run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], - "org.test:bar:1.0": [package_url: purlFor(bar), scope: "development"], + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "development"] ]) - } - def "can filter runtime configurations to determine scope"() { - given: - settingsFile << "include 'a', 'b'" + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS=:b") + run() - buildFile << """ - project(':a') { - apply plugin: 'java-library' - dependencies { - api 'org.test:foo:1.0' - testImplementation 'org.test:baz:1.0' - } - } - project(':b') { - apply plugin: 'java-library' - dependencies { - implementation 'org.test:bar:1.0' - testImplementation 'org.test:baz:1.0' - } - } - """ + then: + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "development"] + ]) when: - executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS=:[ab]") + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS=:b") run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], - "org.test:bar:1.0": [package_url: purlFor(bar), scope: "runtime"], - "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "development"] ]) } @@ -189,22 +205,45 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { """ when: - executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS=compileClasspath") + run() + + then: + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "runtime"], + "org.test:baz:1.0": [scope: "development", dependencies: ["org.test:bar:1.0"]] + ]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS=.*Classpath") run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], - "org.test:bar:1.0": [package_url: purlFor(bar), scope: "runtime"], - "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "runtime"], + "org.test:baz:1.0": [scope: "runtime", dependencies: ["org.test:bar:1.0"]] + ]) + + when: + resetArguments() + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS=.*Classpath") + executer.withArgument("-DDEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS=test(Compile|Runtime)Classpath") + run() + + then: + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "runtime"], + "org.test:baz:1.0": [scope: "development", dependencies: ["org.test:bar:1.0"]] ]) } def "can filter runtime projects and configurations to determine scope"() { given: - settingsFile << "include 'a', 'b'" + settingsFile << "include 'a', 'b', 'c'" buildFile << """ project(':a') { @@ -221,21 +260,27 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { testImplementation 'org.test:baz:1.0' } } + project(':b') { + apply plugin: 'java-library' + dependencies { + api 'org.test:baz:1.0' + } + } """ when: executer - .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS=compileClasspath") - .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_PROJECTS=:a") + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS=:[ab]") + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS=.*Classpath") + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS=:b") + .withArgument("-DDEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS=test(Compile|Runtime)Classpath") run() then: - def manifest = gitHubManifest() - manifest.sourceFile == "settings.gradle" - manifest.assertResolved([ - "org.test:foo:1.0": [package_url: purlFor(foo), scope: "runtime"], - "org.test:bar:1.0": [package_url: purlFor(bar), scope: "development"], - "org.test:baz:1.0": [package_url: purlFor(baz), scope: "development", dependencies: ["org.test:bar:1.0"]] + gitHubManifest().assertResolved([ + "org.test:foo:1.0": [scope: "runtime"], + "org.test:bar:1.0": [scope: "development"], + "org.test:baz:1.0": [scope: "development", dependencies: ["org.test:bar:1.0"]] ]) } diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 2ebc97e..4cfe3d7 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -1,6 +1,5 @@ package org.gradle.dependencygraph.extractor -import org.gradle.api.GradleException import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.artifacts.result.ResolvedDependencyResult @@ -13,7 +12,6 @@ import org.gradle.dependencygraph.model.DependencyScope.* import org.gradle.dependencygraph.util.* import org.gradle.initialization.EvaluateSettingsBuildOperationType import org.gradle.initialization.LoadProjectsBuildOperationType -import org.gradle.internal.exceptions.Contextual import org.gradle.internal.exceptions.DefaultMultiCauseException import org.gradle.internal.operations.* import java.io.File @@ -22,8 +20,12 @@ import java.util.* const val PARAM_INCLUDE_PROJECTS = "DEPENDENCY_GRAPH_INCLUDE_PROJECTS" const val PARAM_INCLUDE_CONFIGURATIONS = "DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS" -const val PARAM_RUNTIME_PROJECTS = "DEPENDENCY_GRAPH_RUNTIME_PROJECTS" -const val PARAM_RUNTIME_CONFIGURATIONS = "DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS" +const val PARAM_EXCLUDE_PROJECTS = "DEPENDENCY_GRAPH_EXCLUDE_PROJECTS" +const val PARAM_EXCLUDE_CONFIGURATIONS = "DEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS" +const val PARAM_RUNTIME_INCLUDE_PROJECTS = "DEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS" +const val PARAM_RUNTIME_INCLUDE_CONFIGURATIONS = "DEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS" +const val PARAM_RUNTIME_EXCLUDE_PROJECTS = "DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS" +const val PARAM_RUNTIME_EXCLUDE_CONFIGURATIONS = "DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS" const val PARAM_REPORT_DIR = "DEPENDENCY_GRAPH_REPORT_DIR" @@ -46,18 +48,8 @@ abstract class DependencyExtractor : // Properties are lazily initialized so that System Properties are initialized by the time // the values are used. This is required due to a bug in older Gradle versions. (https://github.com/gradle/gradle/issues/6825) - private val includeFilter by lazy { - ResolvedConfigurationFilter( - pluginParameters.loadOptional(PARAM_INCLUDE_PROJECTS), - pluginParameters.loadOptional(PARAM_INCLUDE_CONFIGURATIONS) - ) - } - - private val runtimeFilter by lazy { - ResolvedConfigurationFilter( - pluginParameters.loadOptional(PARAM_RUNTIME_PROJECTS), - pluginParameters.loadOptional(PARAM_RUNTIME_CONFIGURATIONS) - ) + private val configurationFilter by lazy { + ResolvedConfigurationFilter(pluginParameters) } private val dependencyGraphReportDir by lazy { @@ -67,12 +59,12 @@ abstract class DependencyExtractor : abstract fun getRendererClassName(): String override fun started(buildOperation: BuildOperationDescriptor, startEvent: OperationStartEvent) { - // This method will never be called when registered in a `BuildServiceRegistry` (ie. Gradle 6.1 & higher) + // This method will never be called when registered in a `BuildServiceRegistry` (i.e. Gradle 6.1 & higher) // No-op } override fun progress(operationIdentifier: OperationIdentifier, progressEvent: OperationProgressEvent) { - // This method will never be called when registered in a `BuildServiceRegistry` (ie. Gradle 6.1 & higher) + // This method will never be called when registered in a `BuildServiceRegistry` (i.e. Gradle 6.1 & higher) // No-op } @@ -170,7 +162,7 @@ abstract class DependencyExtractor : val rootPath = projectIdentityPath ?: details.buildPath val configurationName = details.configurationName - if (!includeFilter.include(rootPath, configurationName)) { + if (!configurationFilter.include(rootPath, configurationName)) { LOGGER.debug("Ignoring resolved configuration: $rootPath - $configurationName") return } @@ -201,8 +193,8 @@ abstract class DependencyExtractor : rootPath: String, configurationName: String ): DependencyScope { - if (runtimeFilter.isConfigured()) { - return if (runtimeFilter.include(rootPath, configurationName)) RUNTIME else DEVELOPMENT + if (configurationFilter.scopesAreConfigured()) { + return if (configurationFilter.isRuntime(rootPath, configurationName)) RUNTIME else DEVELOPMENT } return UNKNOWN } diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt index 6d5de2f..421bea1 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/ResolvedConfigurationFilter.kt @@ -1,20 +1,44 @@ package org.gradle.dependencygraph.extractor -class ResolvedConfigurationFilter(projectFilter: String?, configurationFilter: String?) { - private val projectFilter = projectFilter?.toRegex() - private val configurationFilter = configurationFilter?.toRegex() +import org.gradle.dependencygraph.util.PluginParameters + +class ResolvedConfigurationFilter(pluginParameters: PluginParameters) { + private val includeProjects = pluginParameters.loadOptional(PARAM_INCLUDE_PROJECTS)?.toRegex() + private val includeConfigurations = pluginParameters.loadOptional(PARAM_INCLUDE_CONFIGURATIONS)?.toRegex() + private val excludeProjects = pluginParameters.loadOptional(PARAM_EXCLUDE_PROJECTS)?.toRegex() + private val excludeConfigurations = pluginParameters.loadOptional(PARAM_EXCLUDE_CONFIGURATIONS)?.toRegex() + + private val runtimeIncludeProjects = pluginParameters.loadOptional(PARAM_RUNTIME_INCLUDE_PROJECTS)?.toRegex() + private val runtimeIncludeConfigurations = pluginParameters.loadOptional(PARAM_RUNTIME_INCLUDE_CONFIGURATIONS)?.toRegex() + private val runtimeExcludeProjects = pluginParameters.loadOptional(PARAM_RUNTIME_EXCLUDE_PROJECTS)?.toRegex() + private val runtimeExcludeConfigurations = pluginParameters.loadOptional(PARAM_RUNTIME_EXCLUDE_CONFIGURATIONS)?.toRegex() fun include(projectPath: String, configurationName: String): Boolean { - if (projectFilter != null && !projectFilter.matches(projectPath)) { - return false - } - if (configurationFilter != null && !configurationFilter.matches(configurationName)) { - return false - } - return true + return includes(includeProjects, projectPath) + && notExcludes(excludeProjects, projectPath) + && includes(includeConfigurations, configurationName) + && notExcludes(excludeConfigurations, configurationName) + } + + fun scopesAreConfigured(): Boolean { + return runtimeIncludeProjects != null + || runtimeIncludeConfigurations != null + || runtimeExcludeProjects != null + || runtimeExcludeConfigurations != null + } + + fun isRuntime(projectPath: String, configurationName: String): Boolean { + return includes(runtimeIncludeProjects, projectPath) + && notExcludes(runtimeExcludeProjects, projectPath) + && includes(runtimeIncludeConfigurations, configurationName) + && notExcludes(runtimeExcludeConfigurations, configurationName) + } + + private fun includes(regex: Regex?, value: String): Boolean { + return regex == null || regex.matches(value) } - fun isConfigured(): Boolean { - return projectFilter != null || configurationFilter != null + private fun notExcludes(regex: Regex?, value: String): Boolean { + return regex == null || !regex.matches(value) } } \ No newline at end of file diff --git a/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy b/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy deleted file mode 100644 index a7659c7..0000000 --- a/plugin/src/test/groovy/org/gradle/github/dependencygraph/internal/ResolvedConfigurationTest.groovy +++ /dev/null @@ -1,112 +0,0 @@ -package org.gradle.github.dependencygraph.internal - -import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter -import spock.lang.Specification - -class ResolvedConfigurationTest extends Specification { - def "null filter includes everything"() { - when: - def filter = new ResolvedConfigurationFilter(null, null) - - then: - filter.include("foo", "bar") - filter.include(":foo:bar:baz", "not a real name") - filter.include("", "") - } - - def "filters on exact configuration name"() { - when: - def filter = new ResolvedConfigurationFilter(null, "compileClasspath") - - then: - filter.include("", "compileClasspath") - - !filter.include("", "classpath") - !filter.include("", "runtimeClasspath") - !filter.include("", "testCompileClasspath") - } - - def "filters on exact configuration names"() { - when: - def filter = new ResolvedConfigurationFilter(null, "compileClasspath|runtimeClasspath") - - then: - filter.include("", "compileClasspath") - filter.include("", "runtimeClasspath") - - !filter.include("", "classpath") - !filter.include("", "testCompileClasspath") - !filter.include("", "runtimeElements") - } - - def "filters on configuration name match"() { - when: - def filter = new ResolvedConfigurationFilter(null, ".*[cC]lasspath") - - then: - filter.include("", "compileClasspath") - filter.include("", "runtimeClasspath") - filter.include("", "testCompileClasspath") - filter.include("", "classpath") - - !filter.include("", "runtimeElements") - !filter.include("", "compileClasspathOnly") - } - - def "filters on exact project path"() { - when: - def filter = new ResolvedConfigurationFilter(":parent-proj:proj", null) - - then: - filter.include(":parent-proj:proj", "") - - !filter.include(":parent-proj", "") - !filter.include(":parent-proj:", "") - !filter.include(":proj", "") - !filter.include(":", "") - } - - def "filters on exact project paths"() { - when: - def filter = new ResolvedConfigurationFilter(":proj-a|:proj-b", null) - - then: - filter.include(":proj-a", "") - filter.include(":proj-b", "") - - !filter.include(":parent-proj:proj-a", "") - !filter.include(":proj", "") - !filter.include(":", "") - } - - def "filters on project path match"() { - when: - def filter = new ResolvedConfigurationFilter(/(:[\w-]+)*:proj-a(:[\w-]+)*/, null) - - then: - filter.include(":proj-a", "") - filter.include(":proj-a:proj-b", "") - filter.include(":proj-a:proj-b:proj-c", "") - filter.include(":parent-proj:proj-a", "") - filter.include(":parent-proj:proj-a:proj-b", "") - - !filter.include(":proj-another", "") - !filter.include(":proj-a:", "") - !filter.include(":proj-a:proj-b:", "") - !filter.include("parent-proj:proj-a", "") - } - - def "filters on excluded project path"() { - when: - def filter = new ResolvedConfigurationFilter(/^:(?!buildSrc).*/, null) - - then: - filter.include(":proj-a", "") - filter.include(":proj-a:proj-b", "") - filter.include(":proj-a:buildSrc", "") - - !filter.include(":buildSrc", "") - !filter.include(":buildSrc:", "") - !filter.include(":buildSrc:proj-b:", "") - } -} From 94c5357f222c5d591b56c722e3cd442ca791a684 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 28 Jan 2024 03:45:48 -0700 Subject: [PATCH 35/92] Cosmetic improvements to Simple resolution report --- .../dependencygraph/extractor/DependencyExtractor.kt | 4 ++-- .../org/gradle/dependencygraph/model/DependencyScope.kt | 8 ++++---- .../simple/SimpleDependencyGraphRenderer.kt | 6 +++--- .../dependencygraph/GitHubRepositorySnapshotBuilder.kt | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 4cfe3d7..dcf1eae 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -194,9 +194,9 @@ abstract class DependencyExtractor : configurationName: String ): DependencyScope { if (configurationFilter.scopesAreConfigured()) { - return if (configurationFilter.isRuntime(rootPath, configurationName)) RUNTIME else DEVELOPMENT + return if (configurationFilter.isRuntime(rootPath, configurationName)) Runtime else Development } - return UNKNOWN + return Unknown } private fun walkComponentDependencies( diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt index cb0f4e3..65b8fee 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/model/DependencyScope.kt @@ -6,13 +6,13 @@ package org.gradle.dependencygraph.model * Later development may extend this to a richer set of scopes. */ enum class DependencyScope { - UNKNOWN, DEVELOPMENT, RUNTIME; + Unknown, Development, Runtime; companion object { fun getEffectiveScope(scopes: List): DependencyScope { - if (scopes.contains(RUNTIME)) return RUNTIME - if (scopes.contains(DEVELOPMENT)) return DEVELOPMENT - return UNKNOWN + if (scopes.contains(Runtime)) return Runtime + if (scopes.contains(Development)) return Development + return Unknown } } } \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt index 40979f5..099e403 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/simple/SimpleDependencyGraphRenderer.kt @@ -40,14 +40,14 @@ class SimpleDependencyGraphRenderer : DependencyGraphRenderer { outputDirectory: File, resolvedConfigurations: List ) { - val outputFile = File(outputDirectory, "dependency-scopes.json") + val outputFile = File(outputDirectory, "dependency-resolution.json") val dependencyList: MutableMap> = mutableMapOf() for (config in resolvedConfigurations) { for (dependency in config.allDependencies) { if (dependency.isProject) continue - val dependencyScopes = dependencyList.getOrPut(dependency.id) { mutableSetOf() } - dependencyScopes.add( + val dependencyResolutions = dependencyList.getOrPut(dependency.id) { mutableSetOf() } + dependencyResolutions.add( SimpleDependencyResolution( config.rootOrigin.path, config.configurationName, diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt index 43af25b..3fc3a44 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt @@ -40,9 +40,9 @@ class GitHubRepositorySnapshotBuilder( private fun determineGitHubScope(configuration: ResolvedConfiguration): GitHubDependency.Scope? { return when(configuration.scope) { - DependencyScope.DEVELOPMENT -> GitHubDependency.Scope.development - DependencyScope.RUNTIME -> GitHubDependency.Scope.runtime - DependencyScope.UNKNOWN -> null + DependencyScope.Development -> GitHubDependency.Scope.development + DependencyScope.Runtime -> GitHubDependency.Scope.runtime + DependencyScope.Unknown -> null } } From bf014629c5d309ea53f20b294d88a6f737937431 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 28 Jan 2024 09:03:27 -0700 Subject: [PATCH 36/92] Improve documentation for configuration filtering --- README.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4fed133..6a30f69 100644 --- a/README.md +++ b/README.md @@ -45,27 +45,35 @@ eg: Env var `DEPENDENCY_GRAPH_REPORT_DIR` can be set with `-DDEPENDENCY_GRAPH_RE If you do not want to include every dependency configuration in every project in your build, you can limit the dependency extraction to a subset of these. -To restrict which Gradle subprojects contribute to the report, specify which projects to include via a regular expression. -You can provide this value via the `DEPENDENCY_GRAPH_INCLUDE_PROJECTS` environment variable or system property. +The following parameters control the set of projects and configurations that contribute dependencies. +Each of these is a regular expression value, and can set either as an environment variable or as a system property on the command line. -To restrict which Gradle configurations contribute to the report, you can filter configurations by name using a regular expression. -You can provide this value via the `DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS` environment variable or system property. +| Property | Description | Default | +|-----------------------------------------|---------------------------|---------------------------------| +| DEPENDENCY_GRAPH_INCLUDE_PROJECTS | Projects to include | All projects are included | +| DEPENDENCY_GRAPH_EXCLUDE_PROJECTS | Projects to exclude | No projects are included | +| DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS | Configurations to include | All configurations are included | +| DEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS | Configurations to exclude | No configurations are included | ### Controlling the scope of dependencies in the dependency graph The GitHub dependency graph allows a scope to be assigned to each reported dependency. The only permissible values for scope are 'runtime' and 'development'. -By default, no scope is assigned to dependencies in the graph. To enable scopes in the generated dependency graph, -at least one of `DEPENDENCY_GRAPH_RUNTIME_PROJECTS` or `DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS` must be configured. +The following parameters control the set of projects and configurations that provide 'runtime' scoped dependencies. +Any dependency resolution that does not match these parameters will be scoped 'development'. -To restrict which Gradle subprojects contribute 'runtime' dependencies to the report, specify which projects to include via a regular expression. -You can provide this value via the `DEPENDENCY_GRAPH_RUNTIME_PROJECTS` environment variable or system property. -For a project not matching this filter, all dependencies will be scoped 'development'. +Each of these parameters is a regular expression value, and can set either as an environment variable or as a system property on the command line. -To restrict which Gradle configurations contribute 'runtime' dependencies to the report, you can filter configurations by name using a regular expression. -You can provide this value via the `DEPENDENCY_GRAPH_RUNTIME_CONFIGURATIONS` environment variable or system property. -Dependencies resolved by a matching configuration will be scoped 'runtime': all other dependencies will be scoped 'development'. +| Property | Description | Default | +|-------------------------------------------------|-----------------------------------------------------------|---------------------------------| +| DEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS | Projects that can provide 'runtime' dependencies | All projects are included | +| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS | Projects that do not provide 'runtime' dependencies | No projects are included | +| DEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS | Configurations that contain 'runtime' dependencies | All configurations are included | +| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS | Configurations that do not contain 'runtime' dependencies | No configurations are included | + +By default, no scope is assigned to dependencies in the graph. To enable scopes in the generated dependency graph, +at least one of these parameters must be configured. For dependencies that are resolved in multiple projects and/or multiple configurations, only a single 'runtime' scoped resolution is required for that dependency to be scoped 'runtime'. From 4b295f7f748264cc061eb878618cda3da8fe66ee Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 28 Jan 2024 09:57:36 -0700 Subject: [PATCH 37/92] Prepare for v1.2.0 release --- release/changes.md | 3 ++- release/version.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/release/changes.md b/release/changes.md index c0b8a7a..0cf92cb 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1 +1,2 @@ -- Improve failure when run with unsupported Gradle version +- Improve failure reporting (#104) +- Assign dependency scopes based on include/exclude filters (#105) diff --git a/release/version.txt b/release/version.txt index 524cb55..26aaba0 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.1.1 +1.2.0 From 9222dc49e1a80e82f02d33ba14ffea273b917175 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Mon, 29 Jan 2024 14:38:09 -0700 Subject: [PATCH 38/92] Fix definitions for 'excluded' --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6a30f69..8001fed 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ Each of these is a regular expression value, and can set either as an environmen | Property | Description | Default | |-----------------------------------------|---------------------------|---------------------------------| | DEPENDENCY_GRAPH_INCLUDE_PROJECTS | Projects to include | All projects are included | -| DEPENDENCY_GRAPH_EXCLUDE_PROJECTS | Projects to exclude | No projects are included | +| DEPENDENCY_GRAPH_EXCLUDE_PROJECTS | Projects to exclude | No projects are excluded | | DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS | Configurations to include | All configurations are included | -| DEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS | Configurations to exclude | No configurations are included | +| DEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS | Configurations to exclude | No configurations are excluded | ### Controlling the scope of dependencies in the dependency graph @@ -68,9 +68,9 @@ Each of these parameters is a regular expression value, and can set either as an | Property | Description | Default | |-------------------------------------------------|-----------------------------------------------------------|---------------------------------| | DEPENDENCY_GRAPH_RUNTIME_INCLUDE_PROJECTS | Projects that can provide 'runtime' dependencies | All projects are included | -| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS | Projects that do not provide 'runtime' dependencies | No projects are included | +| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_PROJECTS | Projects that do not provide 'runtime' dependencies | No projects are excluded | | DEPENDENCY_GRAPH_RUNTIME_INCLUDE_CONFIGURATIONS | Configurations that contain 'runtime' dependencies | All configurations are included | -| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS | Configurations that do not contain 'runtime' dependencies | No configurations are included | +| DEPENDENCY_GRAPH_RUNTIME_EXCLUDE_CONFIGURATIONS | Configurations that do not contain 'runtime' dependencies | No configurations are excluded | By default, no scope is assigned to dependencies in the graph. To enable scopes in the generated dependency graph, at least one of these parameters must be configured. From 0b965f035267243309b4501f857c97a72eb1554a Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Wed, 31 Jan 2024 11:44:39 -0700 Subject: [PATCH 39/92] Add instructions for using SimpleDependencyGraphPlugin --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 8001fed..1be0c25 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,32 @@ Note that no dependency graph will be generated when configuration state is load | 8.0 - 8.0.2 | ✅ | :x: | | 8.1+ | ✅ | ✅ | +## Using the plugin in a standalone project + +As well as the `GitHubDependencyGraphPlugin`, which is tailored for use by the [gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) GitHub Action, this repository also provides the `SimpleDependencyGraphPlugin`, which generates dependency-graph outputs in simple text format. + +To use the `SimpleDependencyGraphPlugin` you'll need to create an `init-script.gradle` file to apply the plugin to your project: + +```groovy +initscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath "org.gradle:github-dependency-graph-gradle-plugin:+" + } +} +apply plugin: org.gradle.dependencygraph.simple.SimpleDependencyGraphPlugin +``` + +and then execute the task to resolve all dependencies in your project: + +``` +./gradlew -I init.gradle :ForceDependencyResolutionPlugin_resolveAllDependencies +``` + +You'll find the generated files in `build/dependency-graph-snapshots`. + ## Building/Testing To build and test this plugin, run the following task: From f63803fc4afdb58ab6f15ac8c8b2046f86c84aae Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Wed, 31 Jan 2024 12:00:29 -0700 Subject: [PATCH 40/92] Disable dependency-verification and config-cache in doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1be0c25..823ec6a 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ apply plugin: org.gradle.dependencygraph.simple.SimpleDependencyGraphPlugin and then execute the task to resolve all dependencies in your project: ``` -./gradlew -I init.gradle :ForceDependencyResolutionPlugin_resolveAllDependencies +./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-configure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies ``` You'll find the generated files in `build/dependency-graph-snapshots`. From feefe25da99b3613e588ebe942ec0ebb72abe150 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Wed, 31 Jan 2024 12:12:04 -0700 Subject: [PATCH 41/92] Add docs for finding the source of a dependency --- README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 823ec6a..4fa6760 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Note that no dependency graph will be generated when configuration state is load | 8.0 - 8.0.2 | ✅ | :x: | | 8.1+ | ✅ | ✅ | -## Using the plugin in a standalone project +## Using the plugin standalone project As well as the `GitHubDependencyGraphPlugin`, which is tailored for use by the [gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) GitHub Action, this repository also provides the `SimpleDependencyGraphPlugin`, which generates dependency-graph outputs in simple text format. @@ -124,6 +124,39 @@ and then execute the task to resolve all dependencies in your project: You'll find the generated files in `build/dependency-graph-snapshots`. +### Determine the underlying source of dependencies + +After generating the dependency reports as described, it is possible to determine the dependency source by: + +1. Locate the dependency (including matching version) in the `dependency-resolution.json` file. +2. Inspect each `resolvedBy` entry for the `path` and `configuration` values. The `scope` value is unimportant in this context. +3. Use the built-in [dependencyInsight](https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#dependency_insights) task to determine exactly how the dependency was resolved. The `path` indicates the project where the task should be executed, and the `configuration` is an input to the task. + +For example, given the following from the `dependency-resolution.json` report: +``` + "dependency" : "com.google.guava:guava:32.1.3-jre", + "effectiveScope" : "Unknown", + "resolvedBy" : [ { + "path" : ":my-subproject", + "configuration" : "compileClasspath", + "scope" : "Unknown" + }, ... +``` + +You would run the command: +``` +./gradlew :my-subproject:dependencyInsight --configuration compileClasspath --dependency com.google.guava:guava:32.1.3-jre +``` + +#### Dealing with 'classpath' configuration + +If the configuration value in `dependency-resolution.json` is "classpath", or for some other reason the above instructions do not work, +it is possible to recostruct the full resolution path using the generated `dependency-graph.json` file. + +Search for the exact dependency version in `dependency-graph.json`, and you'll see an "id" entry for that dependency as well as one or more +"dependencies" entries. By tracing back through the dependencies you can determine the underlying source of the dependency. + + ## Building/Testing To build and test this plugin, run the following task: From 49393e6675686c0d10cf8868c6863a24ed691c83 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Wed, 31 Jan 2024 12:13:00 -0700 Subject: [PATCH 42/92] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4fa6760..18cc1ff 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Note that no dependency graph will be generated when configuration state is load | 8.0 - 8.0.2 | ✅ | :x: | | 8.1+ | ✅ | ✅ | -## Using the plugin standalone project +## Using the plugin to generate dependency reports As well as the `GitHubDependencyGraphPlugin`, which is tailored for use by the [gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) GitHub Action, this repository also provides the `SimpleDependencyGraphPlugin`, which generates dependency-graph outputs in simple text format. @@ -124,7 +124,7 @@ and then execute the task to resolve all dependencies in your project: You'll find the generated files in `build/dependency-graph-snapshots`. -### Determine the underlying source of dependencies +### Using dependency reports to determine the underlying source of a dependency After generating the dependency reports as described, it is possible to determine the dependency source by: From a438b651561cd8355565e41eb0a2a7a1b83cfc19 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 10:42:36 -0700 Subject: [PATCH 43/92] Bump to Gradle 8.6 --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1b5ee57..4baf5a1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a2da4ba435f6728b43554c5845f6f88f79589c3e0018c29ab33eb23bd781255b -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-rc-1-bin.zip +distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From c8c776e791fd989295b5602e9ef4d7fd7ffe7ee5 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 10:29:07 -0700 Subject: [PATCH 44/92] Log resolved dependencies and associated configuration Info-level logging will make it easier to determine the source of a dependency that appears in the graph. Fixes #113 --- .../extractor/DependencyExtractor.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index dcf1eae..6fec2c2 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -163,9 +163,10 @@ abstract class DependencyExtractor : val configurationName = details.configurationName if (!configurationFilter.include(rootPath, configurationName)) { - LOGGER.debug("Ignoring resolved configuration: $rootPath - $configurationName") + LOGGER.info("Excluding resolved configuration from dependency graph: $rootPath - $configurationName") return } + LOGGER.info("Including resolved configuration in dependency graph: $rootPath - $configurationName") val scope = dependencyScope(rootPath, configurationName) @@ -293,7 +294,12 @@ abstract class DependencyExtractor : private fun createRenderer(): DependencyGraphRenderer { LOGGER.lifecycle("Constructing renderer: ${getRendererClassName()}") - return Class.forName(getRendererClassName()).getDeclaredConstructor().newInstance() as DependencyGraphRenderer + val dependencyGraphRenderer = + Class.forName(getRendererClassName()).getDeclaredConstructor().newInstance() as DependencyGraphRenderer + if (LOGGER.isInfoEnabled) { + return LoggingDependencyGraphRenderer(dependencyGraphRenderer) + } + return dependencyGraphRenderer } private fun getOutputDir(): File { @@ -334,6 +340,22 @@ abstract class DependencyExtractor : } } + private class LoggingDependencyGraphRenderer(private val delegate: DependencyGraphRenderer) : DependencyGraphRenderer { + override fun outputDependencyGraph( + pluginParameters: PluginParameters, + buildLayout: BuildLayout, + resolvedConfigurations: List, + outputDirectory: File + ) { + for (configuration in resolvedConfigurations) { + for (dependency in configuration.allDependencies) { + LOGGER.info("Detected dependency '${dependency.id}': project = '${configuration.rootOrigin.path}', configuration = '${configuration.configurationName}'") + } + } + delegate.outputDependencyGraph(pluginParameters, buildLayout, resolvedConfigurations, outputDirectory) + } + } + companion object { private val LOGGER = Logging.getLogger(DependencyExtractor::class.java) } From 20a9fef9cc263cc368e832a1f66833ffe4b7c402 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 10:38:40 -0700 Subject: [PATCH 45/92] Fix assertions in plugin test --- .../dependencygraph/PluginDependencyExtractorTest.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy index a113679..0ee200c 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy @@ -186,5 +186,9 @@ class PluginDependencyExtractorTest extends BaseExtractorTest { dependencies: [] ] ]) + + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved(pluginDependencies) } } From a858e7eac3adc8d78f17a5f95f83d8647059ee4f Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 15:19:54 -0700 Subject: [PATCH 46/92] Exclude plugin marker dependencies from graph Plugin marker dependencies are an artifact of the way plugins are resolved in Gradle, and duplicate the actual plugin dependency. These add no value to the graph and should be excluded by default. Fixes #111 --- .../PluginDependencyExtractorTest.groovy | 40 ++----------------- ...ampleProjectDependencyExtractorTest.groovy | 1 - ...ingleProjectDependencyExtractorTest.groovy | 18 ++------- .../extractor/DependencyExtractor.kt | 27 ++++++++++++- 4 files changed, 34 insertions(+), 52 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy index 0ee200c..45dcd5f 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/PluginDependencyExtractorTest.groovy @@ -124,61 +124,29 @@ class PluginDependencyExtractorTest extends BaseExtractorTest { // Settings plugin Map pluginDependencies = settingsPluginsAreSupported() ? [ - "my.settings.plugin:my.settings.plugin.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: ["com.example:settingPlugin:1.0"] - ], "com.example:settingPlugin:1.0" : [ - relationship: "indirect", + relationship: "direct", dependencies: ["org.test:foo:1.0"] ], "org.test:foo:1.0" : [ relationship: "indirect", dependencies: [] - ], - - // The project plugins are resolved at build level without any transitive deps - "my.project.plugin1:my.project.plugin1.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: [] - ], - "my.project.plugin2:my.project.plugin2.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: [] - ] - ] - : [ - // The project plugins are resolved at build level without any transitive deps - "my.project.plugin1:my.project.plugin1.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: [] - ], - "my.project.plugin2:my.project.plugin2.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: [] ] ] + : [:] // Plugin 1 pluginDependencies.putAll([ - "my.project.plugin1:my.project.plugin1.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: ["com.example:plugin1:1.0"] - ], "com.example:plugin1:1.0" : [ - relationship: "indirect", + relationship: "direct", dependencies: [] ] ]) // Plugin 2 pluginDependencies.putAll([ - "my.project.plugin2:my.project.plugin2.gradle.plugin:1.0": [ - relationship: "direct", - dependencies: ["com.example:plugin2:1.0"] - ], "com.example:plugin2:1.0" : [ - relationship: "indirect", + relationship: "direct", dependencies: ["org.test:bar:1.0"] ], "org.test:bar:1.0" : [ diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy index a0fc398..b10f6a7 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy @@ -26,7 +26,6 @@ class SampleProjectDependencyExtractorTest extends BaseExtractorTest { def manifestDependencies = manifest.resolved [ // plugin dependencies - "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.12.6", "com.gradle:gradle-enterprise-gradle-plugin:3.12.6", "com.diffplug.spotless:spotless-plugin-gradle:4.5.1", "com.diffplug.durian:durian-core:1.2.0", diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SingleProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SingleProjectDependencyExtractorTest.groovy index e4ff0dd..bca3c1a 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SingleProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SingleProjectDependencyExtractorTest.groovy @@ -365,13 +365,8 @@ class SingleProjectDependencyExtractorTest extends BaseExtractorTest { def manifest = gitHubManifest() manifest.sourceFile == "settings.gradle" manifest.assertResolved([ - "my.project.plugin:my.project.plugin.gradle.plugin:1.0": [ - package_url : purlFor("my.project.plugin", "my.project.plugin.gradle.plugin", "1.0"), - dependencies: ["com.example:plugin:1.0"] - ], - "com.example:plugin:1.0" : [ - package_url : purlFor("com.example", "plugin", "1.0"), - relationship: "indirect" + "com.example:plugin:1.0": [ + package_url : purlFor("com.example", "plugin", "1.0") ] ]) } @@ -401,13 +396,8 @@ class SingleProjectDependencyExtractorTest extends BaseExtractorTest { def manifest = gitHubManifest() manifest.sourceFile == "settings.gradle" manifest.assertResolved([ - "my.settings.plugin:my.settings.plugin.gradle.plugin:1.0": [ - package_url : purlFor("my.settings.plugin", "my.settings.plugin.gradle.plugin", "1.0"), - dependencies: ["com.example:plugin:1.0"] - ], - "com.example:plugin:1.0" : [ - package_url : purlFor("com.example", "plugin", "1.0"), - relationship: "indirect" + "com.example:plugin:1.0": [ + package_url : purlFor("com.example", "plugin", "1.0") ] ]) } diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 6fec2c2..6e25527 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -1,5 +1,6 @@ package org.gradle.dependencygraph.extractor +import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.artifacts.result.ResolvedDependencyResult @@ -230,7 +231,31 @@ abstract class DependencyExtractor : } private fun getResolvedDependencies(component: ResolvedComponentResult): List { - return component.dependencies.filterIsInstance().map { it.selected }.filter { it != component } + return component.dependencies + .filterIsInstance() + .map { it.selected } + .mapNotNull { traversePluginMarker(it) } + .filter { it != component } + } + + /** + * If the rawComponent represents a plugin marker artifact, then use the target plugin dependency instead. + * For a plugin marker with no dependencies return null, so it can be filtered from the list. + */ + private fun traversePluginMarker(rawComponent: ResolvedComponentResult): ResolvedComponentResult? { + if (rawComponent.id is ModuleComponentIdentifier) { + val componentId = rawComponent.id as ModuleComponentIdentifier + if (componentId.module == componentId.group + ".gradle.plugin") { + if (rawComponent.dependencies.isEmpty()) { + return null + } + if (rawComponent.dependencies.size == 1) { + val pluginDep = rawComponent.dependencies.iterator().next() as? ResolvedDependencyResult + return pluginDep?.selected + } + } + } + return rawComponent } private fun createComponentNode(componentId: String, origin: DependencyOrigin, isDirectDependency: Boolean, component: ResolvedComponentResult, repositoryLookup: RepositoryUrlLookup): ResolvedDependency { From fc859201dd69814a0a093d3f71332ee9f374e516 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 15:21:24 -0700 Subject: [PATCH 47/92] Prepare for v1.2.1 release --- release/changes.md | 4 ++-- release/version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/changes.md b/release/changes.md index 0cf92cb..1f8f7fe 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,2 +1,2 @@ -- Improve failure reporting (#104) -- Assign dependency scopes based on include/exclude filters (#105) +- [NEW] Log each resolved dependency and associated configuration path (#113) +- [FIX] Exclude plugin marker dependencies from generated graph (#111) diff --git a/release/version.txt b/release/version.txt index 26aaba0..6085e94 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.2.0 +1.2.1 From 11a6f55ad989a039a8c4e914c0c52c4dcce12580 Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 15:34:28 -0700 Subject: [PATCH 48/92] Upgrade 'okio' dependency in plugin classpath The dependency 'com.squareup.okio:okio:3.0.0', brought in by the 'com.github.breadmoirai.github-release' plugin, has a known vulnerability. The vulnerability is fixed in v3.4.0. --- .github/workflows/gradle.yml | 1 - gradle/libs.versions.toml | 1 + plugin/build.gradle.kts | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ce8584b..c655e0a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -85,4 +85,3 @@ jobs: name: plugin-json path: build/reports/dependency-graph-snapshots/plugin-self-test.json if-no-files-found: error - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8205a18..8bfaba2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-modul apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.1" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.4.0" } ### Test dependencies diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 78050c4..47e3a83 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -5,6 +5,20 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.util.jar.JarFile +// Upgrade transitive dependencies in plugin classpath +buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + constraints { + // The plugin com.github.breadmoirai.github-release:2.5.2 has dependency on com.squareup.okio:okio:3.0.0 + // which has reported vulnerability CVE-2023-3635. Use a newer version. + classpath(libs.okio) + } + } +} + plugins { kotlin("jvm") version(libs.versions.kotlin) alias(libs.plugins.plugin.publish) From 871601a4124ae3cb7b3aa5a26d7e991b1e3e6b7f Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 15:35:51 -0700 Subject: [PATCH 49/92] Update dependency versions --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8bfaba2..d5a8c4e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,10 +18,10 @@ okio = { group = "com.squareup.okio", name = "okio", version = "3.4.0" } spock-core = { group = "org.spockframework", name = "spock-core", version.ref = "spock" } spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.ref = "spock" } junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.1" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.2" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.20" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.2.0" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.3.1" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } From 952d6c5eac21357a97a847b97968b053a973be6a Mon Sep 17 00:00:00 2001 From: daz Date: Sun, 11 Feb 2024 15:41:59 -0700 Subject: [PATCH 50/92] Fix plugin-self-test --- plugin-self-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-self-test b/plugin-self-test index 8ccf15e..16dd619 100755 --- a/plugin-self-test +++ b/plugin-self-test @@ -15,6 +15,6 @@ else -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN"\ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/gradle/dependency-graph-gradle-plugin/dependency-graph/snapshots \ + https://api.github.com/repos/gradle/github-dependency-graph-gradle-plugin/dependency-graph/snapshots \ -d @build/reports/dependency-graph-snapshots/plugin-self-test.json fi From 0576a40e098c5ee2ccac9d58f94e74e9dc00fc10 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 12 Feb 2024 15:51:09 -0700 Subject: [PATCH 51/92] Bump dependency versions --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5a8c4e..f00aa1b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-modul apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.1" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } -okio = { group = "com.squareup.okio", name = "okio", version = "3.4.0" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.8.0" } ### Test dependencies @@ -21,7 +21,7 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.2" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.20" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.3.1" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.3.2" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } From 5538f99e6a9245ffa8a15ab94be419a995a27f7e Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 12 Feb 2024 15:50:34 -0700 Subject: [PATCH 52/92] Only write dependency-graph on build success Implementation uses "RunWork" build operation on Gradle 8.+, and "buildFinished" on older versions. Fixes #115 --- .../dependencygraph/BaseExtractorTest.groovy | 10 ++++ .../DependencyExtractorConfigTest.groovy | 47 ++++++++++++++++--- .../AbstractDependencyExtractorPlugin.kt | 7 +-- .../extractor/DependencyExtractor.kt | 19 +++++++- .../DependencyExtractorBuildService.kt | 12 +++++ 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy index d3ba3cf..1cb2594 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/BaseExtractorTest.groovy @@ -99,6 +99,16 @@ abstract class BaseExtractorTest extends Specification { return result } + protected BuildResult runAndFail() { + return runAndFail("ForceDependencyResolutionPlugin_resolveAllDependencies") + } + + protected BuildResult runAndFail(String... names) { + executer.withTasks(names) + result = getExecuter().runWithFailure() + return result + } + @CompileDynamic protected void applyDependencyGraphPlugin() { File pluginJar = TEST_CONFIG.asFile("extractorPlugin.jar.path") diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy index 6d49b6a..713f389 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyExtractorConfigTest.groovy @@ -52,6 +52,17 @@ class DependencyExtractorConfigTest extends BaseExtractorTest { assert job.id == environmentVars.jobId } + def "fails gracefully if configuration values not set"() { + when: + def envVars = environmentVars.asEnvironmentMap() + envVars.remove("GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR") + executer.withEnvironmentVars(envVars) + def result = executer.runWithFailure() + + then: + result.output.contains("> The configuration parameter 'GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR' must be set") + } + @IgnoreIf({ // There is an issue where BuildService is closed too early in Gradle 8.0, // resulting in empty dependency graph. @@ -88,14 +99,38 @@ class DependencyExtractorConfigTest extends BaseExtractorTest { !dependencyGraphFile.exists() } - def "fails gracefully if configuration values not set"() { + def "does not generate dependency-graph on configuration failure"() { + given: + buildFile << """ + throw new RuntimeException("Failure during configuration") + """ + when: - def envVars = environmentVars.asEnvironmentMap() - envVars.remove("GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR") - executer.withEnvironmentVars(envVars) - def result = executer.runWithFailure() + def buildResult = runAndFail() then: - result.output.contains("> The configuration parameter 'GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR' must be set") + buildResult.output.contains("Gradle Build did not complete successfully: Dependency Graph file will not be generated.") + !dependencyGraphFile.exists() + } + + def "does not generate dependency-graph on task failure"() { + given: + buildFile << """ + tasks.register("taskThatSucceeds") { + doLast {} + } + tasks.register("taskThatFails") { + doLast { + throw new RuntimeException("Failure in task") + } + } + """ + + when: + def buildResult = runAndFail("taskThatSucceeds", "taskThatFails") + + then: + buildResult.output.contains("Gradle Build did not complete successfully: Dependency Graph file will not be generated.") + !dependencyGraphFile.exists() } } diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt index fba417c..843302f 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/AbstractDependencyExtractorPlugin.kt @@ -89,9 +89,10 @@ abstract class AbstractDependencyExtractorPlugin : Plugin { extractorServiceProvider: Provider ) { gradle.buildFinished { - extractorServiceProvider.get().close() - gradle.buildOperationListenerManager - .removeListener(extractorServiceProvider.get()) + val extractorService = extractorServiceProvider.get() + extractorService.handleBuildCompletion(it.failure) + extractorService.close() + gradle.buildOperationListenerManager.removeListener(extractorService) } } } diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt index 6e25527..da017b3 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractor.kt @@ -10,7 +10,7 @@ import org.gradle.api.logging.Logging import org.gradle.dependencygraph.DependencyGraphRenderer import org.gradle.dependencygraph.model.* import org.gradle.dependencygraph.model.DependencyScope.* -import org.gradle.dependencygraph.util.* +import org.gradle.dependencygraph.util.PluginParameters import org.gradle.initialization.EvaluateSettingsBuildOperationType import org.gradle.initialization.LoadProjectsBuildOperationType import org.gradle.internal.exceptions.DefaultMultiCauseException @@ -38,6 +38,8 @@ abstract class DependencyExtractor : private val pluginParameters = PluginParameters() private var settingsEvaluated = false + private var buildCompleted = false + private var buildFailed = false private val resolvedConfigurations = Collections.synchronizedList(mutableListOf()) @@ -70,6 +72,7 @@ abstract class DependencyExtractor : } override fun finished(buildOperation: BuildOperationDescriptor, finishEvent: OperationFinishEvent) { + handleBuildOperationType< ResolveConfigurationDependenciesBuildOperationType.Details, ResolveConfigurationDependenciesBuildOperationType.Result @@ -341,6 +344,13 @@ abstract class DependencyExtractor : ) } + fun handleBuildCompletion(failure: Throwable?) { + buildCompleted = true + if (failure != null) { + buildFailed = true + } + } + override fun close() { if (thrownExceptions.isNotEmpty()) { throw DefaultMultiCauseException( @@ -358,6 +368,13 @@ abstract class DependencyExtractor : ) return } + // Do not write an incomplete graph when build didn't complete successfully + if (!buildCompleted || buildFailed) { + LOGGER.lifecycle( + "Gradle Build did not complete successfully: Dependency Graph file will not be generated." + ) + return + } try { writeDependencyGraph() } catch (e: RuntimeException) { diff --git a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractorBuildService.kt b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractorBuildService.kt index d0448c1..a7e75fa 100644 --- a/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractorBuildService.kt +++ b/plugin/src/main/kotlin/org/gradle/dependencygraph/extractor/DependencyExtractorBuildService.kt @@ -3,6 +3,9 @@ package org.gradle.dependencygraph.extractor import org.gradle.api.provider.Property import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters +import org.gradle.internal.operations.BuildOperationCategory +import org.gradle.internal.operations.BuildOperationDescriptor +import org.gradle.internal.operations.OperationFinishEvent abstract class DependencyExtractorBuildService : DependencyExtractor(), @@ -16,4 +19,13 @@ abstract class DependencyExtractorBuildService : override fun getRendererClassName(): String { return parameters.rendererClassName.get() } + + override fun finished(buildOperation: BuildOperationDescriptor, finishEvent: OperationFinishEvent) { + super.finished(buildOperation, finishEvent) + + // Track build completion without 'buildFinished' + if (buildOperation.metadata == BuildOperationCategory.RUN_WORK) { + handleBuildCompletion(finishEvent.failure) + } + } } \ No newline at end of file From c78baf7459b89709195fb0a5e1aaef2658a5f848 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 12 Feb 2024 21:31:48 -0700 Subject: [PATCH 53/92] Prepare for v1.2.2 release --- release/changes.md | 3 +-- release/version.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/release/changes.md b/release/changes.md index 1f8f7fe..6dcc34a 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,2 +1 @@ -- [NEW] Log each resolved dependency and associated configuration path (#113) -- [FIX] Exclude plugin marker dependencies from generated graph (#111) +- [FIX] Only write dependency-graph on build success (#115) diff --git a/release/version.txt b/release/version.txt index 6085e94..23aa839 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.2.1 +1.2.2 From 5092596f53acadb97f87ef64ef3433625931c26f Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 21 Mar 2024 22:02:54 -0600 Subject: [PATCH 54/92] Update dependency versions --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f00aa1b..b87c28a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-modul apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.1" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } -okio = { group = "com.squareup.okio", name = "okio", version = "3.8.0" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.9.0" } ### Test dependencies @@ -20,8 +20,8 @@ spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.re junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.2" } -groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.20" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.3.2" } +groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.21" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } From a37a7f8fe3e9048aae36b6aecce344d47e12fef0 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 1 Apr 2024 14:09:56 -0600 Subject: [PATCH 55/92] Update to Gradle 8.7 --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 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 delta 34176 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 SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4baf5a1..381baa9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 0fabed67eccdbc84eccb47dbd315a185d4fcbb9d Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 1 Apr 2024 14:07:40 -0600 Subject: [PATCH 56/92] Apply the configuration filter for 'resolve-all-dependencies' Previously, excluded configurations would be excluded from the graph, but they would still be resolved. By avoiding resolution altogether, we allow users to exclude any unresolvable dependency configurations. Fixes #126 --- ...rationFilterDependencyExtractorTest.groovy | 39 +++++++++++++++++++ .../ForceDependencyResolutionPlugin.kt | 27 +++++++++---- .../LegacyResolveProjectDependenciesTask.kt | 9 ++++- .../ResolveProjectDependenciesTask.kt | 9 ++++- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy index b4da125..da06a4a 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/ConfigurationFilterDependencyExtractorTest.groovy @@ -284,4 +284,43 @@ class ConfigurationFilterDependencyExtractorTest extends BaseExtractorTest { ]) } + def "does not attempt to resolve excluded configurations"() { + given: + settingsFile << "include 'a', 'b'" + + buildFile << """ + project(':a') { + apply plugin: 'java-library' + dependencies { + api 'org.test:foo:1.0' + } + configurations.all { + incoming.beforeResolve { + throw new RuntimeException("Should not resolve project :a") + } + } + } + project(':b') { + apply plugin: 'java-library' + dependencies { + api 'org.test:bar:1.0' + testImplementation 'org.test:baz:1.0' + } + configurations.testCompileClasspath { + incoming.beforeResolve { + throw new RuntimeException("Should not resolve configuration 'testCompileClasspath'") + } + } + } + """ + + when: + executer.withArgument("-DDEPENDENCY_GRAPH_EXCLUDE_PROJECTS=:a") + executer.withArgument("-DDEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS=test(Compile|Runtime)Classpath") + run() + + then: + gitHubManifest().assertResolved(["org.test:bar:1.0"]) + } + } diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt index 8a4d174..eafecd9 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt @@ -5,6 +5,8 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.invocation.Gradle import org.gradle.api.tasks.TaskProvider +import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter +import org.gradle.dependencygraph.util.PluginParameters import org.gradle.util.GradleVersion private const val RESOLVE_PROJECT_TASK = "ForceDependencyResolutionPlugin_resolveProjectDependencies" @@ -14,6 +16,14 @@ private const val RESOLVE_ALL_TASK = "ForceDependencyResolutionPlugin_resolveAll * Adds a task to resolve all dependencies in a Gradle build tree. */ class ForceDependencyResolutionPlugin : Plugin { + + // Properties are lazily initialized so that System Properties are initialized by the time + // the values are used. This is required due to a bug in older Gradle versions. (https://github.com/gradle/gradle/issues/6825) + private val pluginParameters = PluginParameters() + private val configurationFilter by lazy { + ResolvedConfigurationFilter(pluginParameters) + } + override fun apply(gradle: Gradle) { gradle.projectsEvaluated { val resolveAllDeps = gradle.rootProject.tasks.register(RESOLVE_ALL_TASK) @@ -21,7 +31,7 @@ class ForceDependencyResolutionPlugin : Plugin { // Depend on "dependencies" task in all projects gradle.allprojects { project -> val projectTaskFactory = getResolveProjectDependenciesTaskFactory() - val resolveProjectDeps = projectTaskFactory.create(project) + val resolveProjectDeps = projectTaskFactory.create(project, configurationFilter) resolveAllDeps.configure { it.dependsOn(resolveProjectDeps) } @@ -47,19 +57,22 @@ class ForceDependencyResolutionPlugin : Plugin { } private interface ResolveProjectDependenciesTaskFactory { - fun create(project: Project): TaskProvider + fun create(project: Project, filter: ResolvedConfigurationFilter): TaskProvider object Current : ResolveProjectDependenciesTaskFactory { - override fun create(project: Project): TaskProvider { - return project.tasks.register(RESOLVE_PROJECT_TASK, ResolveProjectDependenciesTask::class.java) + override fun create(project: Project, filter: ResolvedConfigurationFilter): TaskProvider { + return project.tasks.register(RESOLVE_PROJECT_TASK, ResolveProjectDependenciesTask::class.java) { + it.configurationFilter = filter + } } } object Legacy : ResolveProjectDependenciesTaskFactory { - override fun create(project: Project): TaskProvider { - return project.tasks.register(RESOLVE_PROJECT_TASK, LegacyResolveProjectDependenciesTask::class.java) + override fun create(project: Project, filter: ResolvedConfigurationFilter): TaskProvider { + return project.tasks.register(RESOLVE_PROJECT_TASK, LegacyResolveProjectDependenciesTask::class.java) { + it.configurationFilter = filter + } } } } - } \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt index af6c45a..f08bc39 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt @@ -2,13 +2,20 @@ package org.gradle.forceresolve import org.gradle.api.DefaultTask import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction +import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Not worth caching") abstract class LegacyResolveProjectDependenciesTask: DefaultTask() { + @Internal + var configurationFilter: ResolvedConfigurationFilter? = null + private fun getReportableConfigurations(): List { - return project.configurations.filter { it.isCanBeResolved } + return project.configurations.filter { + it.isCanBeResolved && configurationFilter!!.include(project.path, it.name) + } } @TaskAction diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt index 4fc0661..5c486ed 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt @@ -4,7 +4,9 @@ import org.gradle.api.DefaultTask import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction +import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter import org.gradle.internal.serialization.Cached import org.gradle.work.DisableCachingByDefault @@ -12,6 +14,9 @@ import org.gradle.work.DisableCachingByDefault abstract class ResolveProjectDependenciesTask: DefaultTask() { private val configurationResolvers = Cached.of { createConfigurationResolvers() } + @Internal + var configurationFilter: ResolvedConfigurationFilter? = null + private fun createConfigurationResolvers(): List> { return getReportableConfigurations().map { it.incoming.resolutionResult.rootComponent @@ -19,7 +24,9 @@ abstract class ResolveProjectDependenciesTask: DefaultTask() { } private fun getReportableConfigurations(): List { - return project.configurations.filter { it.isCanBeResolved } + return project.configurations.filter { + it.isCanBeResolved && configurationFilter!!.include(project.path, it.name) + } } @TaskAction From 0b582109218d2a20bceb633fe73ef85fb8ffe139 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 1 Apr 2024 17:16:23 -0600 Subject: [PATCH 57/92] Bump dependency version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b87c28a..a57b25d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.15.1" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.16.0" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } okio = { group = "com.squareup.okio", name = "okio", version = "3.9.0" } From 1d027c15ac323108e8355245c696ca9756f22ac4 Mon Sep 17 00:00:00 2001 From: daz Date: Mon, 1 Apr 2024 17:17:29 -0600 Subject: [PATCH 58/92] Prepare for v1.3.0 release --- release/changes.md | 2 +- release/version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/changes.md b/release/changes.md index 6dcc34a..9f1af10 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1 +1 @@ -- [FIX] Only write dependency-graph on build success (#115) +- [FIX] Avoid resolving configurations that are excluded from graph (#126) diff --git a/release/version.txt b/release/version.txt index 23aa839..f0bb29e 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.2.2 +1.3.0 From dc07592a917ba025659f3d1c1373519b7d2dc478 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Fri, 5 Apr 2024 16:49:02 -0600 Subject: [PATCH 59/92] Add section on dependency verification --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 18cc1ff..de76ae1 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,19 @@ Note that no dependency graph will be generated when configuration state is load | 8.0 - 8.0.2 | ✅ | :x: | | 8.1+ | ✅ | ✅ | +### Dependency verification + +When using this plugin with [dependency signature verification enabled](https://docs.gradle.org/current/userguide/dependency_verification.html#sec:signature-verification), +the you should be able to update your `dependency-verification.xml` file using `--write-verification-metadata pgp,sha256`. + +However, if this doesn't work, you can add the following to your `dependency-verificaton.xml` file: + +``` + + + +``` + ## Using the plugin to generate dependency reports As well as the `GitHubDependencyGraphPlugin`, which is tailored for use by the [gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) GitHub Action, this repository also provides the `SimpleDependencyGraphPlugin`, which generates dependency-graph outputs in simple text format. @@ -172,3 +185,4 @@ To self-test this plugin and generate a dependency graph for this repository, ru The generated dependency graph will be submitted to GitHub only if you supply a [GitHub API token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) via the environment variable `GITHUB_TOKEN`. + From 68164b84d92e02ba900c8a69666728607e21d741 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Sat, 6 Apr 2024 12:48:57 -0600 Subject: [PATCH 60/92] Avoid resolving configurations that are deprecated for resolving Certain configurations can be reported as "Deprecated for resolving", which is detectable via the`DeprecatableConfiguration.canSafelyBeResolved` internal API. Resolving these configurations during dependency graph generationg can be problematic, particularly in the case of strict Dependency Locking, since no lockfile is likely to be generated for these configurations. With this fix, reflection is used to attempt to determine if a Configuration can be safely resolved, avoiding resolution of deprecated configurations. Fixes #128 --- .github/workflows/gradle.yml | 2 +- ...dencyLockingDependencyExtractorTest.groovy | 99 +++++++++++++++++++ .../AbstractResolveProjectDependenciesTask.kt | 43 ++++++++ .../LegacyResolveProjectDependenciesTask.kt | 14 +-- .../ResolveProjectDependenciesTask.kt | 15 +-- 5 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyLockingDependencyExtractorTest.groovy create mode 100644 plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c655e0a..beda58d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -33,7 +33,7 @@ jobs: matrix: # Test earliest and latest supported version of 5.x, 6.x and 7.x, as well as all patched minor versions of 8.x # Latest 8.x is tested in 'quick-check' job using the wrapper - gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.3", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5"] + gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.4", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5", "8.6"] runs-on: ubuntu-latest env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyLockingDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyLockingDependencyExtractorTest.groovy new file mode 100644 index 0000000..ba04b1c --- /dev/null +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/DependencyLockingDependencyExtractorTest.groovy @@ -0,0 +1,99 @@ +package org.gradle.github.dependencygraph + +import org.gradle.test.fixtures.PluginPublisher +import org.gradle.test.fixtures.maven.MavenModule +import org.gradle.util.GradleVersion +import spock.lang.IgnoreIf + +class DependencyLockingDependencyExtractorTest extends BaseExtractorTest { + private MavenModule foo + private MavenModule bar + private MavenModule baz + private File settingsFile + private File buildFile + + def setup() { + establishEnvironmentVariables() + + foo = mavenRepo.module("org.test", "foo", "1.0").publish() + + settingsFile = file("settings.gradle") << """ + rootProject.name = 'a' + """ + + buildFile = file("build.gradle") << """ + apply plugin: 'java' + + repositories { + maven { url "${mavenRepo.uri}" } + } + """ + } + + def "extracts dependencies when dependency locking is enabled"() { + given: + buildFile << """ + dependencies { + implementation "org.test:foo:+" + } + + dependencyLocking { + lockAllConfigurations() + } + """ + + // Write dependency lock file + run("dependencies", "--write-locks") + mavenRepo.module("org.test", "foo", "1.1").publish() + + when: + applyDependencyGraphPlugin() + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + + manifest.assertResolved([ + "org.test:foo:1.0": [ + package_url: purlFor(foo) + ] + ]) + } + + @IgnoreIf({ + // `LockMode.STRICT` was introduced in Gradle 6.1 + GradleVersion.version(testGradleVersion) < GradleVersion.version("6.1") + }) + def "extracts dependencies when Strict dependency locking is enabled"() { + given: + buildFile << """ + dependencies { + implementation "org.test:foo:+" + } + + dependencyLocking { + lockAllConfigurations() + lockMode = LockMode.STRICT + } + """ + + // Write dependency lock file + run("dependencies", "--write-locks") + mavenRepo.module("org.test", "foo", "1.1").publish() + + when: + applyDependencyGraphPlugin() + run() + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + + manifest.assertResolved([ + "org.test:foo:1.0": [ + package_url: purlFor(foo) + ] + ]) + } +} diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt new file mode 100644 index 0000000..224c104 --- /dev/null +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt @@ -0,0 +1,43 @@ +package org.gradle.forceresolve + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.Internal +import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter +import org.gradle.work.DisableCachingByDefault +import java.lang.reflect.Method + +@DisableCachingByDefault(because = "Not worth caching") +abstract class AbstractResolveProjectDependenciesTask : DefaultTask() { + private val canSafelyBeResolvedMethod: Method? = getCanSafelyBeResolvedMethod() + + @Internal + var configurationFilter: ResolvedConfigurationFilter? = null + + @Internal + protected fun getReportableConfigurations(): List { + return project.configurations.filter { + canSafelyBeResolved(it) && configurationFilter!!.include(project.path, it.name) + } + } + + /** + * If `DeprecatableConfiguration.canSafelyBeResolve()` is available, use it. + * Else fall back to `Configuration.canBeResolved`. + */ + private fun canSafelyBeResolved(configuration: Configuration): Boolean { + if (canSafelyBeResolvedMethod != null) { + return canSafelyBeResolvedMethod.invoke(configuration) as Boolean + } + return configuration.isCanBeResolved + } + + private fun getCanSafelyBeResolvedMethod(): Method? { + return try { + val dc = Class.forName("org.gradle.internal.deprecation.DeprecatableConfiguration") + dc.getMethod("canSafelyBeResolved") + } catch (e: ReflectiveOperationException) { + null + } + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt index f08bc39..85e1414 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt @@ -1,22 +1,10 @@ package org.gradle.forceresolve -import org.gradle.api.DefaultTask -import org.gradle.api.artifacts.Configuration -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction -import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Not worth caching") -abstract class LegacyResolveProjectDependenciesTask: DefaultTask() { - @Internal - var configurationFilter: ResolvedConfigurationFilter? = null - - private fun getReportableConfigurations(): List { - return project.configurations.filter { - it.isCanBeResolved && configurationFilter!!.include(project.path, it.name) - } - } +abstract class LegacyResolveProjectDependenciesTask: AbstractResolveProjectDependenciesTask() { @TaskAction fun action() { diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt index 5c486ed..6d7ef07 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt @@ -1,34 +1,21 @@ package org.gradle.forceresolve -import org.gradle.api.DefaultTask -import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction -import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter import org.gradle.internal.serialization.Cached import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Not worth caching") -abstract class ResolveProjectDependenciesTask: DefaultTask() { +abstract class ResolveProjectDependenciesTask: AbstractResolveProjectDependenciesTask() { private val configurationResolvers = Cached.of { createConfigurationResolvers() } - @Internal - var configurationFilter: ResolvedConfigurationFilter? = null - private fun createConfigurationResolvers(): List> { return getReportableConfigurations().map { it.incoming.resolutionResult.rootComponent } } - private fun getReportableConfigurations(): List { - return project.configurations.filter { - it.isCanBeResolved && configurationFilter!!.include(project.path, it.name) - } - } - @TaskAction fun action() { for (configuration in configurationResolvers.get()) { From b8a6480a771fe8995b66f1d8adb78ea2ccb36f31 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Thu, 18 Apr 2024 13:25:26 -0600 Subject: [PATCH 61/92] Mention absolute path requirement for parameters --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de76ae1..32b5a0e 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ The following environment variables configure the snapshot generated by the `Git - `GITHUB_DEPENDENCY_GRAPH_JOB_ID`: Sets the `job.id` value for the dependency submission - `GITHUB_DEPENDENCY_GRAPH_REF`: Sets the `ref` value for the commit that generated the dependency graph - `GITHUB_DEPENDENCY_GRAPH_SHA`: Sets the `sha` value for the commit that generated the dependency graph -- `GITHUB_DEPENDENCY_GRAPH_WORKSPACE`: Sets the root directory of the github repository -- `DEPENDENCY_GRAPH_REPORT_DIR` (optional): Specifies where the dependency graph report will be generated +- `GITHUB_DEPENDENCY_GRAPH_WORKSPACE`: Sets the root directory of the github repository. Must be an absolute path. +- `DEPENDENCY_GRAPH_REPORT_DIR` (optional): Specifies where the dependency graph report will be generated. Must be an absolute path. Each of these values can also be provided via a system property. eg: Env var `DEPENDENCY_GRAPH_REPORT_DIR` can be set with `-DDEPENDENCY_GRAPH_REPORT_DIR=...` on the command-line. From caaf698d07bcde5ceabef838b3a9be869f64347e Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Sun, 28 Apr 2024 08:42:37 -0600 Subject: [PATCH 62/92] Update README.md - Fix typo - Change to link to gradle/actions/dependency-submission --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 32b5a0e..4773caf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ A Gradle plugin for generating a GitHub dependency graph for a Gradle build, which can be uploaded to the [GitHub Dependency Submission API](https://docs.github.com/en/rest/dependency-graph/dependency-submission). ## Usage -This plugin is designed to be used in a GitHub Actions workflow, an is tightly integrated into the [Gradle Build Action](https://github.com/gradle/gradle-build-action#github-dependency-graph-support). +This plugin is designed to be used in a GitHub Actions workflow, and is tightly integrated into the +[gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) action. For other uses, the [core plugin](https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin) (`org.gradle.github.GitHubDependencyGraphPlugin`) should be applied to the `Gradle` instance via a Gradle init script as follows: From 11c773de7903b0eab77febf6849c2455c303be9f Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sun, 28 Apr 2024 15:49:17 +0100 Subject: [PATCH 63/92] Add missing syntax highlighting in README.md (#136) --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4773caf..a2b7677 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This plugin is designed to be used in a GitHub Actions workflow, and is tightly For other uses, the [core plugin](https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin) (`org.gradle.github.GitHubDependencyGraphPlugin`) should be applied to the `Gradle` instance via a Gradle init script as follows: -``` +```groovy import org.gradle.github.GitHubDependencyGraphPlugin initscript { repositories { @@ -106,7 +106,7 @@ the you should be able to update your `dependency-verification.xml` file using ` However, if this doesn't work, you can add the following to your `dependency-verificaton.xml` file: -``` +```xml @@ -132,7 +132,7 @@ apply plugin: org.gradle.dependencygraph.simple.SimpleDependencyGraphPlugin and then execute the task to resolve all dependencies in your project: -``` +```shell ./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-configure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies ``` @@ -147,7 +147,7 @@ After generating the dependency reports as described, it is possible to determin 3. Use the built-in [dependencyInsight](https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#dependency_insights) task to determine exactly how the dependency was resolved. The `path` indicates the project where the task should be executed, and the `configuration` is an input to the task. For example, given the following from the `dependency-resolution.json` report: -``` +```json "dependency" : "com.google.guava:guava:32.1.3-jre", "effectiveScope" : "Unknown", "resolvedBy" : [ { @@ -158,7 +158,7 @@ For example, given the following from the `dependency-resolution.json` report: ``` You would run the command: -``` +```shell ./gradlew :my-subproject:dependencyInsight --configuration compileClasspath --dependency com.google.guava:guava:32.1.3-jre ``` From 321383e803910581d412d92e05f6496173cea82c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 07:46:25 -0600 Subject: [PATCH 64/92] Bump commons-io:commons-io from 2.16.0 to 2.16.1 (#130) Bumps commons-io:commons-io from 2.16.0 to 2.16.1. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=commons-io:commons-io&package-manager=gradle&previous-version=2.16.0&new-version=2.16.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a57b25d..ec7c59a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.16.0" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.16.1" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } okio = { group = "com.squareup.okio", name = "okio", version = "3.9.0" } From efe92a0eeb45388500c130e1b90288c3ae5ca7db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 21:33:37 +0000 Subject: [PATCH 65/92] --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ec7c59a..5262c0e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.21" } json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } -google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" } +google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } [plugins] shadow-jar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"} From 219e762e7dd03de598ad3c829f835916a6422fb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 22:01:53 +0000 Subject: [PATCH 66/92] Bump com.networknt:json-schema-validator from 1.4.0 to 1.4.2 Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.4.0 to 1.4.2. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.4.0...1.4.2) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5262c0e..d9d1ab6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.2" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.21" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.0" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.2" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From 52097231394b16406def60f2948eec579a7f51c7 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Thu, 27 Jun 2024 17:17:50 -0600 Subject: [PATCH 67/92] Avoid circular task dependency when build includes itself (#143) Fixes #141 --- ...MultiProjectDependencyExtractorTest.groovy | 30 +++++++++++++++++++ .../ForceDependencyResolutionPlugin.kt | 6 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy index a9d70f7..cb6f0e3 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/MultiProjectDependencyExtractorTest.groovy @@ -2,6 +2,8 @@ package org.gradle.github.dependencygraph import org.gradle.test.fixtures.maven.MavenModule +import org.gradle.util.GradleVersion +import spock.lang.IgnoreIf class MultiProjectDependencyExtractorTest extends BaseExtractorTest { private MavenModule foo @@ -250,4 +252,32 @@ class MultiProjectDependencyExtractorTest extends BaseExtractorTest { "org.test:foo:1.0": [package_url: purlFor(foo)] ]) } + + @IgnoreIf({ + // `includeBuild('.')` is not possible with Gradle < 6.1 + GradleVersion.version(testGradleVersion) < GradleVersion.version("6.1") + }) + def "extracts dependencies from build that includes itself"() { + given: + settingsFile << """ + includeBuild('.') +""" + buildFile << """ + apply plugin: 'java' + dependencies { + implementation 'org.test:bar:1.0' + } + +""" + + when: + run(":ForceDependencyResolutionPlugin_resolveAllDependencies") + + then: + def manifest = gitHubManifest() + manifest.sourceFile == "settings.gradle" + manifest.assertResolved([ + "org.test:bar:1.0": [package_url: purlFor(bar)] + ]) + } } diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt index eafecd9..28cb07c 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/ForceDependencyResolutionPlugin.kt @@ -39,8 +39,10 @@ class ForceDependencyResolutionPlugin : Plugin { // Depend on all 'resolveBuildDependencies' task in each included build gradle.includedBuilds.forEach { includedBuild -> - resolveAllDeps.configure { - it.dependsOn(includedBuild.task(":$RESOLVE_ALL_TASK")) + if (includedBuild.projectDir != gradle.rootProject.projectDir) { + resolveAllDeps.configure { + it.dependsOn(includedBuild.task(":$RESOLVE_ALL_TASK")) + } } } } From 1aa17f27d5ce2c5bfc99d1033ee3e9472932a8a3 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Thu, 27 Jun 2024 18:19:20 -0600 Subject: [PATCH 68/92] Improve test coverage matrix (#144) - Avoid testing all intermediate 8.x releases - Test latest Gradle with different JVMs --- .github/workflows/gradle.yml | 28 ++++++++++++++++++++++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index beda58d..b555144 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -31,9 +31,9 @@ jobs: strategy: fail-fast: false matrix: - # Test earliest and latest supported version of 5.x, 6.x and 7.x, as well as all patched minor versions of 8.x + # Test earliest and latest supported version of 5.x, 6.x, 7.x, and 8.x # Latest 8.x is tested in 'quick-check' job using the wrapper - gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.4", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5", "8.6"] + gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.4", "8.0.2", "8.8"] runs-on: ubuntu-latest env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} @@ -52,6 +52,30 @@ jobs: - name: Execute Gradle Build run: ./gradlew -S build -DtestGradleVersion=${{ matrix.gradle-version }} + test-jvm-version: + needs: quick-check + strategy: + fail-fast: false + matrix: + jvm-version: [ "8", "11", "17", "21", "22"] + runs-on: ubuntu-latest + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.jvm-version }} + distribution: temurin + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Execute Gradle Build + run: ./gradlew -S build -DtestGradleVersion=8.8 + self-test: needs: quick-check runs-on: ubuntu-latest diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 381baa9..8a1f6b9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..b740cf1 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From f99d4c8d1e5e6711c0b30dc855c103307271a9c8 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 27 Jun 2024 18:42:41 -0600 Subject: [PATCH 69/92] Update for v1.3.1 release --- release/changes.md | 3 ++- release/version.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/release/changes.md b/release/changes.md index 9f1af10..925bc10 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1 +1,2 @@ -- [FIX] Avoid resolving configurations that are excluded from graph (#126) +- [FIX] Avoid resolving configurations that are deprecated for resolving (#129) +- [FIX] Avoid circular task dependency when build includes itself (#141) \ No newline at end of file diff --git a/release/version.txt b/release/version.txt index f0bb29e..3a3cd8c 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.3.0 +1.3.1 From 5ab4a8c2087ff67cba7c4d1af5cab88973376d96 Mon Sep 17 00:00:00 2001 From: daz Date: Thu, 27 Jun 2024 19:04:50 -0600 Subject: [PATCH 70/92] Add release instructions to repo --- RELEASING.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 RELEASING.md diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..0b97aaf --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,30 @@ +# Release process for the GitHub Dependency Graph Gradle Plugin + +## Preparation +- Push any outstanding changes to branch main. +- Check that https://github.com/gradle/github-dependency-graph-gradle-plugin/actions is green for all workflows for the main branch. +- Decide on the version number to use for the release. The plugin releases should follow semantic versioning. + - By default, a patch release is assumed (eg. `1.3.0` → `1.3.1`) + - If new features have been added, bump the minor version (eg `1.3.1` → `1.4.0`) + - If a new major release is required, bump the major version (eg `1.3.1` → `2.0.0`) + - Note: The plugin releases follow the semantic version convention of including a .0 patch number for the first release of a minor version, unlike the Gradle convention which omits the trailing .0. +- Update `release/version.txt` with the to-be-published version +- Ensure that `release/changes.md` contains all changes that should be included in the release notes + +## Run the staging release workflow and verify the published plugin +- Publish to staging by executing the [staging release workflow](https://github.com/gradle/dv-solutions/actions/workflows/github-dependency-graph-gradle-plugin-staging-release.yml) job. + - Run the workflow from the `main` branch and enter "STAGING" when requested. + - Once the workflow is complete, check that it was published successfully at https://plugins.grdev.net/plugin/org.gradle.github-dependency-graph-gradle-plugin +- If necessary, run a build that resolves and tests the newly published plugin + +## Run the production release workflow and verify the published plugin +- Publish to production by executing the [production release workflow](https://github.com/gradle/dv-solutions/actions/workflows/github-dependency-graph-gradle-plugin-production-release.yml) job. + - Run the workflow from the `main` branch and enter "PRODUCTION" when requested. +- After the workflow completes, check the outputs: + - Verify that the plugin is available at https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin + - If necessary, run a build that resolves and tests the newly published plugin + - Check that the GitHub release was generated at https://github.com/gradle/github-dependency-graph-gradle-plugin/releases + +## Update `gradle/actions` to use the newly published release +- Update the default value for `dependency-graph-plugin.version` [here](https://github.com/gradle/actions/blob/main/sources/src/resources/init-scripts/gradle-actions.github-dependency-graph-gradle-plugin-apply.groovy#L9), to reference the new release. + - Submit a PR with this change. From 827e7770f390d2c562aeeaf5ebe9b6ce745d4685 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Fri, 28 Jun 2024 18:15:56 +0200 Subject: [PATCH 71/92] Trivially fix a typo in `canSafelyBeResolved` function docs (#145) --- .../forceresolve/AbstractResolveProjectDependenciesTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt b/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt index 224c104..395f0fc 100644 --- a/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt +++ b/plugin/src/main/kotlin/org/gradle/forceresolve/AbstractResolveProjectDependenciesTask.kt @@ -22,7 +22,7 @@ abstract class AbstractResolveProjectDependenciesTask : DefaultTask() { } /** - * If `DeprecatableConfiguration.canSafelyBeResolve()` is available, use it. + * If `DeprecatableConfiguration.canSafelyBeResolved()` is available, use it. * Else fall back to `Configuration.canBeResolved`. */ private fun canSafelyBeResolved(configuration: Configuration): Boolean { From 7a39b7b2451b0a73458d12b7517d3c21dd99c543 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Tue, 2 Jul 2024 11:12:25 -0600 Subject: [PATCH 72/92] Update dependabot.yml Group Dependabot updates for Gradle dependencies --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 54e07fd..40fe314 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,3 +16,8 @@ updates: - gradle-plugin-portal schedule: interval: "weekly" + groups: + gradle-dependencies: + patterns: + - "*" + From df79076247fb191cda5700bee6011b418a1f88b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:17:17 +0000 Subject: [PATCH 73/92] Bump the gradle-dependencies group across 1 directory with 3 updates Bumps the gradle-dependencies group with 3 updates in the / directory: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5), [org.codehaus.groovy:groovy-json](https://github.com/apache/groovy) and [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator). Updates `org.junit.jupiter:junit-jupiter` from 5.10.2 to 5.10.3 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.2...r5.10.3) Updates `org.codehaus.groovy:groovy-json` from 3.0.21 to 3.0.22 - [Commits](https://github.com/apache/groovy/commits) Updates `com.networknt:json-schema-validator` from 1.4.2 to 1.4.3 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.4.2...1.4.3) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: org.codehaus.groovy:groovy-json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d9d1ab6..27e3d05 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,10 +18,10 @@ okio = { group = "com.squareup.okio", name = "okio", version = "3.9.0" } spock-core = { group = "org.spockframework", name = "spock-core", version.ref = "spock" } spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.ref = "spock" } junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.2" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.3" } -groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.21" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.2" } +groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.22" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.3" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From 5cf68b74c76e39c54cf74497028e13318f986a01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 21:09:56 +0000 Subject: [PATCH 74/92] Bump com.networknt:json-schema-validator Bumps the gradle-dependencies group with 1 update: [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator). Updates `com.networknt:json-schema-validator` from 1.4.3 to 1.5.0 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.4.3...1.5.0) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27e3d05..0826244 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.3" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.22" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.4.3" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From d60fb8e7734377a55167ed389a5fddee4281d7e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:56:49 +0000 Subject: [PATCH 75/92] Bump com.networknt:json-schema-validator Bumps the gradle-dependencies group with 1 update: [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator). Updates `com.networknt:json-schema-validator` from 1.5.0 to 1.5.1 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.0...1.5.1) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0826244..7444c25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.3" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.22" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.0" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.1" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From b48ed39a2175f6591568a93b0b12ce24430db984 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 14:06:14 +0000 Subject: [PATCH 76/92] Bump gradle/actions from 3 to 4 Bumps [gradle/actions](https://github.com/gradle/actions) from 3 to 4. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/v3...v4) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/gradle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b555144..469d0a7 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -21,7 +21,7 @@ jobs: distribution: temurin - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Execute Gradle Build run: ./gradlew build @@ -47,7 +47,7 @@ jobs: distribution: temurin - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Execute Gradle Build run: ./gradlew -S build -DtestGradleVersion=${{ matrix.gradle-version }} @@ -71,7 +71,7 @@ jobs: distribution: temurin - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Execute Gradle Build run: ./gradlew -S build -DtestGradleVersion=8.8 @@ -91,7 +91,7 @@ jobs: distribution: temurin - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Self Test :plugin run: ./plugin-self-test ForceDependencyResolutionPlugin_resolveAllDependencies From 6cefb5d0e938f7601d239c4803ab5a1634bf5e1f Mon Sep 17 00:00:00 2001 From: daz Date: Wed, 30 Oct 2024 16:21:13 -0600 Subject: [PATCH 77/92] Switch to Develocity plugin --- .github/workflows/gradle.yml | 76 +++++++++---------- ...ampleProjectDependencyExtractorTest.groovy | 2 +- .../java-single-project/settings.gradle | 2 +- settings.gradle.kts | 10 +-- 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 469d0a7..23c46f4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,8 +9,6 @@ on: jobs: quick-check: runs-on: ubuntu-latest - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} steps: - uses: actions/checkout@v4 @@ -22,6 +20,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + with: + develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - name: Execute Gradle Build run: ./gradlew build @@ -35,8 +35,6 @@ jobs: # Latest 8.x is tested in 'quick-check' job using the wrapper gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.4", "8.0.2", "8.8"] runs-on: ubuntu-latest - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} steps: - uses: actions/checkout@v4 @@ -48,6 +46,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + with: + develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - name: Execute Gradle Build run: ./gradlew -S build -DtestGradleVersion=${{ matrix.gradle-version }} @@ -59,8 +59,6 @@ jobs: matrix: jvm-version: [ "8", "11", "17", "21", "22"] runs-on: ubuntu-latest - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} steps: - uses: actions/checkout@v4 @@ -72,40 +70,42 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + with: + develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - name: Execute Gradle Build run: ./gradlew -S build -DtestGradleVersion=8.8 self-test: - needs: quick-check - runs-on: ubuntu-latest - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - java-version: 11 - distribution: temurin - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Self Test :plugin - run: ./plugin-self-test ForceDependencyResolutionPlugin_resolveAllDependencies - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_DEPENDENCY_GRAPH_JOB_ID: ${{ github.run_id }} - GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "plugin-self-test" - GITHUB_DEPENDENCY_GRAPH_REF: ${{ github.ref }} - GITHUB_DEPENDENCY_GRAPH_SHA: ${{ github.sha }} - GITHUB_DEPENDENCY_GRAPH_WORKSPACE: ${{ github.workspace }} - - - name: Save plugin JSON report - uses: actions/upload-artifact@v4 - with: - name: plugin-json - path: build/reports/dependency-graph-snapshots/plugin-self-test.json - if-no-files-found: error + needs: quick-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 11 + distribution: temurin + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + + - name: Self Test :plugin + run: ./plugin-self-test ForceDependencyResolutionPlugin_resolveAllDependencies + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_DEPENDENCY_GRAPH_JOB_ID: ${{ github.run_id }} + GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "plugin-self-test" + GITHUB_DEPENDENCY_GRAPH_REF: ${{ github.ref }} + GITHUB_DEPENDENCY_GRAPH_SHA: ${{ github.sha }} + GITHUB_DEPENDENCY_GRAPH_WORKSPACE: ${{ github.workspace }} + + - name: Save plugin JSON report + uses: actions/upload-artifact@v4 + with: + name: plugin-json + path: build/reports/dependency-graph-snapshots/plugin-self-test.json + if-no-files-found: error diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy index b10f6a7..0d37538 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy @@ -26,7 +26,7 @@ class SampleProjectDependencyExtractorTest extends BaseExtractorTest { def manifestDependencies = manifest.resolved [ // plugin dependencies - "com.gradle:gradle-enterprise-gradle-plugin:3.12.6", + "com.gradle:develocity-gradle-plugin:3.18.1", "com.diffplug.spotless:spotless-plugin-gradle:4.5.1", "com.diffplug.durian:durian-core:1.2.0", ].forEach { diff --git a/sample-projects/java-single-project/settings.gradle b/sample-projects/java-single-project/settings.gradle index 1c90fdb..e00a4ae 100644 --- a/sample-projects/java-single-project/settings.gradle +++ b/sample-projects/java-single-project/settings.gradle @@ -1,4 +1,4 @@ plugins { - id 'com.gradle.enterprise' version '3.12.6' + id 'com.gradle.develocity' version '3.18.1' } rootProject.name = 'java-lib' diff --git a/settings.gradle.kts b/settings.gradle.kts index 730314d..8bf9287 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,16 +1,14 @@ plugins { - id("com.gradle.enterprise").version("3.13") - id("com.gradle.common-custom-user-data-gradle-plugin").version("1.10") + id("com.gradle.develocity").version("3.18.1") + id("com.gradle.common-custom-user-data-gradle-plugin").version("2.0.2") } val isCI = !System.getenv("CI").isNullOrEmpty() -gradleEnterprise { +develocity { server = "https://ge.gradle.org" buildScan { - publishAlways() - capture { isTaskInputFiles = true } - isUploadInBackground = !isCI + uploadInBackground = !isCI obfuscation { ipAddresses { addresses -> addresses.map { _ -> "0.0.0.0" } } From 973ccb8013916845f678076b5a58e93687674d19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:49:58 +0000 Subject: [PATCH 78/92] Bump the gradle-dependencies group across 1 directory with 6 updates Bumps the gradle-dependencies group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | commons-io:commons-io | `2.16.1` | `2.17.0` | | [com.squareup.okio:okio](https://github.com/square/okio) | `3.9.0` | `3.9.1` | | [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) | `5.10.3` | `5.11.3` | | [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) | `1.5.1` | `1.5.2` | | [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) | `24.1.0` | `26.0.1` | | com.gradle.plugin-publish | `1.2.1` | `1.3.0` | Updates `commons-io:commons-io` from 2.16.1 to 2.17.0 Updates `com.squareup.okio:okio` from 3.9.0 to 3.9.1 - [Release notes](https://github.com/square/okio/releases) - [Changelog](https://github.com/square/okio/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okio/compare/parent-3.9.0...3.9.1) Updates `org.junit.jupiter:junit-jupiter` from 5.10.3 to 5.11.3 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.10.3...r5.11.3) Updates `com.networknt:json-schema-validator` from 1.5.1 to 1.5.2 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.1...1.5.2) Updates `org.jetbrains:annotations` from 24.1.0 to 26.0.1 - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/24.1.0...26.0.1) Updates `com.gradle.plugin-publish` from 1.2.1 to 1.3.0 --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: com.squareup.okio:okio dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-major dependency-group: gradle-dependencies - dependency-name: com.gradle.plugin-publish dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7444c25..b8c1d0e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,24 +8,24 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.16.1" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.17.0" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } -okio = { group = "com.squareup.okio", name = "okio", version = "3.9.0" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.9.1" } ### Test dependencies spock-core = { group = "org.spockframework", name = "spock-core", version.ref = "spock" } spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.ref = "spock" } junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.3" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.3" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.22" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.1" } -jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.2" } +jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.1" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } [plugins] shadow-jar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"} -plugin-publish = { id = "com.gradle.plugin-publish", version = "1.2.1" } +plugin-publish = { id = "com.gradle.plugin-publish", version = "1.3.0" } github-release = { id = "com.github.breadmoirai.github-release", version = "2.5.2"} From db3a4faf1784c9267281ed76bf3e0c34b3380245 Mon Sep 17 00:00:00 2001 From: daz Date: Wed, 30 Oct 2024 17:54:47 -0600 Subject: [PATCH 79/92] Constrain build dependency to address security vulnerability --- plugin/build.gradle.kts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 47e3a83..cdb5258 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -12,9 +12,10 @@ buildscript { } dependencies { constraints { - // The plugin com.github.breadmoirai.github-release:2.5.2 has dependency on com.squareup.okio:okio:3.0.0 - // which has reported vulnerability CVE-2023-3635. Use a newer version. + // The plugin com.github.breadmoirai.github-release:2.5.2 depends on vulnerable library releases. + // We constrain these to newer, patched versions. classpath(libs.okio) + classpath(libs.apache.commons.io) } } } From 55000da1dda84a45d5484cb048224fabf8fe1de9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:35:48 +0000 Subject: [PATCH 80/92] Bump the gradle-dependencies group across 1 directory with 2 updates Bumps the gradle-dependencies group with 2 updates in the / directory: [org.codehaus.groovy:groovy-json](https://github.com/apache/groovy) and [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator). Updates `org.codehaus.groovy:groovy-json` from 3.0.22 to 3.0.23 - [Commits](https://github.com/apache/groovy/commits) Updates `com.networknt:json-schema-validator` from 1.5.2 to 1.5.3 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.2...1.5.3) --- updated-dependencies: - dependency-name: org.codehaus.groovy:groovy-json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b8c1d0e..4ba828f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,8 +20,8 @@ spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.re junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.3" } -groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.22" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.2" } +groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.23" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.3" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.1" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From 47a495f78ff791894d8039894853e4bc6e6854f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:36:39 +0000 Subject: [PATCH 81/92] Bump the gradle-dependencies group across 1 directory with 3 updates Bumps the gradle-dependencies group with 3 updates in the / directory: commons-io:commons-io, [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) and [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator). Updates `commons-io:commons-io` from 2.17.0 to 2.18.0 Updates `org.junit.jupiter:junit-jupiter` from 5.11.3 to 5.11.4 - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.11.3...r5.11.4) Updates `com.networknt:json-schema-validator` from 1.5.3 to 1.5.4 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.3...1.5.4) --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ba828f..38f7b8d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.17.0" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.18.0" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } okio = { group = "com.squareup.okio", name = "okio", version = "3.9.1" } @@ -18,10 +18,10 @@ okio = { group = "com.squareup.okio", name = "okio", version = "3.9.1" } spock-core = { group = "org.spockframework", name = "spock-core", version.ref = "spock" } spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.ref = "spock" } junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.3" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.4" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.23" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.3" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.4" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.1" } google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } From 9733cfe99993757b5e4ab090b56b7747002bc1c1 Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 17 Dec 2024 11:57:34 -0700 Subject: [PATCH 82/92] Prepare for release --- release/changes.md | 4 ++-- release/version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/changes.md b/release/changes.md index 925bc10..0c7185a 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,2 +1,2 @@ -- [FIX] Avoid resolving configurations that are deprecated for resolving (#129) -- [FIX] Avoid circular task dependency when build includes itself (#141) \ No newline at end of file +This patch release updates a number of dependency versions. +No functional changes are included. \ No newline at end of file diff --git a/release/version.txt b/release/version.txt index 3a3cd8c..1892b92 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.3.1 +1.3.2 From 4e3062ce9a8e0b1346f13ff483aef22d11c84fa2 Mon Sep 17 00:00:00 2001 From: daz Date: Tue, 17 Dec 2024 12:14:26 -0700 Subject: [PATCH 83/92] Bump DV plugin to 3.19 --- .../dependencygraph/SampleProjectDependencyExtractorTest.groovy | 2 +- sample-projects/java-single-project/settings.gradle | 2 +- settings.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy index 0d37538..4bd15d5 100644 --- a/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy +++ b/plugin-test/src/test/groovy/org/gradle/github/dependencygraph/SampleProjectDependencyExtractorTest.groovy @@ -26,7 +26,7 @@ class SampleProjectDependencyExtractorTest extends BaseExtractorTest { def manifestDependencies = manifest.resolved [ // plugin dependencies - "com.gradle:develocity-gradle-plugin:3.18.1", + "com.gradle:develocity-gradle-plugin:3.19", "com.diffplug.spotless:spotless-plugin-gradle:4.5.1", "com.diffplug.durian:durian-core:1.2.0", ].forEach { diff --git a/sample-projects/java-single-project/settings.gradle b/sample-projects/java-single-project/settings.gradle index e00a4ae..fd9b1d8 100644 --- a/sample-projects/java-single-project/settings.gradle +++ b/sample-projects/java-single-project/settings.gradle @@ -1,4 +1,4 @@ plugins { - id 'com.gradle.develocity' version '3.18.1' + id 'com.gradle.develocity' version '3.19' } rootProject.name = 'java-lib' diff --git a/settings.gradle.kts b/settings.gradle.kts index 8bf9287..f702fa5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradle.develocity").version("3.18.1") + id("com.gradle.develocity").version("3.19") id("com.gradle.common-custom-user-data-gradle-plugin").version("2.0.2") } From 0f8c712f3ca62006dacb569549b897579ead847b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:47:38 +0000 Subject: [PATCH 84/92] Bump com.squareup.okio:okio Bumps the gradle-dependencies group with 1 update in the / directory: [com.squareup.okio:okio](https://github.com/square/okio). Updates `com.squareup.okio:okio` from 3.9.1 to 3.10.2 - [Release notes](https://github.com/square/okio/releases) - [Changelog](https://github.com/square/okio/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okio/compare/3.9.1...3.10.2) --- updated-dependencies: - dependency-name: com.squareup.okio:okio dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 38f7b8d..8cdf923 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-modul apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.18.0" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } -okio = { group = "com.squareup.okio", name = "okio", version = "3.9.1" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.10.2" } ### Test dependencies From 8b4ed390702f9e9828c4d79a544b20b9015d3003 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Sat, 18 Jan 2025 09:50:49 -0700 Subject: [PATCH 85/92] Fix mistakes in documentation Fixes #170 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a2b7677..1ba3760 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ However, if this doesn't work, you can add the following to your `dependency-ver As well as the `GitHubDependencyGraphPlugin`, which is tailored for use by the [gradle/actions/dependency-submission](https://github.com/gradle/actions/tree/main/dependency-submission) GitHub Action, this repository also provides the `SimpleDependencyGraphPlugin`, which generates dependency-graph outputs in simple text format. -To use the `SimpleDependencyGraphPlugin` you'll need to create an `init-script.gradle` file to apply the plugin to your project: +To use the `SimpleDependencyGraphPlugin` you'll need to create an `init.gradle` file to apply the plugin to your project: ```groovy initscript { @@ -133,10 +133,10 @@ apply plugin: org.gradle.dependencygraph.simple.SimpleDependencyGraphPlugin and then execute the task to resolve all dependencies in your project: ```shell -./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-configure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies +./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-zconfigure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies ``` -You'll find the generated files in `build/dependency-graph-snapshots`. +You'll find the generated files in `build/reports/dependency-graph-snapshots`. ### Using dependency reports to determine the underlying source of a dependency From 394b23cc8cfcbb865ba540deca4cca3780f1317b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:41:34 +0000 Subject: [PATCH 86/92] Bump the gradle-dependencies group across 1 directory with 4 updates Bumps the gradle-dependencies group with 4 updates in the / directory: [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator), [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations), [com.google.code.gson:gson](https://github.com/google/gson) and com.gradle.plugin-publish. Updates `com.networknt:json-schema-validator` from 1.5.4 to 1.5.5 - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.4...1.5.5) Updates `org.jetbrains:annotations` from 26.0.1 to 26.0.2 - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md) - [Commits](https://github.com/JetBrains/java-annotations/compare/26.0.1...26.0.2) Updates `com.google.code.gson:gson` from 2.11.0 to 2.12.1 - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.11.0...gson-parent-2.12.1) Updates `com.gradle.plugin-publish` from 1.3.0 to 1.3.1 --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: org.jetbrains:annotations dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: com.gradle.plugin-publish dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8cdf923..09536d9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,11 +21,11 @@ junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.4" } groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.23" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.4" } -jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.1" } -google-gson = { group = "com.google.code.gson", name = "gson", version = "2.11.0" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.5" } +jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.2" } +google-gson = { group = "com.google.code.gson", name = "gson", version = "2.12.1" } [plugins] shadow-jar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"} -plugin-publish = { id = "com.gradle.plugin-publish", version = "1.3.0" } +plugin-publish = { id = "com.gradle.plugin-publish", version = "1.3.1" } github-release = { id = "com.github.breadmoirai.github-release", version = "2.5.2"} From c1b066632c7589b62f7dfcd13c47f2e76506736e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 09:44:38 -0600 Subject: [PATCH 87/92] Bump the gradle-dependencies group across 1 directory with 6 updates (#176) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the gradle-dependencies group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | commons-io:commons-io | `2.18.0` | `2.19.0` | | [com.squareup.okio:okio](https://github.com/square/okio) | `3.10.2` | `3.11.0` | | [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) | `5.11.4` | `5.12.2` | | [org.codehaus.groovy:groovy-json](https://github.com/apache/groovy) | `3.0.23` | `3.0.24` | | [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) | `1.5.5` | `1.5.6` | | [com.google.code.gson:gson](https://github.com/google/gson) | `2.12.1` | `2.13.0` | Updates `commons-io:commons-io` from 2.18.0 to 2.19.0 Updates `com.squareup.okio:okio` from 3.10.2 to 3.11.0
    Changelog

    Sourced from com.squareup.okio:okio's changelog.

    Version 3.11.0

    2025-04-09

    • Fix: Clear the deflater's byte array reference
    • New: Faster implementation of String.decodeHex() on Kotlin/JS.
    • New: Declare EXACTLY_ONCE execution for blocks like Closeable.use {} and FileSystem.read {}.
    • Upgrade: [Kotlin 2.1.20][kotlin_2_1_20].
    Commits
    • a5b9420 Revert publish plugin
    • e3c8d93 Prepare for release 3.11.0.
    • a8040d3 Merge pull request #1614 from square/jwilson.0409.promote_fast_js_hex
    • a571c31 Fix appleMain dependency
    • b43d833 Spotless
    • 6326888 Custom JS implementation of decodeHex
    • 9b42a97 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.0.3 (#1613)
    • 9ed2595 Clear the deflater's byte array reference (#1612)
    • 7f14587 Update dependency com.android.tools.build:gradle to v8.9.1 (#1610)
    • 992d189 Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v2.1.20 (#1605)
    • Additional commits viewable in compare view

    Updates `org.junit.jupiter:junit-jupiter` from 5.11.4 to 5.12.2
    Release notes

    Sourced from org.junit.jupiter:junit-jupiter's releases.

    JUnit 5.12.2 = Platform 1.12.2 + Jupiter 5.12.2 + Vintage 5.12.2

    See Release Notes.

    Full Changelog: https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2

    JUnit 5.12.1 = Platform 1.12.1 + Jupiter 5.12.1 + Vintage 5.12.1

    See Release Notes.

    Full Changelog: https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1

    JUnit 5.12.0 = Platform 1.12.0 + Jupiter 5.12.0 + Vintage 5.12.0

    See Release Notes.

    New Contributors

    Full Changelog: https://github.com/junit-team/junit5/compare/r5.11.4...r5.12.0

    JUnit 5.12.0-RC2 = Platform 1.12.0-RC2 + Jupiter 5.12.0-RC2 + Vintage 5.12.0-RC2

    See Release Notes.

    Full Changelog: https://github.com/junit-team/junit5/compare/r5.12.0-RC1...r5.12.0-RC2

    JUnit 5.12.0-RC1 = Platform 1.12.0-RC1 + Jupiter 5.12.0-RC1 + Vintage 5.12.0-RC1

    See Release Notes.

    Full Changelog: https://github.com/junit-team/junit5/compare/r5.12.0-M1...r5.12.0-RC1

    JUnit 5.12.0-M1 = Platform 1.12.0-M1 + Jupiter 5.12.0-M1 + Vintage 5.12.0-M1

    See Release Notes.

    New Contributors

    ... (truncated)

    Commits
    • 0a44659 Release 5.12.2
    • 4c7dfdc Finalize 5.12.2 release notes
    • 561613e Fix handling of CleanupMode.ON_SUCCESS
    • 19d07d2 Add 5.12.2 release notes from template
    • 803cbb6 Add build parameter for enabling dry-run mode for test execution
    • eb43e62 Back to snapshots for further development
    • ba9c9ae Release 5.12.1
    • e28ad4a Finalize 5.12.1 release notes
    • 1044e2c Move entry to 5.12.1 release notes
    • bea821d Fix Javadoc formatting
    • Additional commits viewable in compare view

    Updates `org.codehaus.groovy:groovy-json` from 3.0.23 to 3.0.24
    Commits

    Updates `com.networknt:json-schema-validator` from 1.5.5 to 1.5.6
    Release notes

    Sourced from com.networknt:json-schema-validator's releases.

    1.5.6- 2025-02-19

    Added

    Changed

    Changelog

    Sourced from com.networknt:json-schema-validator's changelog.

    Change Log

    All notable changes to this project will be documented in this file.

    This format is based on Keep a Changelog.

    This project does not adhere to Semantic Versioning and minor version changes can have incompatible API changes. These incompatible API changes will largely affect those who have custom validator or walker implementations. Those who just use the library to validate using the standard JSON Schema Draft specifications may not need changes.

    [Unreleased]

    Added

    Changed

    1.5.6- 2025-02-19

    Added

    Changed

    Commits
    • 2e3fa10 upgrade to 1.5.6 and update changelog
    • 6bff075 Set requires static for optional and excludable dependencies (#1155)
    • da3865c Fix NPE when walking a missing node that will have missing properties (#1152)
    • 6c37935 Fix relative iris with colons (#1147)
    • 77c91a2 Fix explicit disabling of format assertions (#1145)
    • See full diff in compare view

    Updates `com.google.code.gson:gson` from 2.12.1 to 2.13.0
    Release notes

    Sourced from com.google.code.gson:gson's releases.

    Gson 2.13.0

    What's Changed

    • A bug in deserializing collections has been fixed. Previously, if you did something like this:

      gson.fromJson(jsonString, new
      TypeToken<ImmutableList<String>>() {})
      

      then the inferred type would be ImmutableList<String>, but Gson actually gave you an ArrayList<String>. Usually that would lead to an immediate ClassCastException, but in some circumstances the code might sometimes succeed despite the wrong type. Now you will see an exception like this:

      com.google.gson.JsonIOException: Abstract classes can't be
      instantiated!
      Adjust the R8 configuration or register an InstanceCreator or a
      TypeAdapter for this type.
      Class name: com.google.common.collect.ImmutableList
      

      because Gson now really is trying to create an ImmutableList through its constructor, but that isn't possible. Either change the requested type (in the TypeToken) to List<String>, or register a TypeAdapter or JsonDeserializer for ImmutableList.

    • The internal classes $Gson$Types and $Gson$Preconditions have been renamed to remove the $ characters. Since these are internal classes (as signaled not only by the package name but by the $ characters), client code should not be affected. If your code was depending on these classes then we suggest making a copy of the class (subject to the license) rather than depending on the new names.

    Full Changelog: https://github.com/google/gson/compare/gson-parent-2.12.1...gson-parent-2.13.0

    Commits
    • bfe0fd5 [maven-release-plugin] prepare release gson-parent-2.13.0
    • 6ed64ca add multi-catch support to the code base (#2841)
    • 0074376 Bump the maven group with 3 updates (#2840)
    • 45e5e14 Rename $Gson$Preconditions and $Gson$Types. (#2838)
    • c6d4425 Remove obsolete comment in pom.xml (#2835)
    • 9afd6f8 Bump the maven group with 10 updates (#2831)
    • ad5371e Fix findings that are new with the latest Error Prone. (#2834)
    • de190d7 Restructure code to avoid assignment expression warning. (#2833)
    • 3d66847 Bump the github-actions group with 3 updates (#2832)
    • 2549ba9 Fix ConstructorConstructor creating mismatching Collection and Map instances ...
    • Additional commits viewable in compare view

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
    Dependabot commands and options
    You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
    Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 09536d9..07021de 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,22 +8,22 @@ jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-datab jackson-parameter-names = { group = "com.fasterxml.jackson.module", name = "jackson-module-parameter-names" } jackson-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin" } -apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.18.0" } +apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.19.0" } github-packageurl = { group = "com.github.package-url", name = "packageurl-java", version = "1.5.0" } -okio = { group = "com.squareup.okio", name = "okio", version = "3.10.2" } +okio = { group = "com.squareup.okio", name = "okio", version = "3.11.0" } ### Test dependencies spock-core = { group = "org.spockframework", name = "spock-core", version.ref = "spock" } spock-junit4 = { group = "org.spockframework", name = "spock-junit4", version.ref = "spock" } junit-junit4 = { group = "junit", name = "junit", version = "4.13.2" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.11.4" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version = "5.12.2" } -groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.23" } -json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.5" } +groovy-json = { group = "org.codehaus.groovy", name = "groovy-json", version = "3.0.24" } +json-schema-validator = { group = "com.networknt", name = "json-schema-validator", version = "1.5.6" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "26.0.2" } -google-gson = { group = "com.google.code.gson", name = "gson", version = "2.12.1" } +google-gson = { group = "com.google.code.gson", name = "gson", version = "2.13.0" } [plugins] shadow-jar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"} From 0b278879fa298b834b6bc7c8496c0a7f04c6e275 Mon Sep 17 00:00:00 2001 From: Lewis Jones Date: Fri, 16 May 2025 16:55:56 +0100 Subject: [PATCH 88/92] GitHub Detector can be customized with env vars (#177) In order for GitHub to use action for Gradle Auto Submission we need to be able to set these values on the the JSON payload submitted to our snapshots API. Added optional env var parameter: ``` GITHUB_DEPENDENCY_GRAPH_DETECTOR_NAME GITHUB_DEPENDENCY_GRAPH_DETECTOR_VERSION GITHUB_DEPENDENCY_GRAPH_DETECTOR_URL ``` These override the detector name, version and url if provided. --- README.md | 11 ++++++++++- .../GitHubRepositorySnapshotBuilder.kt | 8 +++++++- .../github/dependencygraph/GitHubSnapshotParams.kt | 11 ++++++++++- .../github/dependencygraph/model/GitHubDetector.kt | 6 +++--- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1ba3760..1cefdf8 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,12 @@ This causes 2 separate plugins to be applied, that can be used independently: - `GitHubDependencyExtractorPlugin` collects all dependencies that are resolved during a build execution and writes these to a file. The output file can be found at `/build/reports/github-depenency-graph-snapshots/.json`. - `ForceDependencyResolutionPlugin` creates a `ForceDependencyResolutionPlugin_resolveAllDependencies` task that will attempt to resolve all dependencies for a Gradle build, by simply invoking `dependencies` on all projects. -### Required environment variables +### Environment variables The following environment variables configure the snapshot generated by the `GitHubDependencyExtractorPlugin`. See the [GitHub Dependency Submission API docs](https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28) for details: + +#### Required environment variables + - `GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR`: Sets the `job.correlator` value for the dependency submission - `GITHUB_DEPENDENCY_GRAPH_JOB_ID`: Sets the `job.id` value for the dependency submission - `GITHUB_DEPENDENCY_GRAPH_REF`: Sets the `ref` value for the commit that generated the dependency graph @@ -38,6 +41,12 @@ The following environment variables configure the snapshot generated by the `Git - `GITHUB_DEPENDENCY_GRAPH_WORKSPACE`: Sets the root directory of the github repository. Must be an absolute path. - `DEPENDENCY_GRAPH_REPORT_DIR` (optional): Specifies where the dependency graph report will be generated. Must be an absolute path. +#### Optional environment variables + +- `GITHUB_DEPENDENCY_GRAPH_DETECTOR_NAME`: Sets the `detector.name` value for the dependency submission. Defaults to `GitHub Dependency Graph Gradle Plugin` +- `GITHUB_DEPENDENCY_GRAPH_DETECTOR_VERSION`: Sets the `detector.version` value for the dependency submission. Defaults to current version of the plugin. +- `GITHUB_DEPENDENCY_GRAPH_DETECTOR_URL`: Sets the `detector.url` value for the dependency submission. Defaults to `https://github.com/gradle/github-dependency-graph-gradle-plugin` + Each of these values can also be provided via a system property. eg: Env var `DEPENDENCY_GRAPH_REPORT_DIR` can be set with `-DDEPENDENCY_GRAPH_REPORT_DIR=...` on the command-line. diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt index 3fc3a44..ae6e012 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubRepositorySnapshotBuilder.kt @@ -10,7 +10,13 @@ class GitHubRepositorySnapshotBuilder( private val snapshotParams: GitHubSnapshotParams ) { - private val detector by lazy { GitHubDetector() } + private val detector by lazy { + GitHubDetector( + name = snapshotParams.githubDetectorName, + version = snapshotParams.githubDetectorVersion, + url = snapshotParams.githubDetectorUrl + ) + } private val job by lazy { GitHubJob( diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt index e00aa0d..870815f 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubSnapshotParams.kt @@ -8,6 +8,9 @@ const val PARAM_JOB_ID = "GITHUB_DEPENDENCY_GRAPH_JOB_ID" const val PARAM_JOB_CORRELATOR = "GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR" const val PARAM_GITHUB_REF = "GITHUB_DEPENDENCY_GRAPH_REF" const val PARAM_GITHUB_SHA = "GITHUB_DEPENDENCY_GRAPH_SHA" +const val PARAM_GITHUB_DETECTOR_NAME = "GITHUB_DEPENDENCY_GRAPH_DETECTOR_NAME" +const val PARAM_GITHUB_DETECTOR_VERSION = "GITHUB_DEPENDENCY_GRAPH_DETECTOR_VERSION" +const val PARAM_GITHUB_DETECTOR_URL = "GITHUB_DEPENDENCY_GRAPH_DETECTOR_URL" /** * Environment variable should be set to the workspace directory that the Git repository is checked out in. * This is used to determine relative path to build files referenced in the dependency graph. @@ -16,9 +19,15 @@ const val PARAM_GITHUB_WORKSPACE = "GITHUB_DEPENDENCY_GRAPH_WORKSPACE" class GitHubSnapshotParams(pluginParameters: PluginParameters) { val dependencyGraphJobCorrelator: String = pluginParameters.load(PARAM_JOB_CORRELATOR) - val dependencyGraphJobId: String =pluginParameters.load(PARAM_JOB_ID) + val dependencyGraphJobId: String = pluginParameters.load(PARAM_JOB_ID) val gitSha: String = pluginParameters.load(PARAM_GITHUB_SHA) val gitRef: String = pluginParameters.load(PARAM_GITHUB_REF) val gitHubWorkspace: Path = Paths.get(pluginParameters.load(PARAM_GITHUB_WORKSPACE)) + val githubDetectorName: String = pluginParameters.loadOptional(PARAM_GITHUB_DETECTOR_NAME) + ?: javaClass.`package`.implementationTitle + val githubDetectorVersion: String = pluginParameters.loadOptional(PARAM_GITHUB_DETECTOR_VERSION) + ?: javaClass.`package`.implementationVersion + val githubDetectorUrl: String = pluginParameters.loadOptional(PARAM_GITHUB_DETECTOR_URL) + ?: "https://github.com/gradle/github-dependency-graph-gradle-plugin" } diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDetector.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDetector.kt index f5e256c..8961b8f 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDetector.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/model/GitHubDetector.kt @@ -1,7 +1,7 @@ package org.gradle.github.dependencygraph.model data class GitHubDetector( - val name: String = GitHubDetector::class.java.`package`.implementationTitle, - val version: String = GitHubDetector::class.java.`package`.implementationVersion, - val url: String = "https://github.com/gradle/github-dependency-graph-gradle-plugin" + val name: String, + val version: String, + val url: String ) From c4022c6b531458392a4bc4bcee6ffeae2e36bacb Mon Sep 17 00:00:00 2001 From: daz Date: Fri, 16 May 2025 09:44:46 -0600 Subject: [PATCH 89/92] Prepare for release --- release/changes.md | 3 +-- release/version.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/release/changes.md b/release/changes.md index 0c7185a..20acfc6 100644 --- a/release/changes.md +++ b/release/changes.md @@ -1,2 +1 @@ -This patch release updates a number of dependency versions. -No functional changes are included. \ No newline at end of file +- [NEW] GitHub Detector can be customized with environment variables diff --git a/release/version.txt b/release/version.txt index 1892b92..88c5fb8 100644 --- a/release/version.txt +++ b/release/version.txt @@ -1 +1 @@ -1.3.2 +1.4.0 From 23186fef190c3eb4d48242b026b85d8cca0d0292 Mon Sep 17 00:00:00 2001 From: Daz DeBoer Date: Fri, 23 May 2025 10:37:49 -0600 Subject: [PATCH 90/92] Bump to Gradle 8.14 (#179) --- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 10 ++++++---- gradlew.bat | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 35073 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 delta 34912 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

    Gn4F+B~IivciU9^t0j-Mgao3 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 dDuazZG^u*-) zCV{{p$D*4m=Z;!QJ0C3EBfaUv#@3CmRgS-#a4+Uk9LtR(mzdSt-?*B-Q{Jgs!F;+_ zT&Jc~{N951OskjVtnWOLnfUacmSEkQ#fQojqa=HN?aN#ubFA){K&TrfoSnBphJ<43S zS5?OUrqPYpEqf;(zc;IIty0d`-n-Ke%PlArJt^DN>ZYW)ZS5z$Rnx=cxbM1#zPRNS1N0u>c z*gpBeTvyKAjdr#Uz%$J3C+p2~l9`-P6`=_1Y83#nAp`Kd5jfb*H#u;=%w#=&g~|Q% z1!N9S7n+#|tj#t98*^$X3S9)i3I*n?N+YghXJ9Z!QN%wvV7|W$;;Lx|1{)Ol%c4L- zlz|txtAK4ozLL`xMPZR7Sm6grRj@+Db&BXF9#sG|gMywm=IO1O01imHv3 zFjX9ri>su;HX}wK85k5$R0A&?fdtA5_Q_vX$jc(elo%M~QB>A-K~#2igOjH$d^80Z zC8#NjzZarXrcVZJJEF73z+ix4=7c`5N>TJt7>~rfl$^E}UAMlP2$P)vS26{cE?ONf_&Vd@-E2?PMU+S~pB diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8a1f6b9..247cf2a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionSha256Sum=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index b740cf1..23d15a9 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -203,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -211,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 25da30d..db3a6ac 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From d80b3029e61302901527679d4fdefac98707e629 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Wed, 6 Aug 2025 16:36:55 +0100 Subject: [PATCH 91/92] Fix typo in code snippet in README.md (#183) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1cefdf8..3aea17e 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ apply plugin: org.gradle.dependencygraph.simple.SimpleDependencyGraphPlugin and then execute the task to resolve all dependencies in your project: ```shell -./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-zconfigure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies +./gradlew -I init.gradle --dependency-verification=off --no-configuration-cache --no-configure-on-demand :ForceDependencyResolutionPlugin_resolveAllDependencies ``` You'll find the generated files in `build/reports/dependency-graph-snapshots`. From be867f1baea35e8c8fcece928c7887e9952980b6 Mon Sep 17 00:00:00 2001 From: Boris Faniuk <107043479+boris-faniuk-n26@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:38:33 +0200 Subject: [PATCH 92/92] Fixed a typo in documentation (no-configure-on-demand) (#186)