Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions MAVEN_LINTER_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Maven Linter Quick Start

## Enable (pom.xml)
```xml
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<executions>
<execution>
<id>lint-plugin</id>
<goals>
<goal>lint</goal>
</goals>
</execution>
</executions>
</plugin>
```

## Run
- `mvn verify`
- `mvn deploy`
- `mvn install`
- `mvn dsf-maven-plugin:lint`

## Parameters
- Skip: `-Ddsf.lint.skip=true`
- Do not fail build: `-Ddsf.lint.failOnErrors=false`
- HTML Report: `-Ddsf.lint.html=true`
- JSON Report: `-Ddsf.lint.json=true`
- Verbose Logs: `-Ddsf.lint.verbose=true`
- Report path: `-Ddsf.lint.reportPath=target/dsf-linter-report`
- Exclusion rules: `-Ddsf.lint.exclusionsFile=path/to/dsf-linter-exclusions.json`

## Excluding Issues

Users often encounter known, intentional, or external findings that clutter reports and make triage harder.
The exclusion system lets you suppress specific lint items from HTML and JSON reports without touching the plugin source.

### Option A — Auto-discovery (recommended)

Place a file named `dsf-linter-exclusions.json` in your project root. The plugin picks it up automatically — no extra configuration needed.

### Option B — Explicit file

Pass the path via the Maven property:

```bash
mvn verify -Ddsf.lint.exclusionsFile=path/to/my-exclusions.json
```

Or configure it permanently in `pom.xml`:

```xml
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<configuration>
<exclusionsFile>${project.basedir}/dsf-linter-exclusions.json</exclusionsFile>
</configuration>
<executions>
<execution>
<id>lint-plugin</id>
<goals><goal>lint</goal></goals>
</execution>
</executions>
</plugin>
```

### Exclusion file format

```json
{
"affectsExitStatus": false,
"rules": [
{ "type": "BPMN_PROCESS_HISTORY_TIME_TO_LIVE_MISSING" },
{ "severity": "WARN", "file": "update-allow-list.bpmn" },
{ "messageContains": "optional field" }
]
}
```

**Rule fields** (AND-combined within a rule; multiple rules are OR-combined):

| Field | Match | Example |
|---|---|---|
| `type` | Exact, case-insensitive `LintingType` name | `"BPMN_PROCESS_HISTORY_TIME_TO_LIVE_MISSING"` |
| `severity` | Exact, case-insensitive severity | `"WARN"`, `"ERROR"`, `"INFO"` |
| `file` | Case-insensitive substring of the file name | `"update-allow-list"` |
| `messageContains` | Case-insensitive substring of the description | `"optional field"` |

**`affectsExitStatus`:**

| Value | Behaviour |
|---|---|
| `false` *(default)* | Excluded items are fully suppressed — hidden from reports **and** not counted towards build failure |
| `true` | Excluded items are hidden from reports, but their error count **still** causes a build failure |
39 changes: 23 additions & 16 deletions dsf-bpe/dsf-bpe-test-plugin-v1/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,28 @@
</dependencies>

<build>
<plugins>
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate-config-doc</goal>
</goals>
</execution>
</executions>
<configuration>
<configDocPackages>dev.dsf.bpe.test.spring.config</configDocPackages>
</configuration>
</plugin>
</plugins>
<plugins>
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-config-doc</id>
<goals>
<goal>generate-config-doc</goal>
</goals>
</execution>
<execution>
<id>lint-plugin</id>
<goals>
<goal>lint</goal>
</goals>
</execution>
</executions>
<configuration>
<configDocPackages>dev.dsf.bpe.test.spring.config</configDocPackages>
</configuration>
</plugin>
</plugins>
</build>
</project>
35 changes: 21 additions & 14 deletions dsf-bpe/dsf-bpe-test-plugin-v2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,27 @@

<build>
<plugins>
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate-config-doc</goal>
</goals>
</execution>
</executions>
<configuration>
<configDocPackages>dev.dsf.bpe.test.spring.config</configDocPackages>
</configuration>
</plugin>
<plugin>
<groupId>dev.dsf</groupId>
<artifactId>dsf-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-config-doc</id>
<goals>
<goal>generate-config-doc</goal>
</goals>
</execution>
<execution>
<id>lint-plugin</id>
<goals>
<goal>lint</goal>
</goals>
</execution>
</executions>
<configuration>
<configDocPackages>dev.dsf.bpe.test.spring.config</configDocPackages>
</configuration>
</plugin>
</plugins>
</build>
</project>
7 changes: 6 additions & 1 deletion dsf-maven/dsf-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<dependency>
<groupId>dev.dsf.linter</groupId>
<artifactId>linter-core</artifactId>
</dependency>

<dependency>
<groupId>dev.dsf</groupId>
<artifactId>dsf-bpe-process-api-v1</artifactId>
<exclusions>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright 2018-2025 Heilbronn University of Applied Sciences
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.dsf.maven.linter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;

