Skip to content

rewrite / verify hard-fail with no rules file — should pass through unfiltered #69

Description

@miroslavpojer

Describe the bug

When a project has neither jmf.globalRules nor a jmf-rules.txt file, the Maven rewrite
and verify goals abort the build:

Failed to execute goal io.github.moranaapps:jacoco-method-filter-maven-plugin:2.2.0:rewrite
  (jmf-rewrite) on project X: Configuration problems detected:
  - Rules configuration missing at: .../jmf-rules.txt
    Solution: execute 'mvn io.github.moranaapps:jacoco-method-filter-maven-plugin:2.2.0:init-rules'

The CLI enforces the same rule — CoverageRewriterCli.checkConfig:

} else if (cfg.globalRules.isEmpty && cfg.localRules.isEmpty) {
  failure("At least one of --global-rules or --local-rules must be specified")
}

Expected: no rules configured ⇒ every class passes through unchanged ⇒ a normal, unfiltered
JaCoCo report.
Method filtering is an opt-in refinement; its absence should not be a build error.

Why this matters

It blocks wiring the plugin into a shared / parent POM for a group of projects. The moment the
coverage profile runs, every project that has not yet authored a jmf-rules.txt fails its build.
The only escapes are -Djmf.skip=true (turns the whole thing off, per project) or forcing every
project to commit a rules file before it is ready. That defeats the point of a common, centrally
managed coverage setup.

This is a regression AND an internal inconsistency

  1. CHANGELOG [1.0.0] (2024-08-24) lists "Quiet-exit mode for builds without a rules file" as
    a shipped feature. The 2.0.0 rework (--global-rules / --local-rules, the checkConfig
    "at least one" requirement, the new Maven mojos) dropped it.

  2. The sbt plugin already does the right thing. JacocoFilterPlugin.jmfRewrite / jmfVerify:

    val hasRulesConfig = globalRules.isDefined || localRules.exists(_.exists) || rulesFile.exists
    if (!hasRulesConfig) { log.warn(s"[jmf] rules file missing: ...; skipping."); classesIn }

    It warns and proceeds on the original classes. Only the Maven plugin + CLI hard-fail.

  3. The core engine already supports it. Rules.loadAll(None, None) returns Seq.empty;
    CoverageRewriter.run with an empty rule set walks every class, marks 0 methods, and writes a
    complete pass-through copy to --out:
    [info] Processed N class file(s), marked 0 method(s).

So the "no rules" path is blocked only by two artificial guards (CLI checkConfig +
RewriteMojo/VerifyMojo.checkInputs). The desired behaviour needs no core changes.

Docs currently document it as an error

docs/rules-reference.md → CLI Reference:

| --global-rules <path\|url> | At least one of the two | Global rules file path or URL |
| --local-rules <path> | At least one of the two | Local rules file path |

Steps to reproduce

Minimal Maven project, jacoco-method-filter-maven-plugin:2.2.0 rewrite execution bound in a
profile, no jmf-rules.txt:

mvn -Pcoverage clean verify

BUILD FAILURE at jmf:rewrite, exit 1. Reproduced on Maven 3.9.16 / Temurin JDK 1.8.0_504 /
Windows 11 / JMF 2.2.0
(a parent+consumer repro is available on request).

Expected state

  • rewrite with no rules → BUILD SUCCESS; target/classes-filtered holds an unchanged copy of
    every class; a later report produces a normal JaCoCo report.
  • verify with no rules → BUILD SUCCESS, "0 methods matched".
  • CLI --in … --out … with neither --global-rules nor --local-rules → exit 0, pass-through.
  • Maven and sbt behave identically for the no-rules case.

Proposed fix

1. CLI — CoverageRewriterCli.checkConfig
Remove the cfg.globalRules.isEmpty && cfg.localRules.isEmptyfailure(...) branch. With zero
sources, run() already produces a pass-through copy and logs Loaded 0 rule(s).

2. CLI — missing --local-rules file
Rules.loadFromPath throws NoSuchFileException for a non-existent path. Preferred: when a
--local-rules path does not exist, emit
[warn] local rules file not found: <path> — proceeding with 0 local rules and treat it as empty.
(Otherwise wrappers must be careful never to pass the flag for an absent file.)

3. Maven — RewriteMojo / VerifyMojo

  • Drop the hasRulesConfig hard-fail in checkInputs(). When nothing resolves, log
    [INFO] No JMF rules configured (no jmf.globalRules, no <basedir>/jmf-rules.txt) — classes pass through unfiltered.
    and continue.
  • Only pass --local-rules to the CLI when the file actually exists. (localRules is never null
    — it has a default of ${project.basedir}/jmf-rules.txt — so today --local-rules <nonexistent>
    is always sent.)
  • Net effect: rewrite still populates target/classes-filtered (pass-through), so the resources
    overlay and report downstream are unaffected.

4. Opt-in strictness (optional but recommended)
Add jmf.requireRules (Maven) / --require-rules (CLI), default false, that restores today's
"fail when no rules" behaviour for teams that want it enforced in CI.

5. Docs

  • docs/rules-reference.md CLI table: --global-rules / --local-rulesOptional. With neither,
    all classes pass through unfiltered (no-op).
  • maven-plugin/README.md: document the pass-through behaviour and jmf.requireRules.
  • CHANGELOG.md: note restoration of the v1.0.0 "quiet-exit mode for builds without a rules file".

Acceptance criteria

  1. mvn …:rewrite with no jmf.globalRules and no jmf-rules.txtBUILD SUCCESS;
    target/classes-filtered contains an unchanged copy of every .class; a subsequent report
    yields a normal JaCoCo report.
  2. mvn …:verify with no rules → BUILD SUCCESS, 0 matched methods.
  3. CLI --in … --out … with no rules sources → exit 0, pass-through copy.
  4. Today's behaviour is still reachable via jmf.requireRules=true / --require-rules.
  5. sbt and Maven behave identically for the no-rules case.
  6. Integration tests cover the no-rules path for CLI, Maven, and sbt.
  7. Docs and CHANGELOG updated.

Impact / Severity

High — blocks adoption via a shared parent POM, which is the intended model for org-wide
standardisation.

Attachments / Evidence

  • CoverageRewriterCli.scala checkConfig (the CLI guard)
  • RewriteMojo.java / VerifyMojo.java checkInputs() (the Maven guard)
  • JacocoFilterPlugin.scala jmfRewrite / jmfVerify (the sbt path that already warns-and-skips)
  • CoverageRewriter.scala run() + Rules.loadAll (core already yields a pass-through on empty rules)
  • CHANGELOG.md [1.0.0] — "Quiet-exit mode for builds without a rules file"

Desktop

  • OS: Windows 11
  • Maven: 3.9.16
  • JDK: Eclipse Temurin 1.8.0_504
  • JMF: 2.2.0 (maven plugin + core)

Related / References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions