Skip to content
Open
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
fetch-depth: 0
persist-credentials: false

- uses: actions/setup-java@v5
- uses: actions/setup-java@v6
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
Expand Down Expand Up @@ -78,7 +78,7 @@ jobs:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-java@v5
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: 17
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
with:
persist-credentials: false

- uses: actions/setup-java@v5
- uses: actions/setup-java@v6
with:
distribution: 'temurin'
java-version: 17
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
script: |
core.setFailed('Not running on main branch, github.ref is ${{ github.ref }}. Please start this workflow only on main')

- uses: actions/setup-java@v5
- uses: actions/setup-java@v6
with:
distribution: "temurin"
java-version: 17
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [#77](https://github.com/itsallcode/openfasttrace-gradle/issues/77)
- Add support for OpenFastTrace plugin dependencies
- [PR #80](https://github.com/itsallcode/openfasttrace-gradle/pull/80)
- Fix JavaDoc warnings and let build fail on warnings
- [PR #82](https://github.com/itsallcode/openfasttrace-gradle/pull/82)
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ You can configure the following properties:
* `filteredArtifactTypes`: Use only the listed artifact types during tracing
* `filterWantedStatuses`: Import only specification items that have a status contained in the list of statuses. Possible values: `draft`, `proposed`, `approved`, `rejected`. See the [OFT user guide](https://github.com/itsallcode/openfasttrace/blob/main/doc/user_guide/user_guide.md#filtering-by-status) for details.

### Using OpenFastTrace Plugins

OpenFastTrace extension plugins can be added with the `pluginDependencies` property. The dependencies are added to the classpath used by requirement collection and tracing:

```groovy
repositories {
mavenCentral()
}

requirementTracing {
pluginDependencies = ['org.itsallcode:openfasttrace-asciidoc-plugin:0.3.0']
}
```

These are OpenFastTrace extension plugins, not Gradle build plugins. Plugin JARs must provide the appropriate OpenFastTrace service descriptors and should not include a duplicate incompatible `openfasttrace-api` dependency. Plugin discovery remains additive to OpenFastTrace's built-in plugin directory.

### 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.
Expand Down
16 changes: 16 additions & 0 deletions example-projects/plugin-config/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id "base"
id 'org.itsallcode.openfasttrace'
}

repositories {
mavenCentral()
}

requirementTracing {
failBuild = true
inputDirectories = files('doc', 'src')
reportFormat = 'plain'
// Once we upgrade this, we can remove RegexMatchingImporterFactory
pluginDependencies = ['org.itsallcode:openfasttrace-asciidoc-plugin:0.3.0']
}
6 changes: 6 additions & 0 deletions example-projects/plugin-config/doc/spec.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
== AsciiDoc Spec

[.specitem, oft-sid="dsn~asciidoc-exampleB~1", oft-needs="impl,test"]
=== Example AsciiDoc Requirement

Example AsciiDoc requirement
6 changes: 6 additions & 0 deletions example-projects/plugin-config/doc/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# MarkDown Tracing Example
`dsn~md-exampleA~1`

Example MarkDown requirement

Needs: impl, test
1 change: 1 addition & 0 deletions example-projects/plugin-config/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'plugin-config'
5 changes: 5 additions & 0 deletions example-projects/plugin-config/src/Source.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// [impl->dsn~md-exampleA~1]
// [impl->dsn~asciidoc-exampleB~1]
class Source
{
}
5 changes: 5 additions & 0 deletions example-projects/plugin-config/src/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// [test->dsn~md-exampleA~1]
// [test->dsn~asciidoc-exampleB~1]
class Test
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.itsallcode.openfasttrace.api.importer;

import java.util.Collection;

/**
* Compatibility shim for RegexMatchingImporterFactory which was renamed to
* AbstractRegexMatchingImporterFactory in OpenFastTrace 4.5.0.
* <p>
* Shim can be removed when the following issue is fixed:
* <a href="https://github.com/itsallcode/openfasttrace-asciidoc-plugin/issues/27">
* itsallcode/openffasttrace-asciidoc-plugin # 27
* </a>
* </p>
* Copied from
* <a href=
* "https://github.com/itsallcode/openfasttrace-maven-plugin/blob/main/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java">OpenFastTrace
* Maven Plugin</a>
*
* @deprecated use {@link AbstractRegexMatchingImporterFactory} instead.
*/
@Deprecated(since = "3.2.0", forRemoval = true)
@SuppressWarnings("java:S118") // Shim class. Ignore name convention.
public abstract class RegexMatchingImporterFactory extends AbstractRegexMatchingImporterFactory
{
/**
* Constructs a new RegexMatchingImporterFactory with the specified file extensions.
*
* @param extensions
* the file extensions to be associated with this importer factory
* @deprecated use {@link AbstractRegexMatchingImporterFactory} instead.
*/
@Deprecated(since = "3.2.0", forRemoval = true)
protected RegexMatchingImporterFactory(final String... extensions)
{
super(extensions);
}

/**
* Constructs a new RegexMatchingImporterFactory with the specified file extensions.
*
* @param extensions
* the file extensions to be associated with this importer factory
* @deprecated use {@link AbstractRegexMatchingImporterFactory} instead.
*/
@Deprecated(since = "3.2.0", forRemoval = true)
protected RegexMatchingImporterFactory(final Collection<String> extensions)
{
super(extensions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ private static TaskProvider<CollectTask> createCollectTask(final Project rootPro
task.setGroup(TASK_GROUP_NAME);
task.setDescription("Collect requirements and generate specobject file");
task.getInputDirectories().set(getAllInputDirectories(rootProject.getAllprojects()));
task.getPluginFiles().from(
getPluginDependencies(rootProject, rootProject.getAllprojects()));
task.getOutputFile().set(
rootProject.getLayout().getBuildDirectory().file("reports/requirements.xml"));
task.getPathConfig().set(getPathConfig(rootProject.getAllprojects()));
Expand Down Expand Up @@ -108,6 +110,8 @@ private static void configureTask(final Project rootProject,
task.getReportFormat().set(config.getReportFormat());
task.getImportedRequirements()
.from(getImportedRequirements(rootProject, rootProject.getAllprojects()));
task.getPluginFiles().from(
getPluginDependencies(rootProject, rootProject.getAllprojects()));
task.getFilteredArtifactTypes().set(config.getFilteredArtifactTypes());
task.getFilteredTags().set(config.getFilteredTags());
task.getFilterAcceptsItemsWithoutTag().set(config.getFilterAcceptsItemsWithoutTag());
Expand Down Expand Up @@ -156,11 +160,38 @@ private static ConfigurableFileCollection getImportedRequirements(final Project
private static Configuration getImportedRequirements(final Project project)
{
final String CONFIG_NAME = "oftRequirementConfig";
final Configuration configuration = project.getConfigurations().create(CONFIG_NAME);
getConfig(project).getImportedRequirements().get().forEach(dependency -> {
return getOrCreateConfiguration(project, CONFIG_NAME,
getConfig(project).getImportedRequirements().get());
}

private static ConfigurableFileCollection getPluginDependencies(final Project rootProject,
final Set<Project> allProjects)
{
return rootProject.files(allProjects.stream()
.map(OpenFastTracePlugin::getPluginDependencies)
.toList());
}

private static Configuration getPluginDependencies(final Project project)
{
final String CONFIG_NAME = "oftPluginConfig";
return getOrCreateConfiguration(project, CONFIG_NAME, getConfig(project).getPluginDependencies().get());
}

private static Configuration getOrCreateConfiguration(final Project project, final String configurationName,
final List<Object> dependencies)
{
final Configuration existingConfiguration = project.getConfigurations().findByName(configurationName);
if (existingConfiguration != null)
{
return existingConfiguration;
}

final Configuration configuration = project.getConfigurations().create(configurationName);
dependencies.forEach(dependency -> {
LOG.info("Adding dependency {} with configuration {} to project {}", dependency,
CONFIG_NAME, project);
project.getDependencies().add(CONFIG_NAME, dependency);
configurationName, project);
project.getDependencies().add(configurationName, dependency);
});
return configuration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class TracingConfig
private final ConfigurableFileCollection inputDirectories;
private final RegularFileProperty reportFile;
private final ListProperty<Object> importedRequirements;
private final ListProperty<Object> pluginDependencies;
private final SetProperty<String> filteredTags;
private final SetProperty<String> filteredArtifactTypes;
private final SetProperty<String> filterWantedStatuses;
Expand All @@ -35,7 +36,7 @@ public class TracingConfig

/**
* Creates a tracing configuration with the plugin defaults.
*
*
* @param project
* the Gradle project owning the configuration
*/
Expand All @@ -49,6 +50,7 @@ public TracingConfig(final Project project)
this.reportFormat = project.getObjects().property(String.class);
this.reportFormat.set(DEFAULT_REPORT_FORMAT);
this.importedRequirements = project.getObjects().listProperty(Object.class);
this.pluginDependencies = project.getObjects().listProperty(Object.class);
this.filteredTags = project.getObjects().setProperty(String.class);
this.filteredArtifactTypes = project.getObjects().setProperty(String.class);
this.filterAcceptsItemsWithoutTag = project.getObjects().property(Boolean.class);
Expand All @@ -62,7 +64,7 @@ public TracingConfig(final Project project)

/**
* Returns the report verbosity property.
*
*
* @return the verbosity property
*/
public Property<ReportVerbosity> getReportVerbosity()
Expand Down Expand Up @@ -120,6 +122,16 @@ public ListProperty<Object> getImportedRequirements()
return importedRequirements;
}

/**
* Returns the OpenFastTrace plugin dependencies.
*
* @return the plugin dependencies
*/
public ListProperty<Object> getPluginDependencies()
{
return pluginDependencies;
}

/**
* Returns the tags to include in tracing.
*
Expand Down Expand Up @@ -300,6 +312,17 @@ public void setImportedRequirements(final List<Object> importedRequirements)
this.importedRequirements.set(importedRequirements);
}

/**
* Sets the OpenFastTrace plugin dependencies.
*
* @param pluginDependencies
* dependencies to add to the OpenFastTrace plugin classpath
*/
public void setPluginDependencies(final List<Object> pluginDependencies)
{
this.pluginDependencies.set(pluginDependencies);
}

/**
* Sets the tags to include in tracing.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.ListProperty;
import org.gradle.api.provider.SetProperty;
Expand All @@ -17,6 +18,7 @@
import org.itsallcode.openfasttrace.api.importer.ImportSettings;
import org.itsallcode.openfasttrace.api.importer.tag.config.PathConfig;
import org.itsallcode.openfasttrace.core.*;
import org.itsallcode.openfasttrace.gradle.task.classloader.OftPluginClassLoader;
import org.itsallcode.openfasttrace.gradle.task.config.SerializableTagPathConfig;

/** Gradle task that collects specification items into a specobject file. */
Expand All @@ -35,6 +37,8 @@ public class CollectTask extends DefaultTask
@SuppressWarnings({ "this-escape" })
public final ListProperty<SerializableTagPathConfig> pathConfig = getProject().getObjects()
.listProperty(SerializableTagPathConfig.class);
@SuppressWarnings("this-escape")
private final ConfigurableFileCollection pluginFiles = getProject().files();

/** Creates the task. */
public CollectTask()
Expand Down Expand Up @@ -76,12 +80,28 @@ public ListProperty<SerializableTagPathConfig> getPathConfig()
return pathConfig;
}

/**
* Returns the OpenFastTrace plugin files.
*
* @return the plugin files
*/
@InputFiles
@PathSensitive(PathSensitivity.ABSOLUTE)
public ConfigurableFileCollection getPluginFiles()
{
return pluginFiles;
}

/** Collects specification items and writes the specobject file. */
@TaskAction
public void collectRequirements()
{
createReportOutputDir();
OftPluginClassLoader.runWithPlugins(pluginFiles, this::collectWithPlugins);
}

private void collectWithPlugins()
{
final Oft oft = new OftRunner();
final ImportSettings settings = getImportSettings();
getLogger().info("Importing from {} locations {} and {} path configurations: {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.itsallcode.openfasttrace.api.report.ReportVerbosity;
import org.itsallcode.openfasttrace.core.Oft;
import org.itsallcode.openfasttrace.core.OftRunner;
import org.itsallcode.openfasttrace.gradle.task.classloader.OftPluginClassLoader;

/** Gradle task that traces requirements and writes a report. */
@SuppressWarnings("this-escape")
Expand All @@ -38,6 +39,7 @@ public class TraceTask extends DefaultTask
private final Property<DetailsSectionDisplay> detailsSectionDisplay = getProject().getObjects()
.property(DetailsSectionDisplay.class);
private final ConfigurableFileCollection importedRequirements = getProject().files();
private final ConfigurableFileCollection pluginFiles = getProject().files();
private final SetProperty<String> filteredArtifactTypes = getProject().getObjects()
.setProperty(String.class);
private final SetProperty<String> filteredTags = getProject().getObjects()
Expand Down Expand Up @@ -123,6 +125,18 @@ public ConfigurableFileCollection getImportedRequirements()
return importedRequirements;
}

/**
* Returns the OpenFastTrace plugin files.
*
* @return the plugin files
*/
@InputFiles
@PathSensitive(PathSensitivity.ABSOLUTE)
public ConfigurableFileCollection getPluginFiles()
{
return pluginFiles;
}

/**
* Returns the artifact type filter.
*
Expand Down Expand Up @@ -201,6 +215,11 @@ private boolean shouldFailBuild()
public void trace()
{
createReportOutputDir();
OftPluginClassLoader.runWithPlugins(pluginFiles, this::traceWithPlugins);
}

private void traceWithPlugins()
{
final Oft oft = new OftRunner();
final ImportSettings importSettings = getImportSettings();
final List<SpecificationItem> importedItems = oft.importItems(importSettings);
Expand Down
Loading
Loading