diff --git a/.github/workflows/broken_links_checker.yml b/.github/workflows/broken_links_checker.yml index 4343dff..6b5a198 100644 --- a/.github/workflows/broken_links_checker.yml +++ b/.github/workflows/broken_links_checker.yml @@ -9,6 +9,9 @@ on: jobs: linkChecker: runs-on: ubuntu-latest + permissions: + contents: read + steps: - uses: actions/checkout@v4 - name: Configure broken links checker diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3f199a..339e836 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,13 @@ on: jobs: matrix-build: + permissions: + contents: read + runs-on: ubuntu-latest + defaults: + run: + shell: "bash" strategy: matrix: java: [11, 17, 21] @@ -34,7 +40,7 @@ jobs: 21 cache: 'gradle' - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/actions/wrapper-validation@v3 - name: Cache SonarQube packages uses: actions/cache@v4 @@ -47,13 +53,6 @@ jobs: run: | ./gradlew build --warning-mode=summary -PjavaVersion=${{ matrix.java }} - - name: Publish Test Report for Java ${{ matrix.java }} - uses: scacap/action-surefire-report@v1 - if: ${{ always() && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' }} - with: - report_paths: '**/build/test-results/*/TEST-*.xml' - github_token: ${{ secrets.GITHUB_TOKEN }} - - name: Sonar analysis if: ${{ env.DEFAULT_JAVA == matrix.java && env.SONAR_TOKEN != null }} run: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a0ff50b..f830c3e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,9 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + contents: read + security-events: write strategy: fail-fast: true @@ -29,6 +32,8 @@ jobs: java-version: 11 cache: 'gradle' + - uses: gradle/actions/wrapper-validation@v3 + - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index f9dbe56..3a8264b 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,6 +1,5 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.builder.annotationPath.allLocations=disabled -org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=ignore org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=javax.annotation.Nonnull diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ea623..68ae21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.2.0] - unreleased + +- [Issue #41](https://github.com/itsallcode/openfasttrace-gradle/issues/41) + - Upgrade to [OpenFastTrace 3.8.0](https://github.com/itsallcode/openfasttrace/releases/tag/3.8.0) + - Add support for `detailsSectionDisplay` configuration + + ## [1.1.0] - 2023-03-11 - [PR #35](https://github.com/itsallcode/openfasttrace-gradle/pull/35): diff --git a/README.md b/README.md index 2bfcac8..3e85613 100644 --- a/README.md +++ b/README.md @@ -14,24 +14,55 @@ Gradle plugin for the requirement tracing suite [OpenFastTrace](https://github.c ## Usage -1. Preconditions: Java 11 and Gradle 7.0 +1. Preconditions: Java 11 and Gradle 8.5 1. Add plugin [`org.itsallcode.openfasttrace`](https://plugins.gradle.org/plugin/org.itsallcode.openfasttrace) to your project: ```gradle plugins { - id "org.itsallcode.openfasttrace" version "1.1.0" + id "org.itsallcode.openfasttrace" version "1.2.0" } ``` 1. Configure your project, see [examples](https://github.com/itsallcode/openfasttrace-gradle/tree/main/example-projects) 1. Run - ```bash + ```sh ./gradlew traceRequirements ``` 1. Report is written to `build/reports/tracing.txt` by default. +### General Configuration + +```gradle +requirementTracing { + inputDirectories = files('custom-dir') + reportFile = file('build/custom-report.txt') + reportFormat = 'plain' + reportVerbosity = 'failure_details' + detailsSectionDisplay = 'collapse' +} +``` + +You can configure the following properties: + +* `inputDirectories`: Files or directories to import +* `reportFile`: Path to the report file +* `reportFormat`: Format of the report + * `plain` - Plain Text (default) + * `html` - HTML +* `reportVerbosity`: Report verbosity + * `quiet` - no output (in case only the return code is used) + * `minimal` - display ok or not ok + * `summary` - display only the summary, not individual specification items + * `failures` - list of defect specification items + * `failure_summaries` - list of summaries for defect specification items + * `failure_details` - summaries and details for defect specification items (default) + * `all` - summaries and details for all specification items +* `detailsSectionDisplay`: Initial display status of the details section in the HTML report + * `collapse` - hide details (default) + * `expand` - show details + ### Configuring the short tag importer The short tag importer allows omitting artifact type and the covered artifact type. Optionally you can add a prefix to the item name, e.g. a common module name. @@ -68,14 +99,14 @@ In bigger setups you might want to share requirements between multiple projects. Example: The Software Architecture Design project `swad` contains overall requirements that must be fulfilled by projects `component-a` and `component-b`. -1. The `swad` project publishes its requirements as a zip file `swad-req` to a maven repository. +1. The `swad` project publishes its requirements as a zip file `swad-req` to a Maven repository. 1. Both components import these requirements and cover them in their Software Detailed Design (swdd). -1. Both components publish their requirements as artefacts `component-a-req` and `component-b-req` to the shared maven repository. +1. Both components publish their requirements as artefacts `component-a-req` and `component-b-req` to the shared Maven repository. 1. A regular job check that all requirements from `swad` are covered by tracing `swad-req`, `component-a-req` and `component-b-req`. -#### Publishing requirements to a maven repository +#### Publishing requirements to a Maven repository -If you want to publish requirements to a maven repository you can use the following configuration in your `build.gradle`: +If you want to publish requirements to a Maven repository you can use the following configuration in your `build.gradle`: ```gradle plugins { @@ -122,7 +153,7 @@ See [dependency-config](https://github.com/itsallcode/openfasttrace-gradle/tree/ ## Development -```bash +```sh git clone https://github.com/itsallcode/openfasttrace-gradle-gradle.git ./gradlew check # Test report: build/reports/tests/index.html @@ -150,19 +181,19 @@ Import into eclipse using [buildship](https://projects.eclipse.org/projects/tool ### Check if dependencies are up-to-date -```bash +```sh ./gradlew dependencyUpdates ``` ### Check dependencies for vulnerabilities -```bash +```sh ./gradlew ossIndexAudit ``` ### Run sonar analysis -```bash +```sh ./gradlew clean sonar --info -Dsonar.token=[token] ``` @@ -185,7 +216,7 @@ gradle.publish.secret = 1. Commit and push changes. 1. Run - ```bash + ```sh ./gradlew clean publishPlugins --info ``` diff --git a/build.gradle b/build.gradle index d24e038..525db98 100644 --- a/build.gradle +++ b/build.gradle @@ -3,10 +3,10 @@ plugins { id 'jacoco' id 'signing' id 'com.gradle.plugin-publish' version '1.2.1' - id 'org.sonarqube' version '4.4.1.3373' + id 'org.sonarqube' version '5.0.0.4638' id 'pl.droidsonroids.jacoco.testkit' version '1.0.12' - id 'com.github.ben-manes.versions' version '0.50.0' - id 'org.sonatype.gradle.plugins.scan' version '2.7.0' + id 'com.github.ben-manes.versions' version '0.51.0' + id 'org.sonatype.gradle.plugins.scan' version '2.8.1' } repositories { @@ -15,13 +15,13 @@ repositories { apply from: 'gradle/workAroundJacocoGradleTestKitIssueOnWindows.gradle' -version = '1.1.0' +version = '1.2.0' group = 'org.itsallcode' ext { gradlePluginId = 'org.itsallcode.openfasttrace' - oftVersion = '3.7.1' - junitVersion = '5.10.1' + oftVersion = '3.8.0' + junitVersion = '5.10.2' if (project.hasProperty('oftSourceDir')) { oftSourceDir = file(project.oftSourceDir) useOftSources = oftSourceDir.exists() @@ -60,17 +60,28 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testImplementation "org.hamcrest:hamcrest-core:2.2" + testImplementation "com.jparams:to-string-verifier:1.4.8" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" } +def getJavaVersion = { + return project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 11 +} + java { toolchain { - def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 11 + def javaVersion = getJavaVersion() languageVersion = JavaLanguageVersion.of(javaVersion) } modularity.inferModulePath = false } +tasks.withType(JavaCompile) { + options.compilerArgs << '-Xlint:all' + options.compilerArgs << '-Werror' + options.encoding = 'UTF-8' +} + javadoc { failOnError = true options.addBooleanOption('html5', true) @@ -79,11 +90,13 @@ javadoc { task compileOft(type: Exec) { workingDir oftSourceDir + def mavenCommand = 'mvn' if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) { - commandLine 'mvn.cmd', 'resources:resources', 'compiler:compile' - } else { - commandLine "mvn", 'resources:resources', 'compiler:compile' + mavenCommand = 'mvn.cmd' } + commandLine mavenCommand, '-T', '1C', 'package', '-DskipTests', '-Dmaven.test.skip=true', + '-Dmaven.site.skip=true', '-Dmaven.javadoc.skip=true', '-Dmaven.source.skip=true', '-Dossindex.skip=true', + '-Djava.version=' + getJavaVersion() } clean { diff --git a/example-projects/html-report/build.gradle b/example-projects/html-report/build.gradle index f9eb899..8720d4e 100644 --- a/example-projects/html-report/build.gradle +++ b/example-projects/html-report/build.gradle @@ -6,4 +6,5 @@ plugins { requirementTracing { inputDirectories = files('custom-dir') reportFormat = 'html' + detailsSectionDisplay = 'EXPAND' } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49..e644113 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e09..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85..7101f8e 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 diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java b/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java index 24b5874..1628160 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePlugin.java @@ -29,11 +29,11 @@ public class OpenFastTracePlugin implements Plugin public void apply(final Project rootProject) { LOG.info("Initializing OpenFastTrack plugin for project '{}'", rootProject); - rootProject.allprojects(this::createConfigDsl); + rootProject.allprojects(OpenFastTracePlugin::createConfigDsl); createTasks(rootProject); } - private void createConfigDsl(final Project project) + private static void createConfigDsl(final Project project) { LOG.info("Setting up plugin configuration for project '{}'", project.getName()); @@ -43,14 +43,14 @@ private void createConfigDsl(final Project project) project); } - private void createTasks(final Project rootProject) + private static void createTasks(final Project rootProject) { LOG.info("Creating tasks for project '{}'", rootProject.getName()); final TaskProvider collectTask = createCollectTask(rootProject); createTracingTask(rootProject, collectTask); } - private TaskProvider createCollectTask(final Project rootProject) + private static TaskProvider createCollectTask(final Project rootProject) { return rootProject.getTasks().register("collectRequirements", CollectTask.class, task -> { task.setGroup(TASK_GROUP_NAME); @@ -63,38 +63,42 @@ private TaskProvider createCollectTask(final Project rootProject) }); } - private void createTracingTask(final Project rootProject, + private static void createTracingTask(final Project rootProject, final TaskProvider collectTask) { - rootProject.getTasks().register("traceRequirements", TraceTask.class, task -> { - task.setGroup(TASK_GROUP_NAME); - task.setDescription("Trace requirements and generate tracing report"); - task.dependsOn(collectTask); - final TracingConfig config = getConfig(rootProject); - task.getRequirementsFile().set(collectTask.get().getOutputFile()); - if (config.getReportFile().isPresent()) - { - task.getOutputFile().set(config.getReportFile()); - } - else - { - final String extension = config.getReportFormat().get().equals("html") ? "html" - : "txt"; - task.getOutputFile() - .set(new File(rootProject.getLayout().getBuildDirectory().getAsFile().get(), - "reports/tracing." + extension)); - } - task.getReportVerbosity().set(config.getReportVerbosity()); - task.getReportFormat().set(config.getReportFormat()); - task.getImportedRequirements() - .set(getImportedRequirements(rootProject.getAllprojects())); - task.getFilteredArtifactTypes().set(config.getFilteredArtifactTypes()); - task.getFilteredTags().set(config.getFilteredTags()); - task.getFilterAcceptsItemsWithoutTag().set(config.getFilterAcceptsItemsWithoutTag()); - }); + rootProject.getTasks().register("traceRequirements", TraceTask.class, + task -> configureTask(rootProject, collectTask, task)); + } + + private static void configureTask(final Project rootProject, + final TaskProvider collectTask, final TraceTask task) + { + task.setGroup(TASK_GROUP_NAME); + task.setDescription("Trace requirements and generate tracing report"); + task.dependsOn(collectTask); + final TracingConfig config = getConfig(rootProject); + task.getRequirementsFile().set(collectTask.get().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.getReportVerbosity().set(config.getReportVerbosity()); + task.getReportFormat().set(config.getReportFormat()); + task.getImportedRequirements().set(getImportedRequirements(rootProject.getAllprojects())); + task.getFilteredArtifactTypes().set(config.getFilteredArtifactTypes()); + task.getFilteredTags().set(config.getFilteredTags()); + task.getFilterAcceptsItemsWithoutTag().set(config.getFilterAcceptsItemsWithoutTag()); + task.getDetailsSectionDisplay().set(config.getDetailsSectionDisplay()); } - private Set getAllInputDirectories(final Set allProjects) + private static Set getAllInputDirectories(final Set allProjects) { return allProjects.stream() // .map(project -> getConfig(project).getInputDirectories().getFiles()) // @@ -102,37 +106,37 @@ private Set getAllInputDirectories(final Set allProjects) .collect(toSet()); } - private Set getImportedRequirements(final Set allProjects) + private static Set getImportedRequirements(final Set allProjects) { return allProjects.stream() // - .flatMap(this::getImportedRequirements) // + .flatMap(OpenFastTracePlugin::getImportedRequirements) // .collect(toSet()); } - private Stream getImportedRequirements(final Project project) + private static Stream getImportedRequirements(final Project project) { - final String configurationName = "oftRequirementConfig"; - final Configuration configuration = project.getConfigurations().create(configurationName); + final String CONFIG_NAME = "oftRequirementConfig"; + final Configuration configuration = project.getConfigurations().create(CONFIG_NAME); getConfig(project).getImportedRequirements().get().forEach(dependency -> { LOG.info("Adding dependency {} with configuration {} to project {}", dependency, - configurationName, project); - project.getDependencies().add(configurationName, dependency); + 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(); } - private List getPathConfig(final Set allProjects) + private static List getPathConfig(final Set allProjects) { return allProjects.stream() // - .map(this::getTagPathConfig) // + .map(OpenFastTracePlugin::getTagPathConfig) // .filter(Optional::isPresent) // .map(Optional::get) // .collect(toList()); } - private Optional getTagPathConfig(final Project project) + private static Optional getTagPathConfig(final Project project) { final TagPathConfiguration tagPathConfig = getConfig(project).getTagPathConfig(); if (tagPathConfig.getPathConfig().isEmpty()) @@ -142,7 +146,7 @@ private Optional getTagPathConfig(final Project proje return Optional.of(new SerializableTagPathConfig(tagPathConfig)); } - private TracingConfig getConfig(final Project project) + private static TracingConfig getConfig(final Project project) { return project.getExtensions().getByType(TracingConfig.class); } diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/config/TracingConfig.java b/src/main/java/org/itsallcode/openfasttrace/gradle/config/TracingConfig.java index f51847a..1a8e78a 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/config/TracingConfig.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/config/TracingConfig.java @@ -8,6 +8,7 @@ import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +import org.itsallcode.openfasttrace.api.DetailsSectionDisplay; import org.itsallcode.openfasttrace.api.report.ReportVerbosity; public class TracingConfig @@ -23,6 +24,7 @@ public class TracingConfig private final ListProperty filteredTags; private final ListProperty filteredArtifactTypes; private final Property filterAcceptsItemsWithoutTag; + private final Property detailsSectionDisplay; public TracingConfig(final Project project) { @@ -37,6 +39,8 @@ public TracingConfig(final Project project) this.filteredArtifactTypes = project.getObjects().listProperty(String.class); this.filterAcceptsItemsWithoutTag = project.getObjects().property(Boolean.class); this.filterAcceptsItemsWithoutTag.set(true); + this.detailsSectionDisplay = project.getObjects().property(DetailsSectionDisplay.class); + this.detailsSectionDisplay.set(DetailsSectionDisplay.COLLAPSE); } public Property getReportVerbosity() @@ -79,6 +83,11 @@ public Property getFilterAcceptsItemsWithoutTag() return filterAcceptsItemsWithoutTag; } + public Property getDetailsSectionDisplay() + { + return detailsSectionDisplay; + } + public void setReportVerbosity(final String reportVerbosity) { setReportVerbosity(ReportVerbosity.valueOf(reportVerbosity)); @@ -124,6 +133,11 @@ public void setFilterAcceptsItemsWithoutTag(final boolean filterAcceptsItemsWith this.filterAcceptsItemsWithoutTag.set(filterAcceptsItemsWithoutTag); } + public void setDetailsSectionDisplay(final String detailsSectionDisplay) + { + this.detailsSectionDisplay.set(DetailsSectionDisplay.valueOf(detailsSectionDisplay)); + } + public TagPathConfiguration getTagPathConfig() { return ((ExtensionAware) this).getExtensions().getByType(TagPathConfiguration.class); diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/task/CollectTask.java b/src/main/java/org/itsallcode/openfasttrace/gradle/task/CollectTask.java index 9f18cfb..7dff71a 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/task/CollectTask.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/task/CollectTask.java @@ -4,7 +4,6 @@ import static java.util.stream.Collectors.toList; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.stream.Stream; @@ -13,24 +12,26 @@ import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.SetProperty; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.*; import org.itsallcode.openfasttrace.api.core.Newline; import org.itsallcode.openfasttrace.api.core.SpecificationItem; import org.itsallcode.openfasttrace.api.importer.ImportSettings; import org.itsallcode.openfasttrace.api.importer.tag.config.PathConfig; -import org.itsallcode.openfasttrace.core.ExportSettings; -import org.itsallcode.openfasttrace.core.Oft; -import org.itsallcode.openfasttrace.core.OftRunner; +import org.itsallcode.openfasttrace.core.*; import org.itsallcode.openfasttrace.gradle.task.config.SerializableTagPathConfig; public class CollectTask extends DefaultTask { + // Possible 'this' escape before subclass is fully initialized + @SuppressWarnings("this-escape") public final SetProperty inputDirectories = getProject().getObjects() .setProperty(File.class); + @SuppressWarnings("this-escape") public final RegularFileProperty outputFile = getProject().getObjects().fileProperty(); + // non-transient instance field of a serializable class declared with a + // non-serializable type + // We use only serializable types + @SuppressWarnings({ "serial", "this-escape" }) public final ListProperty pathConfig = getProject().getObjects() .listProperty(SerializableTagPathConfig.class); @@ -53,7 +54,7 @@ public ListProperty getPathConfig() } @TaskAction - public void collectRequirements() throws IOException + public void collectRequirements() { createReportOutputDir(); @@ -68,7 +69,7 @@ public void collectRequirements() throws IOException oft.exportToPath(importedItems, output, getExportSettings()); } - private ExportSettings getExportSettings() + private static ExportSettings getExportSettings() { return ExportSettings.builder() // .outputFormat("specobject") // @@ -104,19 +105,19 @@ private List getPathConfigInternal() if (getLogger().isInfoEnabled()) { getLogger().info("Got {} path configurations:\n{}", paths.size(), - paths.stream().map(this::formatPathConfig).collect(joining("\n"))); + paths.stream().map(CollectTask::formatPathConfig).collect(joining("\n"))); } return paths; } - private String formatPathConfig(final PathConfig config) + private static String formatPathConfig(final PathConfig config) { return " - " + config.getDescription() + " (type " + config.getTagArtifactType() + "): covers '" + config.getCoveredItemArtifactType() + "', prefix: '" + config.getCoveredItemNamePrefix() + "'"; } - private void createReportOutputDir() throws IOException + private void createReportOutputDir() { final File outputDir = getOuputFileInternal().getParentFile(); if (outputDir.exists()) @@ -125,7 +126,7 @@ private void createReportOutputDir() throws IOException } if (!outputDir.mkdirs()) { - throw new IOException("Error creating directory " + outputDir); + throw new IllegalStateException("Error creating directory " + outputDir); } } 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 08e2c1f..2edd2d1 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/task/TraceTask.java @@ -3,7 +3,6 @@ import static java.util.stream.Collectors.toList; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.stream.Stream; @@ -12,21 +11,15 @@ import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.SetProperty; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; -import org.itsallcode.openfasttrace.api.FilterSettings; -import org.itsallcode.openfasttrace.api.ReportSettings; -import org.itsallcode.openfasttrace.api.core.LinkedSpecificationItem; -import org.itsallcode.openfasttrace.api.core.Newline; -import org.itsallcode.openfasttrace.api.core.SpecificationItem; -import org.itsallcode.openfasttrace.api.core.Trace; +import org.gradle.api.tasks.*; +import org.itsallcode.openfasttrace.api.*; +import org.itsallcode.openfasttrace.api.core.*; import org.itsallcode.openfasttrace.api.importer.ImportSettings; import org.itsallcode.openfasttrace.api.report.ReportVerbosity; import org.itsallcode.openfasttrace.core.Oft; import org.itsallcode.openfasttrace.core.OftRunner; +@SuppressWarnings("this-escape") public class TraceTask extends DefaultTask { private final RegularFileProperty requirementsFile = getProject().getObjects().fileProperty(); @@ -34,6 +27,8 @@ public class TraceTask extends DefaultTask private final Property reportVerbosity = getProject().getObjects() .property(ReportVerbosity.class); 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 SetProperty filteredArtifactTypes = getProject().getObjects() @@ -91,8 +86,14 @@ public Property getFilterAcceptsItemsWithoutTag() return filterAcceptsItemsWithoutTag; } + @Input + public Property getDetailsSectionDisplay() + { + return detailsSectionDisplay; + } + @TaskAction - public void trace() throws IOException + public void trace() { createReportOutputDir(); final Oft oft = new OftRunner(); @@ -115,6 +116,7 @@ private ReportSettings getReportSettings() .outputFormat(reportFormat.get()) // .showOrigin(true) // .newline(Newline.UNIX) // + .detailsSectionDisplay(detailsSectionDisplay.get()) // .build(); } @@ -145,7 +147,7 @@ private List getAllImportFiles() return Stream.concat(importedRequirementPaths, inputDirPaths).collect(toList()); } - private void createReportOutputDir() throws IOException + private void createReportOutputDir() { final File outputDir = getOutputFileInternal().getParentFile(); if (outputDir.exists()) @@ -154,7 +156,7 @@ private void createReportOutputDir() throws IOException } if (!outputDir.mkdirs()) { - throw new IOException("Error creating directory " + outputDir); + throw new IllegalStateException("Error creating directory " + outputDir); } } diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfig.java b/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfig.java index f4fc4b5..6b9e1a6 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfig.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfig.java @@ -14,7 +14,10 @@ public class SerializableTagConfig implements Serializable { private static final long serialVersionUID = 1L; - + // non-transient instance field of a serializable class declared with a + // non-serializable type + // We use only serializable types + @SuppressWarnings("serial") private final Set paths; private final String coveredItemArtifactType; private final String tagArtifactType; @@ -27,7 +30,7 @@ public SerializableTagConfig(final TagConfig tagConfig) tagArtifactType = tagConfig.tagArtifactType; coveredItemNamePrefix = tagConfig.coveredItemNamePrefix != null ? tagConfig.coveredItemNamePrefix - : tagConfig.getProjectName() + "."; + : (tagConfig.getProjectName() + "."); } public List getPaths() diff --git a/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfig.java b/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfig.java index a6864cb..f34603e 100644 --- a/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfig.java +++ b/src/main/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfig.java @@ -14,6 +14,10 @@ public class SerializableTagPathConfig implements Serializable { private static final long serialVersionUID = 1L; + // non-transient instance field of a serializable class declared with a + // non-serializable type + // We use only serializable types + @SuppressWarnings("serial") private final List tagConfigs; public SerializableTagPathConfig(final TagPathConfiguration tagPathConfig) @@ -35,6 +39,6 @@ public Stream getPathConfig() @Override public String toString() { - return "SerializableTagPathConfig [" + tagConfigs + "]"; + return "SerializableTagPathConfig [tagConfigs=" + tagConfigs + "]"; } } diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/GradleTestConfig.java b/src/test/java/org/itsallcode/openfasttrace/gradle/GradleTestConfig.java index fdee119..aef7a2f 100644 --- a/src/test/java/org/itsallcode/openfasttrace/gradle/GradleTestConfig.java +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/GradleTestConfig.java @@ -6,7 +6,7 @@ public enum GradleTestConfig /** * Version 8.5 is the first one that supports running on Java 21 */ - EIGHT_FIVE("8.5"); + EIGHT_FIVE("8.5"), EIGHT_SIX("8.6"), EIGHT_SEVEN("8.7"); public final String gradleVersion; diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java index f840c20..8cbca16 100644 --- a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java @@ -8,13 +8,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.nio.file.*; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipException; @@ -22,9 +18,7 @@ import org.gradle.api.logging.Logging; import org.gradle.internal.impldep.org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.gradle.internal.impldep.org.apache.commons.compress.archivers.zip.ZipFile; -import org.gradle.testkit.runner.BuildResult; -import org.gradle.testkit.runner.GradleRunner; -import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.testkit.runner.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.slf4j.Logger; @@ -125,8 +119,9 @@ void testHtmlReportConfig(final GradleTestConfig config) throws IOException "traceRequirements"); assertEquals(TaskOutcome.SUCCESS, buildResult.task(":traceRequirements").getOutcome()); assertFileContent(HTML_REPORT_CONFIG_DIR.resolve("build/reports/tracing.html"), - "", // - ""); + "", + "", + "
"); } @ParameterizedTest diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagConfigTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagConfigTest.java new file mode 100644 index 0000000..9835d3b --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagConfigTest.java @@ -0,0 +1,14 @@ +package org.itsallcode.openfasttrace.gradle.config; + +import org.junit.jupiter.api.Test; + +import com.jparams.verifier.tostring.ToStringVerifier; + +class TagConfigTest +{ + @Test + void testToString() + { + ToStringVerifier.forClass(TagConfig.class).withIgnoredFields("project").verify(); + } +} diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagPathConfigurationTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagPathConfigurationTest.java new file mode 100644 index 0000000..89f973e --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/config/TagPathConfigurationTest.java @@ -0,0 +1,14 @@ +package org.itsallcode.openfasttrace.gradle.config; + +import org.junit.jupiter.api.Test; + +import com.jparams.verifier.tostring.ToStringVerifier; + +class TagPathConfigurationTest +{ + @Test + void testToString() + { + ToStringVerifier.forClass(TagPathConfiguration.class).withIgnoredFields("project").verify(); + } +} diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfigTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfigTest.java new file mode 100644 index 0000000..a4f5aea --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagConfigTest.java @@ -0,0 +1,14 @@ +package org.itsallcode.openfasttrace.gradle.task.config; + +import org.junit.jupiter.api.Test; + +import com.jparams.verifier.tostring.ToStringVerifier; + +class SerializableTagConfigTest +{ + @Test + void testToString() + { + ToStringVerifier.forClass(SerializableTagConfig.class).verify(); + } +} diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfigTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfigTest.java new file mode 100644 index 0000000..b81bd28 --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/task/config/SerializableTagPathConfigTest.java @@ -0,0 +1,14 @@ +package org.itsallcode.openfasttrace.gradle.task.config; + +import org.junit.jupiter.api.Test; + +import com.jparams.verifier.tostring.ToStringVerifier; + +class SerializableTagPathConfigTest +{ + @Test + void testToString() + { + ToStringVerifier.forClass(SerializableTagPathConfig.class).verify(); + } +}