From adb8ce7a89334be744d803f56e5aa568d17bd33c Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Mon, 30 Mar 2026 12:12:43 +0200 Subject: [PATCH 1/6] replacing replacing legacy sbt-jacoco --- build.sbt | 23 ++++--- jmf-rules.txt | 155 ++++++++++++++++++++++++++++++++++++++++++++ project/plugins.sbt | 10 +-- 3 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 jmf-rules.txt diff --git a/build.sbt b/build.sbt index 5187317..402b66c 100644 --- a/build.sbt +++ b/build.sbt @@ -58,17 +58,16 @@ lazy val fmtFilterExpression: String = System.getProperty("os.name").toLowerCase } scalafmtFilter.withRank(KeyRanks.Invisible) := fmtFilterExpression -// linting -Global / excludeLintKeys += ThisBuild / name // will be used in publish, todo #3 - confirm if lint ignore is still needed -// JaCoCo code coverage -Test / jacocoReportSettings := JacocoReportSettings( - title = s"spark-data-standardization Jacoco Report - scala:${scalaVersion.value}", - formats = Seq(JacocoReportFormats.HTML, JacocoReportFormats.XML) -) +// JaCoCo Method Filter Plugin +enablePlugins(JacocoFilterPlugin) -// exclude example -Test / jacocoExcludes := Seq( - // "za.co.absa.standardization.udf.UDFBuilder*", // class and related objects - // "za.co.absa.standardization.udf.UDFNames" // class only -) +jacocoReportName := s"spark-data-standardization Jacoco Report - scala:${scalaVersion.value}" +jacocoReportFormats := Set("html", "xml") + +// jacocoExcludes := Seq("za/co/absa/standardization/udf/UDFBuilder*", "za/co/absa/standardization/udf/UDFNames") + +// Command aliases for JaCoCo coverage workflow +addCommandAlias("jacoco", "; jacocoOn; clean; test; jacocoReportAll; jacocoOff") +addCommandAlias("jacocoOn", "; set every jacocoPluginEnabled := true") +addCommandAlias("jacocoOff", "; set every jacocoPluginEnabled := false") diff --git a/jmf-rules.txt b/jmf-rules.txt new file mode 100644 index 0000000..7c2ade2 --- /dev/null +++ b/jmf-rules.txt @@ -0,0 +1,155 @@ +# jacoco-method-filter — Default Rules & HowTo (Scala) +# [jmf:1.0.0] +# +# This file defines which methods should be annotated as *Generated so JaCoCo ignores them. +# One rule per line. +# +# ───────────────────────────────────────────────────────────────────────────── +# HOW TO USE (quick) +# 1) Replace YOUR.PACKAGE.ROOT with your project’s package root (e.g., com.example.app). +# 2) Start with the CONSERVATIVE section only. +# 3) If clean, enable STANDARD. Use AGGRESSIVE only inside DTO/auto‑generated packages. +# 4) Keep rules narrow (by package), prefer flags (synthetic/bridge) for compiler artifacts, +# and add `id:` labels so logs are easy to read. +# +# ───────────────────────────────────────────────────────────────────────────── +# ALLOWED SYNTAX (cheat sheet) +# +# General form: +# #() [FLAGS and PREDICATES...] +# +# FQCN_glob (dot form; $ allowed for inner classes): +# Examples: *.model.*, com.example.*, * +# +# method_glob (glob on method name): +# Examples: copy | $anonfun$* | get* | *_$eq +# +# descriptor_glob (JVM descriptor in (args)ret). You may omit it entirely. +# • Omitting descriptor ⇒ treated as "(*)*" (any args, any return). +# • Short/empty forms "", "()", "(*)" normalize to "(*)*". +# Examples: +# (I)I # takes int, returns int +# (Ljava/lang/String;)V # takes String, returns void +# () or (*) or omitted # any args, any return +# +# FLAGS (optional) — space or comma separated: +# public | protected | private | synthetic | bridge | static | abstract +# +# PREDICATES (optional): +# ret: # match return type only (e.g., ret:V, ret:I, ret:Lcom/example/*;) +# id: # identifier shown in logs/reports +# name-contains: # method name must contain +# name-starts: # method name must start with +# name-ends: # method name must end with +# +# Notes +# - Always use dot-form (com.example.Foo) for class names. +# - Comments (# …) and blank lines are ignored. +# +# ───────────────────────────────────────────────────────────────────────────── +# QUICK EXAMPLES +# +# Simple wildcards +# *#*(*) +# → Match EVERY method in EVERY class (any package). Useful only for diagnostics. +# "(*)" normalizes to "(*)*" ⇒ any args, any return. +# *.dto.*#*(*) +# → Match every method on any class under any package segment named "dto". +# Good when you treat DTOs as generated/boilerplate. + +# Scala case class helpers +# *.model.*#copy(*) +# → Matches Scala case-class `copy` methods under `*.model.*`. +# Hides boilerplate clones with any parameter list and any return. +# *.model.*#productArity() +# → Matches zero-arg `productArity` (case-class/Product API). +# *.model.*#productElement(*) +# → Matches `productElement(int)` (or any descriptor form) on case classes. +# *.model.*#productPrefix() +# → Matches `productPrefix()`; returns the case class' constructor name. + +# Companion objects and defaults +# *.model.*$*#apply(*) +# → Matches companion `apply` factories under `*.model.*` (any args). +# BE CAREFUL: can hide real factory logic; keep the package scope narrow. +# *.model.*$*#unapply(*) +# → Matches extractor `unapply` methods in companions under `*.model.*`. +# *#*$default$*(*) +# → Matches Scala-generated default-argument helpers everywhere. +# Safe to keep enabled; they’re compiler-synthesized. + +# Anonymous / synthetic / bridge +# *#$anonfun$* +# → Matches any method whose name contains `$anonfun$` (Scala lambdas). +# Consider adding `synthetic` and/or a package scope in real configs. +# *#*(*):synthetic # any synthetic +# → Matches ANY method marked `synthetic` (compiler-generated). +# Powerful; scope by package to avoid hiding intentional glue code. +# *#*(*):bridge # any bridge +# → Matches Java generic bridge methods the compiler inserts. +# Usually safe globally, but scoping is still recommended. + +# Setters / fluent APIs +# *.dto.*#*_$eq(*) +# → Matches Scala var setters in DTO packages (e.g., `name_=(...)`). +# Good for excluding trivial field writes. +# *.builder.*#with*(*) +# → Matches builder-style fluent setters (`withXxx(...)`) in builder pkgs. +# Treats chainable configuration as boilerplate. +# *.client.*#with*(*) ret:Lcom/api/client/* +# → Like above but ONLY when the return type matches your client package. +# The `ret:` predicate protects real logic that returns other types. + +# Return-type constraints +# *.jobs.*#*(*):ret:V +# → Any method under `*.jobs.*` returning `void` (`V`). Often orchestration. +# *.math.*#*(*):ret:I +# → Any method under `*.math.*` returning primitive int (`I`). +# *.model.*#*(*):ret:Lcom/example/model/* +# → Any method under `*.model.*` that returns a type in `com.example.model`. +# Handy when the *return type* uniquely identifies boilerplate. + +# ───────────────────────────────────────────────────────────────────────────── +# GLOBALS RULES +# ───────────────────────────────────────────────────────────────────────────── +# ** all case class boilerplate + +# Scala case class helpers +*#canEqual(*) id:case-canequal +*#equals(*) id:case-equals +*#apply(*) id:case-apply +*#unapply(*) id:case-unapply +*#hashCode(*) id:case-hashcode +*#copy(*) id:case-copy +*#copy$default$*(*) id:case-copy-defaults +*#productElement() id:case-prod-element +*#productArity() id:case-prod-arity +*#productPrefix() id:case-prod-prefix +*#productIterator() id:case-prod-iterator +*#tupled() id:case-tupled +*#curried() id:case-curried +*#toString() id:case-tostring +*#name() id:case-name +*#groups() id:case-groups +*#optionalAttributes() id:case-optionalAttributes + +# Companion objects, constructors, and static definitions +*$#(*) id:gen-ctor # constructors +*$#() id:gen-clinit # static initializer blocks + +# Companion objects and defaults +*$*#apply(*) id:comp-apply +*$*#unapply(*) id:comp-unapply +*$*#toString(*) id:comp-tostring +*$*#readResolve(*) id:comp-readresolve + +# anonymous class created by a macro expansion +*$macro$*#$anonfun$inst$macro$* id:macro-inst +*$macro$*#inst$macro$* id:macro-inst + +# lambda +*#* synthetic name-contains:$anonfun$ id:scala-anonfun + +# ───────────────────────────────────────────────────────────────────────────── +# PROJECT RULES +# ───────────────────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 81cd8d5..3f52147 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,21 +20,15 @@ addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.5") -// sbt-jacoco - workaround related dependencies required to download +// JaCoCo Method Filter sbt Plugin (replaces legacy sbt-jacoco) +addSbtPlugin("io.github.moranaapps" % "jacoco-method-filter-sbt" % "2.0.1") lazy val ow2Version = "9.5" -lazy val jacocoVersion = "0.8.10-absa.1" -def jacocoUrl(artifactName: String): String = s"https://github.com/AbsaOSS/jacoco/releases/download/$jacocoVersion/org.jacoco.$artifactName-$jacocoVersion.jar" def ow2Url(artifactName: String): String = s"https://repo1.maven.org/maven2/org/ow2/asm/$artifactName/$ow2Version/$artifactName-$ow2Version.jar" addSbtPlugin("com.jsuereth" %% "scala-arm" % "2.0" from "https://repo1.maven.org/maven2/com/jsuereth/scala-arm_2.11/2.0/scala-arm_2.11-2.0.jar") addSbtPlugin("com.jsuereth" %% "scala-arm" % "2.0" from "https://repo1.maven.org/maven2/com/jsuereth/scala-arm_2.12/2.0/scala-arm_2.12-2.0.jar") - -addSbtPlugin("za.co.absa.jacoco" % "report" % jacocoVersion from jacocoUrl("report")) -addSbtPlugin("za.co.absa.jacoco" % "core" % jacocoVersion from jacocoUrl("core")) -addSbtPlugin("za.co.absa.jacoco" % "agent" % jacocoVersion from jacocoUrl("agent")) addSbtPlugin("org.ow2.asm" % "asm" % ow2Version from ow2Url("asm")) addSbtPlugin("org.ow2.asm" % "asm-commons" % ow2Version from ow2Url("asm-commons")) addSbtPlugin("org.ow2.asm" % "asm-tree" % ow2Version from ow2Url("asm-tree")) -addSbtPlugin("za.co.absa.sbt" % "sbt-jacoco" % "3.4.1-absa.3" from "https://github.com/AbsaOSS/sbt-jacoco/releases/download/3.4.1-absa.3/sbt-jacoco-3.4.1-absa.3.jar") From 4a0c9bfd7a92d3230910acde0dd080fc32790133 Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Mon, 30 Mar 2026 12:38:23 +0200 Subject: [PATCH 2/6] reverted linting removal --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index 402b66c..f4ea43f 100644 --- a/build.sbt +++ b/build.sbt @@ -58,6 +58,8 @@ lazy val fmtFilterExpression: String = System.getProperty("os.name").toLowerCase } scalafmtFilter.withRank(KeyRanks.Invisible) := fmtFilterExpression +// linting +Global / excludeLintKeys += ThisBuild / name // will be used in publish, todo #3 - confirm if lint ignore is still needed // JaCoCo Method Filter Plugin enablePlugins(JacocoFilterPlugin) From cb149e743debed5d7498eba3f14e65718cf3d3b2 Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Wed, 1 Apr 2026 12:13:06 +0200 Subject: [PATCH 3/6] Jacoco workflow update --- .github/workflows/jacoco_check.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jacoco_check.yml b/.github/workflows/jacoco_check.yml index 647d047..be36eb7 100644 --- a/.github/workflows/jacoco_check.yml +++ b/.github/workflows/jacoco_check.yml @@ -19,7 +19,8 @@ name: JaCoCo report on: pull_request: branches: [ master ] - + types: [ opened, edited, synchronize, reopened ] + jobs: test: runs-on: ubuntu-latest @@ -33,7 +34,7 @@ jobs: # vendor distributions. - scala: 2.12.12 scalaShort: "2.12" - overall: 80.0 + overall: 0.0 changed: 80.0 steps: - name: Checkout code @@ -51,9 +52,9 @@ jobs: - name: Build and run tests run: sbt ++${{matrix.scala}} jacoco - - name: Add coverage to PR + - name: Add JaCoCo Report in PR comments id: jacoco - uses: madrapps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 + uses: MoranaApps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 with: paths: ${{ github.workspace }}/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} From d36c24772294666dda727220b7ee3125606ee173 Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Wed, 1 Apr 2026 18:11:46 +0200 Subject: [PATCH 4/6] testing different SHA --- .github/workflows/jacoco_check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jacoco_check.yml b/.github/workflows/jacoco_check.yml index be36eb7..95e5e73 100644 --- a/.github/workflows/jacoco_check.yml +++ b/.github/workflows/jacoco_check.yml @@ -34,7 +34,7 @@ jobs: # vendor distributions. - scala: 2.12.12 scalaShort: "2.12" - overall: 0.0 + overall: 80.0 changed: 80.0 steps: - name: Checkout code @@ -54,7 +54,7 @@ jobs: - name: Add JaCoCo Report in PR comments id: jacoco - uses: MoranaApps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 + uses: MoranaApps/jacoco-report@54bfe284d1119dc917dddba80517c54c5bcf3627 with: paths: ${{ github.workspace }}/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} From 9b8cabd5663d83dfb16f36fad5fd1bdf4bec48f0 Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Wed, 1 Apr 2026 19:11:40 +0200 Subject: [PATCH 5/6] Dynamic path for jacoco.xml not found --- .github/workflows/jacoco_check.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jacoco_check.yml b/.github/workflows/jacoco_check.yml index 95e5e73..2ac66fb 100644 --- a/.github/workflows/jacoco_check.yml +++ b/.github/workflows/jacoco_check.yml @@ -20,7 +20,7 @@ on: pull_request: branches: [ master ] types: [ opened, edited, synchronize, reopened ] - + jobs: test: runs-on: ubuntu-latest @@ -56,7 +56,8 @@ jobs: id: jacoco uses: MoranaApps/jacoco-report@54bfe284d1119dc917dddba80517c54c5bcf3627 with: - paths: ${{ github.workspace }}/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + paths: | + **/target/jacoco/report/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: ${{ matrix.overall }} min-coverage-changed-files: ${{ matrix.changed }} From 7c2bb9d23228a80f0d6462f65fffe183875952e3 Mon Sep 17 00:00:00 2001 From: MatloaItumeleng Date: Thu, 2 Apr 2026 09:06:42 +0200 Subject: [PATCH 6/6] jacoco workaround test --- .github/workflows/jacoco_check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jacoco_check.yml b/.github/workflows/jacoco_check.yml index 2ac66fb..73fc4c2 100644 --- a/.github/workflows/jacoco_check.yml +++ b/.github/workflows/jacoco_check.yml @@ -57,7 +57,7 @@ jobs: uses: MoranaApps/jacoco-report@54bfe284d1119dc917dddba80517c54c5bcf3627 with: paths: | - **/target/jacoco/report/jacoco.xml + **/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: ${{ matrix.overall }} min-coverage-changed-files: ${{ matrix.changed }}