spring-gradle-plugins / dependency-management-plugin

A Gradle plugin that provides Maven-like dependency management functionality
684 stars 85 forks source link

jacoco-report-aggregation plugin misses versions during resolution when io.spring.dependency-management plugin is used #353

Closed rkrisztian closed 1 year ago

rkrisztian commented 1 year ago

When I use the jacoco-report-aggregation plugin together with the io.spring.dependency-management plugin, and execute gw testCodeCoverageReport, I get the following error:

Execution failed for task ':testCodeCoverageReport'.
> Could not resolve all files for configuration ':allCodeCoverageReportClassDirectories'.
   > Could not find org.springframework.boot:spring-boot-starter:.
     Required by:
         project : > project :demo-common
         project : > project :demo-subproject

Expected Behavior

There should be no resolution issues just by introducing the JaCoCO report aggregation plugin in a Spring Boot project.

Current Behavior

See error above.

Context

My company develops Spring Boot applications. One of the major problems I am having is figuring out the most ideal configuration for a JaCoCo report that does not miss coverage when tests cover code across subprojects, and is compatible with SonarQube. While I found one that works (and explained everything at https://github.com/gradle/gradle/issues/8881#issuecomment-1495945301), it would be better to use a configuration that requires less code and is probably more future proof, i.e., by using the jacoco-report-aggregation plugin. But right now I'm stuck midway at reaching that solution because of resolution issues.

It's not just my problem. See https://stackoverflow.com/questions/73727598/jacoco-report-aggregation-plugin-spring-dependency-management where others faced a similar issue. Their workaround they found was to use specific versions, which I don't want to enforce on my company's projects.

Steps to Reproduce

See demo project aggregation-bug.zip. Type gw testCodeCoverageReport. (Sorry next time I'll try out the Gradle issue reproducer.)

Your Environment

Linux with Azul JDK 17, but shouldn't matter much.

wilkinsona commented 1 year ago

The problem can be reproduced without the dependency management plugin:

import org.springframework.boot.gradle.plugin.SpringBootPlugin

plugins {
    id 'base'
    id 'org.springframework.boot' version '3.0.5' apply false
    id 'jacoco-report-aggregation'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

allprojects {
    repositories {
        mavenCentral()
    }
}

subprojects {
    pluginManager.withPlugin('java') {
        apply plugin: 'jacoco'

        java {
            toolchain {
                languageVersion = JavaLanguageVersion.of(17)
            }
        }

        configurations.all {
            resolutionStrategy {
                eachDependency {
                    if (it.requested.group == 'org.springframework.boot') {
                        it.useVersion("3.0.5")
                    }
                }
            }
        }

        dependencies {
            implementation 'org.springframework.boot:spring-boot-starter'
            testImplementation 'org.springframework.boot:spring-boot-starter-test'
        }

        tasks.withType(Test).configureEach { useJUnitPlatform() }
        tasks.withType(JacocoReport).configureEach { reports.html.required = true }
    }
}

// This "withPlugin" call was added to be able to check the build without 'jacoco-report-aggregation' easily.
pluginManager.withPlugin('jacoco-report-aggregation') {
    dependencies {
        subprojects {
            pluginManager.withPlugin('java') {
                jacocoAggregation project
            }
        }
    }

    reporting {
        reports {
            testCodeCoverageReport(JacocoCoverageReport) {
                testType = TestSuiteType.UNIT_TEST
            }
        }
    }

    tasks.named('check') {
        dependsOn 'testCodeCoverageReport'
    }
}

It does not occur if the resolution strategy is applied in all projects rather than in subprojects:

import org.springframework.boot.gradle.plugin.SpringBootPlugin

plugins {
    id 'base'
    id 'org.springframework.boot' version '3.0.5' apply false
    id 'jacoco-report-aggregation'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

allprojects {
    repositories {
        mavenCentral()
    }
}

allprojects {
    configurations.all {
        resolutionStrategy {
            eachDependency {
                if (it.requested.group == 'org.springframework.boot') {
                    it.useVersion("3.0.5")
                }
            }
        }
    }
}

subprojects {
    pluginManager.withPlugin('java') {
        apply plugin: 'jacoco'

        java {
            toolchain {
                languageVersion = JavaLanguageVersion.of(17)
            }
        }

        dependencies {
            implementation 'org.springframework.boot:spring-boot-starter'
            testImplementation 'org.springframework.boot:spring-boot-starter-test'
        }

        tasks.withType(Test).configureEach { useJUnitPlatform() }
        tasks.withType(JacocoReport).configureEach { reports.html.required = true }
    }
}

// This "withPlugin" call was added to be able to check the build without 'jacoco-report-aggregation' easily.
pluginManager.withPlugin('jacoco-report-aggregation') {
    dependencies {
        subprojects {
            pluginManager.withPlugin('java') {
                jacocoAggregation project
            }
        }
    }

    reporting {
        reports {
            testCodeCoverageReport(JacocoCoverageReport) {
                testType = TestSuiteType.UNIT_TEST
            }
        }
    }

    tasks.named('check') {
        dependsOn 'testCodeCoverageReport'
    }
}

This also works with the dependency management plugin:

import org.springframework.boot.gradle.plugin.SpringBootPlugin

plugins {
    id 'base'
    id 'org.springframework.boot' version '3.0.5' apply false
    id 'jacoco-report-aggregation'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

allprojects {
    repositories {
        mavenCentral()
    }
}

allprojects {
    apply plugin: 'io.spring.dependency-management'
    dependencyManagement {
        imports {
            mavenBom SpringBootPlugin.BOM_COORDINATES
        }
    }
}

subprojects {
    pluginManager.withPlugin('java') {
        apply plugin: 'jacoco'

        java {
            toolchain {
                languageVersion = JavaLanguageVersion.of(17)
            }
        }

        dependencies {
            implementation 'org.springframework.boot:spring-boot-starter'
            testImplementation 'org.springframework.boot:spring-boot-starter-test'
        }

        tasks.withType(Test).configureEach { useJUnitPlatform() }
        tasks.withType(JacocoReport).configureEach { reports.html.required = true }
    }
}

// This "withPlugin" call was added to be able to check the build without 'jacoco-report-aggregation' easily.
pluginManager.withPlugin('jacoco-report-aggregation') {
    dependencies {
        subprojects {
            pluginManager.withPlugin('java') {
                jacocoAggregation project
            }
        }
    }

    reporting {
        reports {
            testCodeCoverageReport(JacocoCoverageReport) {
                testType = TestSuiteType.UNIT_TEST
            }
        }
    }

    tasks.named('check') {
        dependsOn 'testCodeCoverageReport'
    }
}

I think this will have to be addressed in the jacoco-report-aggregation plugin. It appears to be resolving dependencies from sub-projects in a way that is incompatible with using a resolution strategy to configure dependency versions.

rkrisztian commented 1 year ago

Thanks for the info, and sorry that I did not understand the problem better.