← All tasks
javaapache/incubator-kie #6876Not a task: not reproduced

CI :: Build failure : NPE in maven-artifact-plugin:compare with `-pl` builds

envgap__apache__incubator-kie-6876

01 / FAILURE SIGNATURE

As reported upstream

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-artifact-plugin:3.5.1:compare (compare)
Not a benchmark task.
  • In a clean container the reported failure did not reproduce, or the known fix did not make the project run.

02 / ENVIRONMENT RECIPE

Base commit
61c0e37b966bb34e50ca12deee906d45b789d9bb
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / ORIGINAL ISSUE TEXT

apache/incubator-kie #6876 · read the original issue
# NPE in maven-artifact-plugin:compare with `-pl` builds

## Symptom

CI build fails with:

```
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-artifact-plugin:3.5.1:compare (compare)
  on project kie-parent: Execution compare of goal
  org.apache.maven.plugins:maven-artifact-plugin:3.5.1:compare failed:
  Cannot invoke "org.apache.maven.project.MavenProject.getBuild()" because "root" is null
```

Reproducible with 3.5.2. **Fixed in 3.6.1** (see Solution below).

https://issues.apache.org/jira/browse/MARTIFACT-79

## Root Cause

The CI workflow (`.github/workflows/ci.yaml`, line 195) runs:

```bash
mvn ... -Dfull -Dreproducible -pl "$pl" install
```

where `$pl` is the list of affected modules (e.g. `kie-parent,kie-api,...`). The root pom (`drools-parent`) is **not** included in `$pl`.

The `-Dreproducible` property activates the `reproducible-build` profile (root `pom.xml`, line 275), which binds `maven-artifact-plugin:compare` to the `install` phase.

In a multi-module reactor, the `compare` goal aggregates results. For the **last** module in the reactor, `CompareMojo.execute()` calls:

```java
checkAgainstReference(artifacts, session.getProjects().size() == 1);
```

Since there are multiple modules, `mono = false`, which leads to:

```java
// CompareMojo.java line 126 (3.5.2)
MavenProject root = mono ? project : getExecutionRoot();
File referenceDir = new File(root.getBuild().getDirectory(), "reference");  // NPE here
```

`getExecutionRoot()` iterates over `session.getProjects()` looking for a project where `isExecutionRoot() == true`. With `-pl`, the root pom (`drools-parent`) is excluded from the reactor, so no project has `isExecutionRoot() == true`, and the method returns `null`.

In 3.6.x the code changed to `session.getTopLevelProject()`, which returns the first project in the reactor (e.g. `kie-parent`) rather than null when the root pom is excluded via `-pl`. This resolves the NPE.

### Code path (3.5.x — broken)

```
AbstractBuildinfoMojo.execute()
  → mono = session.getProjects().size() == 1   // false (multiple -pl modules)
  → last module: calls checkAgainstReference(artifacts, false)
    → CompareMojo.checkAgainstReference()
      → root = getExecutionRoot()               // returns null
      → root.getBuild()                          // NPE
```

```java
// AbstractBuildinfoMojo.java line 342
protected MavenProject getExecutionRoot() {
    for (MavenProject p : session.getProjects()) {
        if (p.isExecutionRoot()) {
            return p;
        }
    }
    return null;  // ← no guard against null
}
```

## Version comparison

| Version | Root resolution method          | NPE with `-pl`? |
|---------|--------------------------------|-----------------|
| 3.5.1   | `getExecutionRoot()`           | Yes             |
| 3.5.2   | `getExecutionRoot()`           | Yes (confirmed) |
| 3.6.1   | `session.getTopLevelProject()` | **No** (confirmed) |

In 3.5.x, `getExecutionRoot()` returns `null` when the root pom is not in the reactor.
In 3.6.x, `session.getTopLevelProject()` returns the first project in the reactor, avoiding the NPE.

## Affected CI step

```yaml
# .github/workflows/ci.yaml line 189-195
- name: "PR CHECK :: BUILD :: Changed and affected modules"
  if: github.event_name == 'pull_request'
  shell: bash
  run: |
    pl=$(paste -sd, "$MAVEN_PL_AFFECTED_FILE")
    [ -z "$pl" ] && echo "No affected modules. Skipping." && exit 0
    mvn --batch-mode --no-transfer-progress -fae -Dsurefire.redirectTestOutputToFile=true -Dfull -Dreproducible -pl "$pl" install
```

Note: the full CI build on `push` (line 200) runs **without** `-pl` and works correctly:

```yaml
- name: "CI :: BUILD :: Full"
  if: github.event_name == 'push'
  run: mvn ... -Dfull -Dreproducible install
```

## Solution

Upgrade `maven-artifact-plugin` to **3.6.1**. No CI workflow changes needed.

In root `pom.xml`:

```xml
<version.maven.artifact.plugin>3.6.1</version.maven.artifact.plugin>
```

### Additional required change

Version 3.6.1's `check-buildplan` goal has an updated plugin database that correctly detects `maven-remote-resources-plugin:3.2.0` as non-reproducible (inherited from the Apache parent POM). Upgrade it to 3.3.0:

```xml
<version.maven-remote-resources-plugin>3.3.0</version.maven-remote-resources-plugin>
```

Without this, the build fails with:

```
[ERROR] plugin with non-reproducible output: org.apache.maven.plugins:maven-remote-resources-plugin:3.2.0, require minimum 3.3.0
```

### Alternative: Add the root pom to `-pl`

If upgrading the plugin is not feasible, adding `.` (the root pom) to `-pl` in the CI workflow also fixes the NPE with 3.5.x:

```bash
mvn ... -Dfull -Dreproducible -pl ".,$pl" install
```

This ensures `drools-parent` is in the reactor so `getExecutionRoot()` finds it. The root pom is `pom`-packaging only, so build overhead is negligible.

### Confirmed

`mvn -Dreproducible -pl kie-parent,kie-api install` succeeds with 3.6.1 **without** adding `.` to `-pl`:

```
[INFO] [Reproducible Builds] rebuild comparison result: 6 files match
[INFO] BUILD SUCCESS
```
Continue on GitHub ↗

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]