Skip to content
Merged
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ This project uses [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Fixed

- **sbt plugin restored to sbt 1.x compatibility.** v2.4.0 used the 3-arg
`Command.process` overload (added in sbt 1.10.0), so `jacocoCleanAll` /
`jacocoReportAll` failed on sbt 1.9.x with
`java.lang.NoSuchMethodError: sbt.Command$.process(...)`. Switched back to the
2-arg overload and pinned `pluginCrossBuild / sbtVersion` to `1.9.0` so the
plugin can no longer compile against newer-than-1.9 sbt APIs.
([#76](https://github.com/MoranaApps/jacoco-method-filter/issues/76))

## [2.4.0] — 2026-09-01

### Changed
Expand Down
5 changes: 5 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ lazy val sbtPlugin = (project in file("sbt-plugin"))
// sbt plugins are built with Scala 2.12 for sbt 1.x
scalaVersion := "2.12.21",
crossScalaVersions := Seq("2.12.21"),
// Compile against the oldest supported sbt so the plugin cannot accidentally
// depend on APIs newer than sbt 1.9 (e.g. the 3-arg Command.process added in
// sbt 1.10.0, which broke JMF on sbt 1.9.x with NoSuchMethodError). The dev
// build itself still runs on the sbt.version in project/build.properties.
pluginCrossBuild / sbtVersion := "1.9.0",
// Prevent publishing the legacy (non-suffixed) Maven artifacts like
// `jacoco-method-filter-sbt-<ver>.jar` which Sonatype Central cannot associate
// with the sbt-plugin coordinates `jacoco-method-filter-sbt_2.12_1.0`.
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ bash integration-tests/test-sbt-init-rules.sh
| `test-cli-verify.sh` | CLI `--verify` mode shows methods that would be filtered |
| `test-cli-verify-unmatched.sh` | CLI `--verify` UNMATCHED RULES report and `--error-on-unmatched` flag |
| `test-sbt-basic.sh` | `examples/sbt-basic` passes tests without filtering, then with filtering + report generation |
| `test-sbt-scala211.sh` | Plugin works on a Scala 2.11 cross-build project (no `NoSuchMethodError`) |
| `test-sbt-19x.sh` | Plugin loads and `jacocoReportAll` runs on sbt 1.9.x — regression guard for the 3-arg `Command.process` (issue #76) |
| `test-sbt-report-custom.sh` | sbt plugin with custom report settings (formats, title, encoding) verifies only specified formats are generated |
| `test-maven-basic.sh` | `examples/maven-basic` (Java) passes tests without and with `-Pcode-coverage` |
| `test-maven-report-custom.sh` | Maven plugin with custom report settings (formats, title, encoding) verifies only specified formats are generated |
Expand All @@ -61,6 +63,9 @@ that has the plugin already enabled, then overlay the source and rules files
from the example. This avoids fragile `sed` edits and ensures dependency
resolution works cleanly in CI.

`fixtures/sbt-scala211/` and `fixtures/sbt-19x/` are self-contained variants
(own source + rules) that pin a specific Scala or sbt version.

## CI Integration

The `integration` job in `.github/workflows/ci.yml` runs `./integration-tests/run-all.sh`,
Expand Down
19 changes: 19 additions & 0 deletions integration-tests/fixtures/sbt-19x/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// CI fixture: minimal single-module project pinned to sbt 1.9.x.
// Guards against regressions that use sbt APIs newer than 1.9 (e.g. the 3-arg
// Command.process overload added in sbt 1.10.0, see issue #76).
lazy val root = (project in file(""))
.enablePlugins(JacocoFilterPlugin)
.settings(
name := "sbt-19x-test",
organization := "io.github.moranaapps",
scalaVersion := "2.12.21",
version := "0.1.0-SNAPSHOT",

libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.18" % Test
)
)

addCommandAlias("jacoco", "; jacocoOn; clean; test; jacocoReportAll; jacocoOff")
addCommandAlias("jacocoOff", "; set every jacocoPluginEnabled := false")
addCommandAlias("jacocoOn", "; set every jacocoPluginEnabled := true")
9 changes: 9 additions & 0 deletions integration-tests/fixtures/sbt-19x/jmf-rules.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Test rules for the sbt 1.9.x integration test
# Exclude synthetic / bridge methods
*#* synthetic
*#* bridge

# Exclude compiler-generated methods
*#$anonfun$*
*#lambda$*
*#$default$*
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.9.9
3 changes: 3 additions & 0 deletions integration-tests/fixtures/sbt-19x/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// CI fixture: verifies the plugin loads and runs on sbt 1.9.x (the 3-arg
// Command.process overload used up to v2.4.0 only exists in sbt >= 1.10.0).
addSbtPlugin("io.github.moranaapps" % "jacoco-method-filter-sbt" % "2.4.0")
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package example

class Calculator {
def add(a: Int, b: Int): Int = a + b

def subtract(a: Int, b: Int): Int = a - b

def multiply(a: Int, b: Int): Int = a * b

def divide(a: Int, b: Int): Int = {
require(b != 0, "Cannot divide by zero")
a / b
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package example

import org.scalatest.funsuite.AnyFunSuite

class CalculatorTest extends AnyFunSuite {
val calc = new Calculator

test("addition") {
assert(calc.add(2, 3) === 5)
}

test("subtraction") {
assert(calc.subtract(5, 3) === 2)
}

test("multiplication") {
assert(calc.multiply(3, 4) === 12)
}

test("division") {
assert(calc.divide(12, 3) === 4)
}

test("division by zero throws exception") {
intercept[IllegalArgumentException] {
calc.divide(5, 0)
}
}
}
7 changes: 6 additions & 1 deletion integration-tests/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@ pass() { echo -e "${GREEN}PASS${NC}: $1"; }
fail() { echo -e "${RED}FAIL${NC}: $1"; exit 1; }
info() { echo -e "${YELLOW}INFO${NC}: $1"; }

# Path of the log file written by the most recent run_cmd call.
LAST_CMD_LOG=""

# ---------------------------------------------------------------------------
# run_cmd <label> <cmd> [args...]
# Runs a command, captures stdout+stderr, and always prints the output so it
# is visible before the temp directory is cleaned up.
# is visible before the temp directory is cleaned up. The captured log path
# is exposed as $LAST_CMD_LOG for follow-up assertions.
# ---------------------------------------------------------------------------
run_cmd() {
local label="$1"; shift
# Normalize label to safe filename: replace non-alphanumerics with underscores
local safe_label
safe_label="$(printf '%s' "$label" | LC_ALL=C tr -c 'A-Za-z0-9' '_')"
local log="$WORK_DIR/${safe_label}.log"
LAST_CMD_LOG="$log"
info "$label"
local rc=0
"$@" > "$log" 2>&1 || rc=$?
Expand Down
55 changes: 55 additions & 0 deletions integration-tests/test-sbt-19x.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# Test: sbt 1.9.x compatibility (regression guard for issue #76)
#
# v2.4.0 of the plugin used the 3-arg `Command.process(String, State, onParseError)`
# overload, which only exists in sbt >= 1.10.0. On sbt 1.9.x, `jacocoReportAll`
# (and `jacocoCleanAll`) failed with:
# java.lang.NoSuchMethodError: sbt.Command$.process(...)
#
# This test pins a project to sbt 1.9.9 and runs the full jacoco flow; the
# `jacocoReportAll` step is what invokes `Command.process`, so a regression to a
# newer-than-1.9 sbt API makes `run_cmd` fail here.
#
# Prerequisite: sbt plugin published locally.
# ---------------------------------------------------------------------------
source "$(dirname "$0")/helpers.sh"

TEST_NAME="sbt-19x-compat"
info "Running: $TEST_NAME"

# Self-contained fixture (own src + rules), pinned to sbt.version=1.9.9.
cp -R "$REPO_ROOT/integration-tests/fixtures/sbt-19x" "$WORK_DIR/project"
cd "$WORK_DIR/project"

# ── 1. Plain test (no filtering) — also loads the plugin on sbt 1.9.x ──────
run_cmd "$TEST_NAME — sbt clean test (no filtering)" sbt clean test

# The sbt launcher honours project/build.properties; assert it really is 1.9.x
# so this test cannot silently start passing on a newer sbt.
assert_file_contains "$LAST_CMD_LOG" "welcome to sbt 1.9" \
"$TEST_NAME — launcher is sbt 1.9.x"

pass "$TEST_NAME — tests pass without filtering on sbt 1.9.x"

# ── 2. Full jacoco flow (jacocoReportAll invokes Command.process) ─────────
# The critical step: it must NOT fail with NoSuchMethodError on sbt 1.9.x.
run_cmd "$TEST_NAME — sbt jacoco (with filtering on sbt 1.9.x)" sbt jacoco

assert_file_contains "$LAST_CMD_LOG" "welcome to sbt 1.9" \
"$TEST_NAME — jacoco flow ran on sbt 1.9.x"

REPORT_DIR="target/scala-2.12/jacoco-report"
assert_dir_not_empty "$REPORT_DIR" \
"$TEST_NAME — JaCoCo report directory exists and is not empty"

assert_file_exists "$REPORT_DIR/index.html" \
"$TEST_NAME — HTML report generated"

assert_file_exists "$REPORT_DIR/jacoco.xml" \
"$TEST_NAME — XML report generated"

assert_file_exists "$REPORT_DIR/jacoco.csv" \
"$TEST_NAME — CSV report generated"

pass "$TEST_NAME — coverage with filtering on sbt 1.9.x"
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ object JacocoFilterPlugin extends AutoPlugin {
state
} else {
targets.foldLeft(state) { (st, ref) =>
Command.process(s"${ref.project}/jacocoClean", st, msg => sys.error(msg))
// 2-arg overload: works across all sbt 1.x. sbt already fails the build
// on a command/parse error, so the 3-arg onParseError callback (added in
// sbt 1.10.0) is not needed and would break sbt < 1.10 with NoSuchMethodError.
Command.process(s"${ref.project}/jacocoClean", st)
}
}
}
Expand All @@ -71,7 +74,8 @@ object JacocoFilterPlugin extends AutoPlugin {
state
} else {
targets.foldLeft(state) { (st, ref) =>
Command.process(s"${ref.project}/jacocoReport", st, msg => sys.error(msg))
// 2-arg overload: see note in jacocoCleanAllCmd.
Command.process(s"${ref.project}/jacocoReport", st)
}
}
}
Expand Down
Loading