diff --git a/.github/workflows/jacoco_check.yml b/.github/workflows/jacoco_check.yml index 647d047..73fc4c2 100644 --- a/.github/workflows/jacoco_check.yml +++ b/.github/workflows/jacoco_check.yml @@ -19,6 +19,7 @@ name: JaCoCo report on: pull_request: branches: [ master ] + types: [ opened, edited, synchronize, reopened ] jobs: test: @@ -51,11 +52,12 @@ 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@54bfe284d1119dc917dddba80517c54c5bcf3627 with: - paths: ${{ github.workspace }}/target/scala-${{ matrix.scalaShort }}/jacoco/report/jacoco.xml + paths: | + **/jacoco.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: ${{ matrix.overall }} min-coverage-changed-files: ${{ matrix.changed }} diff --git a/build.sbt b/build.sbt index 5187317..f4ea43f 100644 --- a/build.sbt +++ b/build.sbt @@ -61,14 +61,15 @@ 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")