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
32 changes: 13 additions & 19 deletions .github/workflows/jacoco_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,23 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
persist-credentials: false
fetch-depth: 0
- name: Setup Scala
uses: olafurpg/setup-scala@v10
with:
java-version: "[email protected]"
- name: Build and run tests
run: sbt ++${{matrix.scala}} jacoco
- name: Add coverage to PR
id: jacoco
uses: madrapps/[email protected]
with:
paths: ${{ github.workspace }}/api/target/scala-${{ matrix.scala_short }}/jacoco/report/jacoco.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: ${{ matrix.overall }}
min-coverage-changed-files: ${{ matrix.changed }}
title: JaCoCo code coverage report - scala:${{ matrix.scala }}
update-comment: true
- name: Get the Coverage info
run: |
echo "Total coverage ${{ steps.jacoco.outputs.coverage-overall }}"
echo "Changed Files coverage ${{ steps.jacoco.outputs.coverage-changed-files }}"
- name: Fail PR if changed files coverage is less than ${{ matrix.changed }}%
if: ${{ steps.jacoco.outputs.coverage-changed-files < 80.0 }}
uses: actions/github-script@v6
- name: Add JaCoCo Report in PR comments
uses: MoranaApps/jacoco-report@v2
with:
script: |
core.setFailed('Changed files coverage is less than ${{ matrix.changed }}%!')
token: '${{ secrets.GITHUB_TOKEN }}'
paths: |
**/target/jacoco/report/jacoco.xml
min-coverage-overall: 43.0
min-coverage-changed-files: 70.0
sensitivity: "detail"
comment-mode: 'multi'
skip-unchanged: 'true'
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ sbt jacoco
```
Code coverage will be generated on path:
```
{project-root}/{module}/target/scala-{scala_version}/jacoco/report/html
{project-root}/{module}/target/jacoco/report/index.html
```
## Health check endpoint
Springboot Actuator is enabled for this project. This provides the user with an endpoint (readable via HTTP or JMX)
Expand Down Expand Up @@ -459,4 +459,4 @@ This requires Git to be installed and available on the host.
The example `git.properties` file provided may be edited manually if the git generation is functioning incorrectly.

## Client Library
See Readme in [clientLibrary](clientLibrary/README.md) module.
See Readme in [clientLibrary](clientLibrary/README.md) module.
25 changes: 15 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

import Dependencies._
import com.github.sbt.jacoco.report.JacocoReportSettings

ThisBuild / organization := "za.co.absa"

Expand All @@ -24,23 +23,24 @@ lazy val scala213 = "2.13.12"
ThisBuild / scalaVersion := scala212
ThisBuild / versionScheme := Some("early-semver")

lazy val commonJacocoReportSettings: JacocoReportSettings = JacocoReportSettings(
formats = Seq(JacocoReportFormats.HTML, JacocoReportFormats.XML)
)

lazy val commonJacocoExcludes: Seq[String] = Seq(
"za.co.absa.loginsvc.rest.Application*"
// "za.co.absa.loginsvc.rest.config.BaseConfig" // class only
// "za.co.absa.loginsvc.rest.config.BaseConfig" // class only
)

lazy val commonJavacOptions = Seq("-source", "1.8", "-target", "1.8", "-Xlint") // deliberately making backwards compatible with J8

lazy val parent = (project in file("."))
.aggregate(api, clientLibrary, examples)
.enablePlugins(FilteredJacocoAgentPlugin)
.settings(
name := "login-service",
javacOptions ++= commonJavacOptions,
publish / skip := true
publish / skip := true,
// Global defaults (override per-module below if you wish)
FilteredJacocoAgentPlugin.autoImport.jacocoIncludes := Seq("za.co.absa.*"),
FilteredJacocoAgentPlugin.autoImport.jacocoExcludes ++= commonJacocoExcludes,
FilteredJacocoAgentPlugin.autoImport.jacocoFailOnMissingExec := false
)

lazy val api = project // no need to define file, because path is same as val name
Expand All @@ -50,11 +50,10 @@ lazy val api = project // no need to define file, because path is same as val na
webappWebInfClasses := true,
inheritJarManifest := true,
javacOptions ++= commonJavacOptions,
jacocoReportSettings := commonJacocoReportSettings.withTitle(s"login-service:service Jacoco Report - scala:${scalaVersion.value}"),
jacocoExcludes := commonJacocoExcludes,
publish / skip := true
).enablePlugins(TomcatPlugin)
.enablePlugins(AutomateHeaderPlugin)
.enablePlugins(FilteredJacocoAgentPlugin)

lazy val clientLibrary = project // no need to define file, because path is same as val name
.settings(
Expand All @@ -63,6 +62,7 @@ lazy val clientLibrary = project // no need to define file, because path is same
javacOptions ++= commonJavacOptions,
crossScalaVersions := Seq(scala212, scala213)
).enablePlugins(AutomateHeaderPlugin)
.enablePlugins(FilteredJacocoAgentPlugin)

lazy val examples = project // no need to define file, because path is same as val name
.settings(
Expand All @@ -71,4 +71,9 @@ lazy val examples = project // no need to define file, because path is same as v
javacOptions ++= commonJavacOptions,
publish / skip := true
).enablePlugins(AutomateHeaderPlugin)
.dependsOn(clientLibrary)
.dependsOn(clientLibrary)

// Run activate jacoco + clean + test + per-module reports across the whole build + deactivate jacoco
addCommandAlias("jacoco", "; jacocoOn; clean; test; jacocoReportAll; jacocoOff")
addCommandAlias("jacocoOff", "; set every jacocoPluginEnabled := false")
addCommandAlias("jacocoOn", "; set every jacocoPluginEnabled := true")
154 changes: 154 additions & 0 deletions jmf-rules.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# jacoco-method-filter — Default Rules & HowTo (Scala + Java)
#
# 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:
# <FQCN_glob>#<method_glob>(<descriptor_glob>) [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:<glob> # match return type only (e.g., ret:V, ret:I, ret:Lcom/example/*;)
# id:<string> # identifier shown in logs/reports
# name-contains:<s> # method name must contain <s>
# name-starts:<s> # method name must start with <s>
# name-ends:<s> # method name must end with <s>
#
# 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
*$#<init>(*) id:gen-ctor # constructors
*$#<clinit>() 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

# ─────────────────────────────────────────────────────────────────────────────
# DIRECT RULES
# ─────────────────────────────────────────────────────────────────────────────
Loading