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
28 changes: 26 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ StatusBoard is a real-time monitoring platform for tracking service health statu
- **Database**: AWS DynamoDB for persistence
- **Testing**: ZIO Test framework
- **Code Coverage**: Jacoco
- Compiler warnings treated as errors where configured; coverage ≥ 80% via JMF-enabled JaCoCo (excluding methods listed in `jmf-rules.txt`).
Comment thread
tmikula-dev marked this conversation as resolved.

### Frontend (TypeScript)
- **Framework**: Angular 19
Expand Down Expand Up @@ -105,7 +106,7 @@ za. co.absa.statusboard/
- Access services via ZIO environment: `ZIO.serviceWithZIO[Service](_. method())`

### API Endpoints (Tapir)
Define endpoints in `Endpoints. scala` with full type safety
Define endpoints in `Endpoints.scala` with full type safety
- Use `@accessible` macro for controller traits
- Bind public endpoints: `bindEndpoint(endpoint, handler)`
- Bind authenticated endpoints: `bindEndpoint(authController)(endpoint, handler)`
Expand Down Expand Up @@ -166,6 +167,29 @@ The checker system uses a polymorphic design:
- E2E tests: `npm run e2e` (Cypress via `cypress.json`)
- Test coverage: `npm run test:coverage`

## Coverage Filtering (JMF)

### When a unit test adds value — write one
- The method has any logic of its own.

### When to add to `jmf-rules.txt` instead of writing a unit test
- The body is a single call with no own logic: it forwards to another overload, calls its non-deprecated replacement, returns a field, or wraps a constructor with no transformation.
- **Litmus test**: "Does this method have any logic of its own?" — No → add a JMF rule instead of a test.

### Global rule collision check (CRITICAL)
- When adding any new method, check whether its name matches a pattern in the `# GLOBAL RULES` section of `jmf-rules.txt`.
- If a method name matches a global rule AND the method contains domain logic: immediately add an INCLUDE rescue rule (`+FQCN#method(*)`) in the `# INCLUDE RULES` section of `jmf-rules.txt`.
- High-risk method names (most common collisions): `apply()`, `toString()`, `equals()`, `copy()`, `name()`, `groups()`, `optionalAttributes()`. See the `# GLOBAL RULES` section of `jmf-rules.txt` for the full list.
- Rationale: broad global rules are designed for compiler-generated boilerplate and can silently suppress coverage for domain methods. INCLUDE rules rescue specific methods from broad exclusions.
- Example: if adding `def apply(id: String): Record`, add `+*Record$#apply(*) id:keep-record-factory` to the `# INCLUDE RULES` section to rescue it from the `*$*#apply(*)` global rule.

### JMF drift check (review rule)
- When modifying a method that already appears in `jmf-rules.txt`, verify its body still qualifies for exclusion.
- If own logic has been added since the rule was created, remove the JMF rule and write a unit test instead.

### Cannot add JMF rules for
- Methods with branching logic, error handling, or non-trivial transformations — write a unit test instead.

## Configuration
- Main config: `config. conf` (HOCON format)
- Environment-specific configurations for different deployments
Expand Down Expand Up @@ -279,4 +303,4 @@ npm run sync-version # Sync VERSION file to package.json
## Additional Resources
- [Wiki - REST API](https://github.com/AbsaOSS/StatusBoard/wiki/REST-API)
- [Wiki - Architecture](https://github.com/AbsaOSS/StatusBoard/wiki/Architecture)
- [Wiki - Supported Checkers](https://github.com/AbsaOSS/StatusBoard/wiki/Supported-checkers)
- [Wiki - Supported Checkers](https://github.com/AbsaOSS/StatusBoard/wiki/Supported-checkers)
57 changes: 44 additions & 13 deletions .github/workflows/ci-check-jacoco.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
name: JaCoCo Report
name: CI Check JaCoCo

on:
pull_request:
branches: [ master ]
types: [ opened, edited, synchronize, reopened ]

env:
scalaLong: 2.13.11
scalaLong: 2.13.13
scalaShort: "2.13"
coverage-overall: 80.0
coverage-changed-files: 80.0
coverage-per-changed-file: 0.0
Comment thread
tmikula-dev marked this conversation as resolved.
check-overall-coverages: true

jobs:
jacoco-report:
name: JaCoCo Report
detect:
name: Detect Changed Files
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
scala_changed: ${{ steps.filter.outputs.scala }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false
fetch-depth: 0
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d
id: filter
with:
token: ""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an optimization, where it does a fallback to do a local comparison and do not run a gh api, when it is not needed.

filters: |
scala:
- '**/*.scala'
- '!project/**'

build-test-and-measure:
name: Build, Test and Measure
needs: detect
if: needs.detect.outputs.scala_changed == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
services:
dynamodb-local:
image: "amazon/dynamodb-local:latest"
Expand Down Expand Up @@ -51,16 +76,22 @@ jobs:
with:
python-version: '3.14'

- name: Publish JaCoCo Report in PR comments
- name: Check coverage thresholds and add reports in PR comments
id: jacoco
uses: MoranaApps/jacoco-report@69351d88d18f7697c416e1bc2020ed05606d8120
with:
token: '${{ secrets.GITHUB_TOKEN }}'
paths: |
**/target/**/jacoco/report/jacoco.xml
sensitivity: "detail"
comment-mode: 'single'
min-coverage-overall: ${{ env.coverage-overall }}
min-coverage-changed-files: ${{ env.coverage-changed-files }}
min-coverage-per-changed-file: ${{ env.coverage-per-changed-file }}
skip-unchanged: false
**/target/**/jacoco-report/jacoco.xml
global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}'
report-thresholds-default: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}'
comment-level: 'full'
skip-unchanged: true

noop:
name: No Operation
needs: detect
if: needs.detect.outputs.scala_changed != 'true'
runs-on: ubuntu-latest
steps:
- run: echo "No changes in src/**/*.scala — passing."
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ lazy val root = (project in file("."))
Test / parallelExecution := false,
(assembly / test) := {},
publish := {},
jmfReportFile := Some(target.value / "jmf-report.json"),
jmfReportFormat := "json",
)
.enablePlugins(AutomateHeaderPlugin)
.enablePlugins(AssemblyPlugin)
.enablePlugins(FilteredJacocoAgentPlugin)
.enablePlugins(JacocoFilterPlugin)
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import sbt.*

object Dependencies {
object Versions {
val scala213 = "2.13.12"
val scala213 = "2.13.13"

val zio = "2.0.21"
val zioLogging = "2.2.0"
Expand Down
Loading