diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28b1fe6..b2c8568 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,26 +49,18 @@ jobs: key: ${{ runner.os }}-java-${{ matrix.java }}-sonar restore-keys: ${{ runner.os }}-java-${{ matrix.java }}-sonar - - name: Build with Java ${{ matrix.java }} with configuration cache - run: ./gradlew build --warning-mode all --exclude-task ossIndexAudit -PenableConfigurationCache=true -PjavaVersion=${{ matrix.java }} + - name: Build with Java ${{ matrix.java }} + run: | + ./gradlew build --warning-mode fail \ + --configuration-cache --configuration-cache-problems=fail \ + --exclude-task ossIndexAudit \ + -PjavaVersion=${{ matrix.java }} - name: Upload reports uses: actions/upload-artifact@v7 if: ${{ always() }} with: - name: reports-${{ runner.os }}-java-${{ matrix.java }}-with-configuration-cache - path: build/reports/ - - - name: Build with Java ${{ matrix.java }} without configuration cache - if: ${{ always() }} - # Required to get jacoco test coverage reports for SonarQube, which doesn't work with configuration cache enabled, see https://github.com/gradle/gradle/issues/25979 - run: ./gradlew clean build --warning-mode all --exclude-task ossIndexAudit -PenableConfigurationCache=false -PjavaVersion=${{ matrix.java }} - - - name: Upload reports - uses: actions/upload-artifact@v7 - if: ${{ always() }} - with: - name: reports-${{ runner.os }}-java-${{ matrix.java }}-without-configuration-cache + name: reports-${{ runner.os }}-java-${{ matrix.java }} path: build/reports/ - name: Sonar analysis diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index a0a767f..f42a037 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -10,8 +10,8 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 -org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.APILeak=warning org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning @@ -105,7 +105,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=21 +org.eclipse.jdt.core.compiler.source=17 org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 diff --git a/.vscode/settings.json b/.vscode/settings.json index 73fd29d..13e6f9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,6 @@ "source.generate.finalModifiers": "explicit", "source.fixAll": "explicit" }, - "java.saveActions.organizeImports": true, "java.sources.organizeImports.starThreshold": 3, "java.sources.organizeImports.staticStarThreshold": 3, "java.configuration.updateBuildConfiguration": "automatic", @@ -16,4 +15,4 @@ "projectKey": "org.itsallcode:openfasttrace-gradle" }, "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx12G -Xms100m" -} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d23ea..eadae15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgrade to Gradle 9.7.1 - Update Eclipse formatter settings to latest version - Remove unused Eclipse Save Actions configuration +- [#81](https://github.com/itsallcode/openfasttrace-gradle/issues/81): + - Ensure compatibility with Gradle configuration cache + - Mark plugin as compatible with configuration cache ## [3.2.0] - 2026-08-18 diff --git a/build.gradle b/build.gradle index 5cb7ddc..c32d77a 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,7 @@ dependencies { } def getJavaVersion = { - return project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 21 + return project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 17 } java { @@ -109,6 +109,11 @@ gradlePlugin { displayName = 'OpenFastTrace requirements tracing plugin' description = 'Gradle plugin for tracing requirements using OpenFastTrace' tags.addAll(['requirementstracing', 'requirements', 'tracing', 'reqtracing', 'openfasttrace', 'oft']) + compatibility { + features { + configurationCache = true + } + } } } } @@ -128,23 +133,10 @@ testing { implementation "org.hamcrest:hamcrest-core:3.0" implementation "com.jparams:to-string-verifier:1.4.8" } - targets { - all { - testTask.configure { - if (project.hasProperty('enableConfigurationCache')) { - def enableConfigurationCache = project.getProperty("enableConfigurationCache") - logger.lifecycle "Setting system property 'enableConfigurationCache' to $enableConfigurationCache for test task '${name}'" - systemProperty "enableConfigurationCache", enableConfigurationCache - } - } - } - } } } } -test.onlyIf { rootProject.name == 'openfasttrace-gradle' } - jacocoTestReport { dependsOn test reports { diff --git a/settings.gradle b/settings.gradle index 4456fb2..52fc8c5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,4 @@ +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} rootProject.name = 'openfasttrace-gradle' diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java b/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java index 13b379d..44a07a7 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java @@ -5,11 +5,11 @@ import java.io.File; import java.util.*; -import java.util.stream.Stream; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.logging.Logging; import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.tasks.TaskProvider; @@ -70,9 +70,8 @@ private static TaskProvider createCollectTask(final Project rootPro task.setGroup(TASK_GROUP_NAME); task.setDescription("Collect requirements and generate specobject file"); task.getInputDirectories().set(getAllInputDirectories(rootProject.getAllprojects())); - task.getOutputFile() - .set(new File(rootProject.getLayout().getBuildDirectory().getAsFile().get(), - "reports/requirements.xml")); + task.getOutputFile().set( + rootProject.getLayout().getBuildDirectory().file("reports/requirements.xml")); task.getPathConfig().set(getPathConfig(rootProject.getAllprojects())); }); } @@ -92,21 +91,22 @@ private static void configureTask(final Project rootProject, task.dependsOn(collectTask); final TracingConfig config = getConfig(rootProject); task.getFailBuild().set(config.getFailBuild()); - task.getRequirementsFile().set(collectTask.get().getOutputFile()); + task.getRequirementsFile().set(collectTask.flatMap(CollectTask::getOutputFile)); if (config.getReportFile().isPresent()) { task.getOutputFile().set(config.getReportFile()); } else { - final String extension = "html".equals(config.getReportFormat().get()) ? "html" : "txt"; - task.getOutputFile() - .set(new File(rootProject.getLayout().getBuildDirectory().getAsFile().get(), - "reports/tracing." + extension)); + task.getOutputFile().set(config.getReportFormat().map(format -> rootProject.getLayout() + .getBuildDirectory() + .file("reports/tracing." + ("html".equals(format) ? "html" : "txt")) + .get())); } task.getReportVerbosity().set(config.getReportVerbosity()); task.getReportFormat().set(config.getReportFormat()); - task.getImportedRequirements().set(getImportedRequirements(rootProject.getAllprojects())); + task.getImportedRequirements() + .from(getImportedRequirements(rootProject, rootProject.getAllprojects())); task.getFilteredArtifactTypes().set(config.getFilteredArtifactTypes()); task.getFilteredTags().set(config.getFilteredTags()); task.getFilterAcceptsItemsWithoutTag().set(config.getFilterAcceptsItemsWithoutTag()); @@ -144,14 +144,15 @@ private static Set getAllInputDirectories(final Set allProjects) .collect(toSet()); } - private static Set getImportedRequirements(final Set allProjects) + private static ConfigurableFileCollection getImportedRequirements(final Project rootProject, + final Set allProjects) { - return allProjects.stream() // - .flatMap(OpenFastTracePlugin::getImportedRequirements) // - .collect(toSet()); + return rootProject.files(allProjects.stream() // + .map(OpenFastTracePlugin::getImportedRequirements) // + .toList()); } - private static Stream getImportedRequirements(final Project project) + private static Configuration getImportedRequirements(final Project project) { final String CONFIG_NAME = "oftRequirementConfig"; final Configuration configuration = project.getConfigurations().create(CONFIG_NAME); @@ -160,9 +161,7 @@ private static Stream getImportedRequirements(final Project project) CONFIG_NAME, project); project.getDependencies().add(CONFIG_NAME, dependency); }); - final Set files = configuration.getFiles(); - LOG.info("Found {} dependency files: {}", files.size(), files); - return files.stream(); + return configuration; } private static List getPathConfig(final Set allProjects) diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java b/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java index c7cc96e..a68e798 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java @@ -8,6 +8,7 @@ import java.util.stream.Stream; import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.SetProperty; @@ -31,8 +32,7 @@ public class TraceTask extends DefaultTask private final Property reportFormat = getProject().getObjects().property(String.class); private final Property detailsSectionDisplay = getProject().getObjects() .property(DetailsSectionDisplay.class); - private final SetProperty importedRequirements = getProject().getObjects() - .setProperty(File.class); + private final ConfigurableFileCollection importedRequirements = getProject().files(); private final SetProperty filteredArtifactTypes = getProject().getObjects() .setProperty(String.class); private final SetProperty filteredTags = getProject().getObjects() @@ -99,8 +99,9 @@ public Property getReportFormat() * * @return the imported requirements property */ - @Input - public SetProperty getImportedRequirements() + @InputFiles + @PathSensitive(PathSensitivity.ABSOLUTE) + public ConfigurableFileCollection getImportedRequirements() { return importedRequirements; } @@ -247,7 +248,7 @@ private FilterSettings getFilterSettings() private List getAllImportFiles() { - final Stream importedRequirementPaths = importedRequirements.get().stream() + final Stream importedRequirementPaths = importedRequirements.getFiles().stream() .map(File::toPath); final Stream inputDirPaths = Stream.of(requirementsFile.getAsFile().get().toPath()); return Stream.concat(importedRequirementPaths, inputDirPaths).toList(); diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java index ad30b19..536e53d 100644 --- a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java @@ -3,7 +3,6 @@ import static java.util.stream.Collectors.joining; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.either; import static org.junit.jupiter.api.Assertions.*; @@ -46,26 +45,60 @@ void tracingTaskAddedToProject() @Test void pluginUsesConfigurationCache() { - testConfigurationCache(PROJECT_DEFAULT_CONFIG_DIR); + testConfigurationCache(PROJECT_CUSTOM_CONFIG_DIR, + Path.of("build/custom-report.txt"), + "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", + "not ok - 2 total, 1 direct, 0 transitive defects"); } @Test void pluginUsesConfigurationCacheWithMultiModuleProject() { - testConfigurationCache(MULTI_PROJECT_DIR); + testConfigurationCache(MULTI_PROJECT_DIR, Path.of("build/custom-report.txt"), + "ok - 6 total"); } - private void testConfigurationCache(final Path projectDir) + @Test + void pluginUsesConfigurationCacheWithImportedRequirements() { - PluginTestFixture.assumeConfigurationCacheEnabled(); - final PluginTestFixture fixture = fixture(projectDir).withArgs("tasks"); + final PluginTestFixture fixture = fixture(DEPENDENCY_CONFIG_DIR); + fixture.withArgs("clean").run() + .assertOutcome(":clean", + either(is(TaskOutcome.SUCCESS)) + .or(is(TaskOutcome.UP_TO_DATE))); - fixture.run().assertOutput(containsString( - "traceRequirements - Trace requirements and generate tracing report")); + final Path dependencyZip = DEPENDENCY_CONFIG_DIR + .resolve("build/repo/requirements-1.0.zip"); + createDependencyZip(dependencyZip); + + testConfigurationCache(fixture, + Path.of("build/reports/tracing.txt"), + "requirements-1.0.zip!spec.md:2", + "requirements-1.0.zip!source.java:1", + "not ok - 2 total, 1 direct, 0 transitive defects"); + } - fixture.run().assertOutput(allOf(containsString( - "traceRequirements - Trace requirements and generate tracing report"), - containsString("Reusing configuration cache."))); + private void testConfigurationCache(final Path projectDir, final Path reportFile, + final String... lines) + { + testConfigurationCache(fixture(projectDir), reportFile, lines); + } + + private void testConfigurationCache(final PluginTestFixture fixture, final Path reportFile, + final String... lines) + { + fixture.withArgs("traceRequirements") + .withReportFile(reportFile) + .run() + .assertTraceOutcomeSuccessFromCacheOrUpToDate() + .assertReportFileLines(lines); + + fixture.withArgs("traceRequirements") + .withReportFile(reportFile) + .run() + .assertTraceOutcomeSuccessFromCacheOrUpToDate() + .assertReportFileLines(lines) + .assertOutput(containsString("Reusing configuration cache.")); } @Test diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java b/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java index ba20c4d..90869b6 100644 --- a/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java @@ -3,7 +3,6 @@ import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.nio.file.Path; import java.util.*; @@ -66,11 +65,8 @@ private GradleRunner createGradleRunner() final List allArgs = new ArrayList<>(); allArgs.addAll(List.of(arguments)); allArgs.addAll(List.of("--info", "--stacktrace", "--build-cache")); - if (configurationCacheEnabled()) - { - allArgs.add("--configuration-cache"); - } - allArgs.addAll(List.of("--warning-mode", "all")); + allArgs.addAll(List.of("--configuration-cache", "--configuration-cache-problems=fail")); + allArgs.addAll(List.of("--warning-mode", "fail")); final GradleRunner runner = GradleRunner.create() .withProjectDir(projectDir.toFile()) .withPluginClasspath() @@ -83,17 +79,6 @@ private GradleRunner createGradleRunner() return runner; } - static void assumeConfigurationCacheEnabled() - { - assumeTrue(configurationCacheEnabled(), "Configuration cache is not enabled"); - } - - static boolean configurationCacheEnabled() - { - return System.getProperty("enableConfigurationCache", "false") - .equalsIgnoreCase("true"); - } - private static void configureJacoco(final Path projectDir) { final Optional testkitGradleConfig = TestUtil @@ -129,6 +114,14 @@ Result assertTraceOutcomeSuccessOrFromCache() return assertOutcomeSuccessOrFromCache(":traceRequirements"); } + Result assertTraceOutcomeSuccessFromCacheOrUpToDate() + { + return assertOutcome(":traceRequirements", + either(is(TaskOutcome.SUCCESS)) + .or(is(TaskOutcome.FROM_CACHE)) + .or(is(TaskOutcome.UP_TO_DATE))); + } + Result assertCollectOutcomeUpToDate() { return this.assertOutcome(":collectRequirements", TaskOutcome.UP_TO_DATE);