import dev.dsf.linter.DsfLinter;
import dev.dsf.linter.DsfLinter.OverallLinterResult;
import dev.dsf.linter.exclusion.ExclusionConfig;
import dev.dsf.linter.exclusion.ExclusionConfigLoader;
import dev.dsf.linter.input.InputResolver;
import dev.dsf.linter.input.InputResolver.ResolutionResult;
import dev.dsf.linter.logger.Logger;

/**
* Lints DSF process plugin JAR files by validating BPMN processes, FHIR resources and plugin configurations.
* <p>
* Runs after the JAR has been built (default phase: verify) and delegates to the
* <a href="https://github.com/datasharingframework/dsf-linter">DSF Linter</a> core library.
*/
@Mojo(name = "lint", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.NONE, threadSafe = true)
public class LintPluginMojo extends AbstractMojo
{
@Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
private File projectBuildDirectory;

@Parameter(defaultValue = "${project.build.finalName}", readonly = true, required = true)
private String finalName;

@Parameter(property = "dsf.lint.reportPath")
private File reportPath;

@Parameter(property = "dsf.lint.html", defaultValue = "false")
private boolean generateHtmlReport;

@Parameter(property = "dsf.lint.json", defaultValue = "false")
private boolean generateJsonReport;

@Parameter(property = "dsf.lint.failOnErrors", defaultValue = "true")
private boolean failOnErrors;

@Parameter(property = "dsf.lint.skip", defaultValue = "false")
private boolean skip;

@Parameter(property = "dsf.lint.verbose", defaultValue = "false")
private boolean verbose;

/**
* Path to a JSON file containing exclusion rules. Excluded items are hidden from HTML and JSON reports.
* When omitted, the plugin also looks for {@code dsf-linter-exclusions.json} in the project build
* directory automatically.
*
* <p>Example: {@code -Ddsf.lint.exclusionsFile=path/to/dsf-linter-exclusions.json}</p>
*/
@Parameter(property = "dsf.lint.exclusionsFile")
private File exclusionsFile;

@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
if (skip)
{
getLog().info("DSF Linter: skipped");
return;
}

Path jarPath = projectBuildDirectory.toPath().resolve(finalName + ".jar");

if (!Files.exists(jarPath))
{
getLog().warn("DSF Linter: JAR not found at " + jarPath + ", skipping");
return;
}

Logger logger = new MavenLinterLogger(getLog(), verbose);

InputResolver resolver = new InputResolver(logger);
Optional<ResolutionResult> resolutionResult = resolver.resolve(jarPath.toString());

if (resolutionResult.isEmpty())
{
throw new MojoExecutionException("DSF Linter: failed to resolve JAR file: " + jarPath);
}

ResolutionResult resolution = resolutionResult.get();

try
{
Path lintReportPath = reportPath != null ? reportPath.toPath()
: projectBuildDirectory.toPath().resolve("dsf-linter-report");

Files.createDirectories(lintReportPath);

ExclusionConfig exclusionConfig = loadExclusionConfig(resolution.resolvedPath().toAbsolutePath());

DsfLinter.Config config = new DsfLinter.Config(resolution.resolvedPath().toAbsolutePath(),
lintReportPath.toAbsolutePath(), generateHtmlReport, generateJsonReport, failOnErrors,
exclusionConfig, logger);

OverallLinterResult result = new DsfLinter(config).lint();

if (!result.success())
{
String msg = "DSF Linter: " + result.getTotalErrors() + " error(s), " + result.getPluginWarnings()
+ " warning(s)";

if (failOnErrors)
{
throw new MojoFailureException(msg);
}

getLog().warn(msg);
}
else
{
getLog().info("DSF Linter: passed - no errors found");
}
}
catch (IOException e)
{
throw new MojoExecutionException("DSF Linter failed", e);
}
finally
{
if (resolution.requiresCleanup())
{
resolver.cleanup(resolution);
}
}
}

/**
* Loads the exclusion config from an explicit file ({@code dsf.lint.exclusionsFile}) or
* auto-discovers {@code dsf-linter-exclusions.json} in the extracted project directory.
* Returns {@code null} when no exclusion file is found or configured.
*/
private ExclusionConfig loadExclusionConfig(Path projectRoot) throws MojoExecutionException
{
ExclusionConfigLoader loader = new ExclusionConfigLoader();
try
{
if (exclusionsFile != null)
{
ExclusionConfig config = loader.load(exclusionsFile.toPath());
getLog().info("DSF Linter: loaded exclusion rules from " + exclusionsFile
+ " (" + config.getRules().size() + " rule(s), affectsExitStatus="
+ config.isAffectsExitStatus() + ")");
return config;
}

Optional<ExclusionConfig> auto = loader.loadFromProjectRoot(projectRoot);
if (auto.isPresent())
{
ExclusionConfig config = auto.get();
getLog().info("DSF Linter: auto-discovered exclusion rules from "
+ projectRoot.resolve(ExclusionConfigLoader.DEFAULT_FILENAME)
+ " (" + config.getRules().size() + " rule(s), affectsExitStatus="
+ config.isAffectsExitStatus() + ")");
return config;
}

return null;
}
catch (IOException e)
{
throw new MojoExecutionException("DSF Linter: failed to load exclusion config", e);
}
}
}
Loading