From ebac1dda8a72abfba5438b4a14fe87715dd7554e Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Sat, 16 May 2026 14:46:51 +0200 Subject: [PATCH 01/10] Created demo modules for testing purposes. Jacoco report switched to master. Disabled other wf as not needed. --- .github/workflows/build.yml | 9 +- .github/workflows/check_pr_release_notes.yml | 7 +- .github/workflows/jacoco_report.yml | 2 +- .github/workflows/test_filenames_check.yml | 7 +- build.sbt | 36 +++++++ .../absa/db/balta/calculator/Calculator.scala | 74 ++++++++++++++ .../calculator/CalculatorUnitTests.scala | 82 ++++++++++++++++ .../absa/db/balta/formatter/Formatter.scala | 66 +++++++++++++ .../balta/formatter/FormatterUnitTests.scala | 80 ++++++++++++++++ .../absa/db/balta/validator/Validator.scala | 69 +++++++++++++ .../balta/validator/ValidatorUnitTests.scala | 96 +++++++++++++++++++ 11 files changed, 517 insertions(+), 11 deletions(-) create mode 100644 calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala create mode 100644 calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala create mode 100644 formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala create mode 100644 formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala create mode 100644 validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala create mode 100644 validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f25b4d2..ca3c49c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,11 @@ name: Build on: - pull_request: - branches: - - '**' - types: [ opened, synchronize, reopened ] + # pull_request: + # branches: + # - '**' + # types: [ opened, synchronize, reopened ] + workflow_dispatch: jobs: test: diff --git a/.github/workflows/check_pr_release_notes.yml b/.github/workflows/check_pr_release_notes.yml index 7e3a35d..08e45a0 100644 --- a/.github/workflows/check_pr_release_notes.yml +++ b/.github/workflows/check_pr_release_notes.yml @@ -17,9 +17,10 @@ name: Check PR Release Notes on: - pull_request: - types: [opened, synchronize, reopened, edited, labeled, unlabeled] - branches: [ master ] + # pull_request: + # types: [opened, synchronize, reopened, edited, labeled, unlabeled] + # branches: [ master ] + workflow_dispatch: env: SKIP_LABEL: 'no RN' diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index 21167d1..dcb7da7 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -95,7 +95,7 @@ jobs: - name: Check coverage thresholds and add reports in PR comments id: jacoco - uses: MoranaApps/jacoco-report@54bfe284d1119dc917dddba80517c54c5bcf3627 + uses: MoranaApps/jacoco-report@master with: token: '${{ secrets.GITHUB_TOKEN }}' paths: | diff --git a/.github/workflows/test_filenames_check.yml b/.github/workflows/test_filenames_check.yml index 44d296c..f17ea0c 100644 --- a/.github/workflows/test_filenames_check.yml +++ b/.github/workflows/test_filenames_check.yml @@ -17,9 +17,10 @@ name: Test Filenames Check on: - pull_request: - branches: [ master ] - types: [ opened, synchronize, reopened ] + # pull_request: + # branches: [ master ] + # types: [ opened, synchronize, reopened ] + workflow_dispatch: jobs: test_filenames_check: diff --git a/build.sbt b/build.sbt index 9b9abac..e7fbab3 100644 --- a/build.sbt +++ b/build.sbt @@ -41,3 +41,39 @@ lazy val balta = (project in file("balta")) jmfReportFile := Some(target.value / "jmf-report.json"), jmfReportFormat := "json", ) + +lazy val calculator = (project in file("calculator")) + .enablePlugins(JacocoFilterPlugin) + .settings( + name := "balta-calculator", + scalacOptions ++= Seq( + "-deprecation", + "-feature", + ), + crossScalaVersions := supportedScalaVersions, + libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, + ) + +lazy val validator = (project in file("validator")) + .enablePlugins(JacocoFilterPlugin) + .settings( + name := "balta-validator", + scalacOptions ++= Seq( + "-deprecation", + "-feature", + ), + crossScalaVersions := supportedScalaVersions, + libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, + ) + +lazy val formatter = (project in file("formatter")) + .enablePlugins(JacocoFilterPlugin) + .settings( + name := "balta-formatter", + scalacOptions ++= Seq( + "-deprecation", + "-feature", + ), + crossScalaVersions := supportedScalaVersions, + libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, + ) diff --git a/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala b/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala new file mode 100644 index 0000000..202547b --- /dev/null +++ b/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala @@ -0,0 +1,74 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.calculator + +/** + * Simple arithmetic calculator used to produce measurable JaCoCo coverage data. + */ +class Calculator { + + /** + * Returns the sum of two integers. + * @param a first operand + * @param b second operand + * @return a + b + */ + def add(a: Int, b: Int): Int = a + b + + /** + * Returns the difference of two integers. + * @param a first operand + * @param b second operand + * @return a - b + */ + def subtract(a: Int, b: Int): Int = a - b + + /** + * Returns the product of two integers. + * @param a first operand + * @param b second operand + * @return a * b + */ + def multiply(a: Int, b: Int): Int = a * b + + /** + * Returns the integer quotient of a divided by b. + * @param a dividend + * @param b divisor; must not be zero + * @return a / b + * @throws ArithmeticException if b is zero + */ + def divide(a: Int, b: Int): Int = { + if (b == 0) throw new ArithmeticException("Division by zero") + a / b + } + + /** + * Returns the absolute value of an integer. + * @param a input value + * @return non-negative value of a + */ + def abs(a: Int): Int = if (a < 0) -a else a + + /** + * Returns the larger of two integers. + * @param a first value + * @param b second value + * @return the maximum of a and b + */ + def max(a: Int, b: Int): Int = if (a >= b) a else b +} diff --git a/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala b/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala new file mode 100644 index 0000000..24022d6 --- /dev/null +++ b/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala @@ -0,0 +1,82 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.calculator + +import org.scalatest.funsuite.AnyFunSuite + +class CalculatorUnitTests extends AnyFunSuite { + + private val calc = new Calculator + + test("add returns the sum of two positive integers") { + assert(calc.add(3, 4) == 7) + } + + test("add returns the sum when one operand is negative") { + assert(calc.add(-2, 5) == 3) + } + +// test("subtract returns the difference of two integers") { +// assert(calc.subtract(10, 4) == 6) +// } + +// test("subtract returns negative result when b is greater than a") { +// assert(calc.subtract(2, 9) == -7) +// } + +// test("multiply returns the product of two integers") { +// assert(calc.multiply(3, 7) == 21) +// } + +// test("multiply returns zero when one operand is zero") { +// assert(calc.multiply(0, 99) == 0) +// } + + test("divide returns the integer quotient") { + assert(calc.divide(10, 2) == 5) + } + + test("divide throws ArithmeticException when divisor is zero") { + intercept[ArithmeticException] { + calc.divide(5, 0) + } + } + +// test("abs returns the same value for a positive integer") { +// assert(calc.abs(7) == 7) +// } + +// test("abs returns the negated value for a negative integer") { +// assert(calc.abs(-7) == 7) +// } + +// test("abs returns zero for zero input") { +// assert(calc.abs(0) == 0) +// } + + test("max returns the first value when it is greater") { + assert(calc.max(9, 3) == 9) + } + + test("max returns the second value when it is greater") { + assert(calc.max(3, 9) == 9) + } + + test("max returns the common value when both are equal") { + assert(calc.max(5, 5) == 5) + } +} diff --git a/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala b/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala new file mode 100644 index 0000000..d648913 --- /dev/null +++ b/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.formatter + +/** + * String formatter used to produce measurable JaCoCo coverage data. + */ +class Formatter { + + /** + * Capitalizes the first character of a string, leaving the rest unchanged. + * Returns the input unchanged when it is null or empty. + * @param s input string + * @return string with the first character uppercased + */ + def capitalize(s: String): String = { + if (s == null || s.isEmpty) s + else s.head.toUpper + s.tail + } + + /** + * Truncates a string to at most maxLen characters, appending "..." when truncation occurs. + * @param s input string + * @param maxLen maximum allowed length before truncation + * @return the original string or its prefix followed by "..." + */ + def truncate(s: String, maxLen: Int): String = { + if (s.length <= maxLen) s + else s.take(maxLen) + "..." + } + + /** + * Wraps a string with a prefix and suffix. + * @param s input string + * @param prefix string prepended to s + * @param suffix string appended to s + * @return prefix + s + suffix + */ + def wrap(s: String, prefix: String, suffix: String): String = s"$prefix$s$suffix" + + /** + * Repeats a string a given number of times. + * Returns an empty string when times is zero or negative. + * @param s string to repeat + * @param times number of repetitions + * @return concatenation of s repeated times + */ + def repeat(s: String, times: Int): String = { + if (times <= 0) "" + else s * times + } +} diff --git a/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala b/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala new file mode 100644 index 0000000..6d94dfa --- /dev/null +++ b/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala @@ -0,0 +1,80 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.formatter + +import org.scalatest.funsuite.AnyFunSuite + +class FormatterUnitTests extends AnyFunSuite { + + private val fmt = new Formatter + + test("capitalize uppercases the first character of a lowercase word") { + assert(fmt.capitalize("hello") == "Hello") + } + + test("capitalize leaves an already-capitalized string unchanged") { + assert(fmt.capitalize("Hello") == "Hello") + } + + test("capitalize returns an empty string unchanged") { + assert(fmt.capitalize("") == "") + } + + test("capitalize returns null unchanged") { + assert(fmt.capitalize(null) == null) + } + + test("capitalize handles a single-character string") { + assert(fmt.capitalize("a") == "A") + } + +// test("truncate returns the string unchanged when shorter than maxLen") { +// assert(fmt.truncate("hi", 10) == "hi") +// } + +// test("truncate returns the string unchanged when equal to maxLen") { +// assert(fmt.truncate("hello", 5) == "hello") +// } + +// test("truncate appends ellipsis when the string exceeds maxLen") { +// assert(fmt.truncate("hello world", 5) == "hello...") +// } + + test("wrap surrounds a string with the given prefix and suffix") { + assert(fmt.wrap("world", "Hello, ", "!") == "Hello, world!") + } + + test("wrap with empty prefix and suffix returns the original string") { + assert(fmt.wrap("world", "", "") == "world") + } + +// test("repeat returns the string concatenated the given number of times") { +// assert(fmt.repeat("ab", 3) == "ababab") +// } + +// test("repeat returns an empty string when times is zero") { +// assert(fmt.repeat("ab", 0) == "") +// } + +// test("repeat returns an empty string when times is negative") { +// assert(fmt.repeat("ab", -1) == "") +// } + +// test("repeat with times equal to one returns the original string") { +// assert(fmt.repeat("ab", 1) == "ab") +// } +} diff --git a/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala b/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala new file mode 100644 index 0000000..8e5b228 --- /dev/null +++ b/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala @@ -0,0 +1,69 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.validator + +/** + * Value and string validator used to produce measurable JaCoCo coverage data. + */ +class Validator { + + /** + * Returns true when the integer is strictly positive. + * @param value the integer to check + * @return true if value > 0 + */ + def isPositive(value: Int): Boolean = value > 0 + + /** + * Returns true when the string is non-null and non-empty. + * @param value the string to check + * @return true if value is not null and not empty + */ + def isNonEmpty(value: String): Boolean = value != null && value.nonEmpty + + /** + * Returns true when value falls within the inclusive range [min, max]. + * @param value the integer to check + * @param min lower bound (inclusive) + * @param max upper bound (inclusive) + * @return true if min <= value <= max + */ + def isInRange(value: Int, min: Int, max: Int): Boolean = value >= min && value <= max + + /** + * Classifies an integer as "negative", "zero", or "positive". + * @param value the integer to classify + * @return one of "negative", "zero", or "positive" + */ + def classify(value: Int): String = value match { + case v if v < 0 => "negative" + case 0 => "zero" + case _ => "positive" + } + + /** + * Returns the first non-null, non-empty string from the pair, or None if both are absent. + * @param primary preferred value + * @param secondary fallback value + * @return Some(first non-empty value) or None + */ + def firstNonEmpty(primary: String, secondary: String): Option[String] = { + if (isNonEmpty(primary)) Some(primary) + else if (isNonEmpty(secondary)) Some(secondary) + else None + } +} diff --git a/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala b/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala new file mode 100644 index 0000000..f099321 --- /dev/null +++ b/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala @@ -0,0 +1,96 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.db.balta.validator + +import org.scalatest.funsuite.AnyFunSuite + +class ValidatorUnitTests extends AnyFunSuite { + + private val validator = new Validator + +// test("isPositive returns true for a positive integer") { +// assert(validator.isPositive(1)) +// } + +// test("isPositive returns false for zero") { +// assert(!validator.isPositive(0)) +// } + +// test("isPositive returns false for a negative integer") { +// assert(!validator.isPositive(-5)) +// } + + test("isNonEmpty returns true for a non-empty string") { + assert(validator.isNonEmpty("hello")) + } + + test("isNonEmpty returns false for an empty string") { + assert(!validator.isNonEmpty("")) + } + + test("isNonEmpty returns false for a null string") { + assert(!validator.isNonEmpty(null)) + } + +// test("isInRange returns true when value is within bounds") { +// assert(validator.isInRange(5, 1, 10)) +// } + +// test("isInRange returns true when value equals the lower bound") { +// assert(validator.isInRange(1, 1, 10)) +// } + +// test("isInRange returns true when value equals the upper bound") { +// assert(validator.isInRange(10, 1, 10)) +// } + +// test("isInRange returns false when value is below the lower bound") { +// assert(!validator.isInRange(0, 1, 10)) +// } + +// test("isInRange returns false when value is above the upper bound") { +// assert(!validator.isInRange(11, 1, 10)) +// } + + test("classify returns 'negative' for a negative integer") { + assert(validator.classify(-3) == "negative") + } + + test("classify returns 'zero' for zero") { + assert(validator.classify(0) == "zero") + } + + test("classify returns 'positive' for a positive integer") { + assert(validator.classify(7) == "positive") + } + +// test("firstNonEmpty returns the primary value when it is non-empty") { +// assert(validator.firstNonEmpty("first", "second") == Some("first")) +// } + +// test("firstNonEmpty returns the secondary value when primary is empty") { +// assert(validator.firstNonEmpty("", "second") == Some("second")) +// } + +// test("firstNonEmpty returns None when both values are empty") { +// assert(validator.firstNonEmpty("", "") == None) +// } + +// test("firstNonEmpty returns None when both values are null") { +// assert(validator.firstNonEmpty(null, null) == None) +// } +} From 8e85e4a3861344047d98c1bdfce9d9e8a6fb071c Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Sun, 17 May 2026 19:13:09 +0200 Subject: [PATCH 02/10] Experimental integration. --- .github/workflows/jacoco_report.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index dcb7da7..b8bbe5b 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -93,19 +93,28 @@ jobs: id: jacocorun run: sbt jacoco + # - name: Check coverage thresholds and add reports in PR comments + # id: jacoco + # uses: MoranaApps/jacoco-report@master + # with: + # token: '${{ secrets.GITHUB_TOKEN }}' + # paths: | + # balta/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 + - name: Check coverage thresholds and add reports in PR comments id: jacoco uses: MoranaApps/jacoco-report@master with: token: '${{ secrets.GITHUB_TOKEN }}' paths: | - balta/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 + **/jacoco.xml + global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}' noop: name: No Operation From 0c7404bf86a47f20ef98236ca3f61db1e3feedda Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Sun, 17 May 2026 19:25:04 +0200 Subject: [PATCH 03/10] chore: add Python setup step to JaCoCo workflow --- .github/workflows/jacoco_report.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index b8bbe5b..ba37276 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -77,6 +77,11 @@ jobs: with: java-version: "adopt@1.8" + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.14" + - name: Setup database run: | psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/02_users.ddl From 287daf4d05d73f104beb2645090df54f597c7b07 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Sun, 17 May 2026 21:06:23 +0200 Subject: [PATCH 04/10] test --- .github/workflows/jacoco_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index ba37276..8cd3e16 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -120,6 +120,7 @@ jobs: paths: | **/jacoco.xml global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}' + fail-on-threshold: '' noop: name: No Operation From 4774db92606adf95273883eb5b79a0e1c4ec008a Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:00:51 +0200 Subject: [PATCH 05/10] Clean up of demo changes. --- .github/CODEOWNERS | 1 - .github/ISSUE_TEMPLATE/bug_report.yml | 82 ----------- .github/ISSUE_TEMPLATE/devops_task.yml | 17 --- .github/ISSUE_TEMPLATE/documentation_task.yml | 53 ------- .github/ISSUE_TEMPLATE/epic_task.yml | 47 ------ .github/ISSUE_TEMPLATE/feature_request.yml | 65 --------- .github/ISSUE_TEMPLATE/operative_task.yml | 55 ------- .github/ISSUE_TEMPLATE/pointer.yml | 71 ---------- .github/ISSUE_TEMPLATE/spike_task.yml | 60 -------- .github/ISSUE_TEMPLATE/technical_debt.yml | 96 ------------- .github/copilot-instructions.md | 125 ---------------- .github/copilot-review-rules.md | 47 ------ .github/dependabot.yml | 16 --- .github/pull_request_template.md | 11 -- .github/workflows/assign_issue_to_project.yml | 30 ---- .github/workflows/build.yml | 77 ---------- .github/workflows/check_pr_release_notes.yml | 47 ------ .github/workflows/jacoco_report.yml | 131 ----------------- .github/workflows/release_draft.yml | 134 ------------------ .github/workflows/release_publish.yml | 44 ------ .github/workflows/test_filenames_check.yml | 42 ------ build.sbt | 36 ----- .../absa/db/balta/calculator/Calculator.scala | 74 ---------- .../calculator/CalculatorUnitTests.scala | 82 ----------- .../absa/db/balta/formatter/Formatter.scala | 66 --------- .../balta/formatter/FormatterUnitTests.scala | 80 ----------- .../absa/db/balta/validator/Validator.scala | 69 --------- .../balta/validator/ValidatorUnitTests.scala | 96 ------------- 28 files changed, 1754 deletions(-) delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/devops_task.yml delete mode 100644 .github/ISSUE_TEMPLATE/documentation_task.yml delete mode 100644 .github/ISSUE_TEMPLATE/epic_task.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 .github/ISSUE_TEMPLATE/operative_task.yml delete mode 100644 .github/ISSUE_TEMPLATE/pointer.yml delete mode 100644 .github/ISSUE_TEMPLATE/spike_task.yml delete mode 100644 .github/ISSUE_TEMPLATE/technical_debt.yml delete mode 100644 .github/copilot-instructions.md delete mode 100644 .github/copilot-review-rules.md delete mode 100644 .github/dependabot.yml delete mode 100644 .github/pull_request_template.md delete mode 100644 .github/workflows/assign_issue_to_project.yml delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/check_pr_release_notes.yml delete mode 100644 .github/workflows/jacoco_report.yml delete mode 100644 .github/workflows/release_draft.yml delete mode 100644 .github/workflows/release_publish.yml delete mode 100644 .github/workflows/test_filenames_check.yml delete mode 100644 calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala delete mode 100644 calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala delete mode 100644 formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala delete mode 100644 formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala delete mode 100644 validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala delete mode 100644 validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 9220c51..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @miroslavpojer @lsulak @salamonpavel @tmikula-dev @benedeki diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 07fc71a..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Bug Report -description: Create a report to help us improve -labels: ["bug"] -body: - - type: markdown - attributes: - value: | - Please supply complete reproduction details to speed up resolution. - - - type: textarea - id: bug-description - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. - placeholder: Tell us what went wrong. - validations: - required: true - - - type: textarea - id: reproduction-steps - attributes: - label: Steps to Reproduce - description: Steps to reproduce the behavior OR commands run - placeholder: | - 1. Go to '...' - 2. Click on '....' - 3. Enter value '...' - 4. See error - validations: - required: true - - - type: textarea - id: expected-state - attributes: - label: Expected state - description: A clear and concise description of what you expected to happen - placeholder: What should have happened? - validations: - required: true - - - type: dropdown - id: severity - attributes: - label: Impact / Severity - description: Level of impact - options: - - Blocker - - High - - Medium - - Low - validations: - required: true - - - type: textarea - id: evidence - attributes: - label: Attachments / Evidence - description: Logs, screenshots, traces. - placeholder: Paste relevant excerpts. - validations: - required: false - - - type: textarea - id: os-browser-version - attributes: - label: Desktop - description: What are your OS, browser, and version? - placeholder: | - OS: e.g., Windows 10, macOS 11.2, Ubuntu 20.04 - Browser: e.g., Chrome, Safari, Firefox - Version: e.g., 22 - validations: - required: false - - - type: textarea - id: related - attributes: - label: Related / References - description: Link other issues/PRs. - placeholder: "#456, #789" - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/devops_task.yml b/.github/ISSUE_TEMPLATE/devops_task.yml deleted file mode 100644 index e7777bc..0000000 --- a/.github/ISSUE_TEMPLATE/devops_task.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: DevOps Task -description: Issue template for a task about setting up the project. Usually not touching the codebase. -labels: ["infrastructure"] -body: - - type: markdown - attributes: - value: | - A clear and concise description of the task to be done. - - - type: textarea - id: task-description - attributes: - label: The task - description: Short description of the task - placeholder: Describe what needs to be done - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/documentation_task.yml b/.github/ISSUE_TEMPLATE/documentation_task.yml deleted file mode 100644 index 4fc01c6..0000000 --- a/.github/ISSUE_TEMPLATE/documentation_task.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Documentation -description: Update or create technical or operational documentation -labels: ["documentation"] -body: - - type: markdown - attributes: - value: | - Provide clear purpose and scope for the documentation update. - - - type: input - id: summary - attributes: - label: Summary - description: Short description of the documentation update. - placeholder: e.g. Add onboarding guide for new microservice - validations: - required: true - - - type: textarea - id: current-state - attributes: - label: Current State (If Updating) - description: Briefly describe existing content or gap. - placeholder: Existing runbook lacks failure recovery steps. - validations: - required: false - - - type: textarea - id: proposed-changes - attributes: - label: Proposed Changes - description: Key additions, removals, or restructures. - placeholder: Add section on X; remove deprecated Y. - validations: - required: true - - - type: textarea - id: dependencies - attributes: - label: Dependencies / References - description: Link related issues, PRs, external standards. - placeholder: "#123, ADR-7, compliance doc link." - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Any other information about the documentation update here. - placeholder: Add any other relevant context. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/epic_task.yml b/.github/ISSUE_TEMPLATE/epic_task.yml deleted file mode 100644 index 47c156f..0000000 --- a/.github/ISSUE_TEMPLATE/epic_task.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Epic -description: A larger task consisting of more deliverables. -labels: ["epic"] -body: - - type: markdown - attributes: - value: | - Use this template for large initiatives with multiple child issues. - - - type: textarea - id: background - attributes: - label: Background - description: High-level narrative and intended outcomes. - placeholder: Platform modernization initiative. - validations: - required: true - - - type: textarea - id: goals - attributes: - label: Goals - description: Bullet list of measurable goals. - placeholder: What do we want to accomplish? - validations: - required: true - - - type: textarea - id: subtasks - attributes: - label: Subtasks - description: List of planned child issues. - placeholder: | - - [ ] #123 Subtask 1 - - [ ] #223 Subtask 2 - - [ ] #323 Subtask 3 - validations: - required: false - - - type: textarea - id: references - attributes: - label: Related / References - description: Docs, ADRs, previous epics links. - placeholder: ADR-12 link, design doc. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 66331ff..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Feature Request -description: Suggest an idea for this project. -labels: ["enhancement"] -body: - - type: markdown - attributes: - value: | - Thank you for suggesting an improvement. Please provide clear, actionable detail. - - - type: textarea - id: feature-description - attributes: - label: Feature Description - description: A clear and concise description of what you want to happen. - placeholder: Describe the feature you'd like to see. - validations: - required: true - - - type: textarea - id: problem - attributes: - label: Problem / Opportunity - description: What user/business problem does this solve? Who benefits? - placeholder: Current pain, limitation, or missed opportunity. - validations: - required: true - - - type: textarea - id: acceptance - attributes: - label: Acceptance Criteria - description: Clear, testable outcomes. Use bullet points. - placeholder: | - 1. User can... - 2. API returns... - 3. Performance within... - validations: - required: true - - - type: textarea - id: proposed-solution - attributes: - label: Proposed Solution - description: High-level approach. Mention alternatives if considered. - placeholder: Describe the direction without full implementation detail. - validations: - required: false - - - type: textarea - id: dependencies - attributes: - label: Dependencies / Related - description: Link related issues, epics, or external docs. - placeholder: "#123, #456, ADR link" - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Any other information or screenshots about the feature request here - placeholder: Add any other relevant context or screenshots - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/operative_task.yml b/.github/ISSUE_TEMPLATE/operative_task.yml deleted file mode 100644 index 14fd100..0000000 --- a/.github/ISSUE_TEMPLATE/operative_task.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Operative / Management Task -description: General-purpose template for operational, management, and non-development tasks -labels: ["operative"] -body: - - type: markdown - attributes: - value: | - Use this flexible template for operational, management, planning, or administrative tasks. - - - type: input - id: summary - attributes: - label: Task Summary - description: Brief one-line description of the task - placeholder: e.g., Organize Q2 planning meeting, Update team guidelines. - validations: - required: true - - - type: textarea - id: description - attributes: - label: Description - description: Detailed description of what needs to be done and why - placeholder: Provide context, background, and objectives for this task - validations: - required: true - - - type: textarea - id: objectives - attributes: - label: Objectives / Goals - description: What are we trying to achieve? - placeholder: | - - Objective 1 - - Objective 2 - validations: - required: false - - - type: input - id: deadline - attributes: - label: Deadline / Target Date - description: When should this be completed? - placeholder: YYYY-MM-DD or "End of Sprint 5" - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Any other relevant information, links, or attachments - placeholder: Links to documents, previous discussions, related materials - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/pointer.yml b/.github/ISSUE_TEMPLATE/pointer.yml deleted file mode 100644 index e879cdf..0000000 --- a/.github/ISSUE_TEMPLATE/pointer.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Pointer -description: Track external work referenced across repositories -labels: ["pointer"] -body: - - type: markdown - attributes: - value: | - Use pointer issues to track external work (other repos, third-party tasks) impacting this domain. - - - type: input - id: summary - attributes: - label: Summary - description: Short description of external work. - placeholder: e.g. Migration of auth service in repo X - validations: - required: true - - - type: textarea - id: references - attributes: - label: Source / External References - description: List external issues/PRs/links (one per line). - placeholder: | - repo-a#123 - repo-b#456 - Design doc link - validations: - required: true - - - type: dropdown - id: relationship - attributes: - label: Relationship Type - description: Nature of linkage - options: - - Dependency - - Blocking - - Follow-up - - Mirroring - - Audit - - Observability - validations: - required: true - - - type: textarea - id: reason - attributes: - label: Reason for Tracking - description: Why visibility matters (release, compliance, coordination). - placeholder: Release impact; compliance trace; dependency risk. - validations: - required: true - - - type: textarea - id: closure - attributes: - label: Closure Conditions - description: When can this pointer be closed? - placeholder: External PR merged + deployed + docs updated. - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Any other information about the pointer here. - placeholder: Add any other relevant context. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/spike_task.yml b/.github/ISSUE_TEMPLATE/spike_task.yml deleted file mode 100644 index f5fd388..0000000 --- a/.github/ISSUE_TEMPLATE/spike_task.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Spike -description: Issue template for spikes, research and investigation tasks. -labels: ["spike"] -body: - - type: markdown - attributes: - value: | - Use this form for research/investigation tasks. Keep scope tight. - - - type: textarea - id: background - attributes: - label: Background - description: Describe the problem or topic we need to understand. - placeholder: Tell us what you need to research or investigate. - validations: - required: true - - - type: textarea - id: questions - attributes: - label: Questions To Answer - description: List the key questions that need to be answered - placeholder: | - 1. Question one - 2. Question two - 3. Question three - validations: - required: true - - - type: textarea - id: desired-outcome - attributes: - label: Desired Outcome - description: List the desired outcomes of this spike ticket. - placeholder: What should be the result of this investigation? - validations: - required: true - - - type: checkboxes - id: tasks - attributes: - label: Tasks - options: - - label: Questions have been answered or we have a clearer idea of how to get to our goal - - label: Discussion with the team - - label: Documentation - - label: Create recommendations and new implementation tickets - - - type: textarea - id: additional-info - attributes: - label: Additional Info/Resources - description: Optional additional information or resources - placeholder: | - 1. Resource one - 2. Resource two - 3. Resource three - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/technical_debt.yml b/.github/ISSUE_TEMPLATE/technical_debt.yml deleted file mode 100644 index a30adc7..0000000 --- a/.github/ISSUE_TEMPLATE/technical_debt.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Technical Debt -description: Track and address technical debt items to improve code quality, maintainability, or architecture. -labels: ["tech debt"] -body: - - type: markdown - attributes: - value: | - Use this template to document technical debt that should be addressed to improve long-term code health. - - - type: textarea - id: debt-description - attributes: - label: Description of Technical Debt - description: What is the current state that needs improvement? - placeholder: Describe the technical debt (outdated dependencies, code smells, architecture issues, etc.) - validations: - required: true - - - type: textarea - id: impact - attributes: - label: Impact of Technical Debt - description: What problems does this debt cause or risk causing? - placeholder: | - - Slows down development - - Increases bug risk - - Makes testing difficult - - Blocks future features - validations: - required: true - - - type: dropdown - id: category - attributes: - label: Category - description: Type of technical debt - options: - - Code Quality / Refactoring - - Architecture / Design - - Dependencies / Libraries - - Documentation - - Testing / Test Coverage - - Performance - - Security - - DevOps / Infrastructure - - Other - validations: - required: true - - - type: dropdown - id: priority - attributes: - label: Priority - description: How urgent is this to address? - options: - - High - Actively causing problems - - Medium - Should be addressed soon - - Low - Nice to have - validations: - required: true - - - type: textarea - id: proposed-solution - attributes: - label: Proposed Solution - description: How should this be addressed? Include alternatives if applicable. - placeholder: Describe the refactoring approach, migration plan, or improvement strategy. - validations: - required: false - - - type: input - id: effort-estimate - attributes: - label: Effort Estimate - description: Rough estimate of work required - placeholder: e.g., 1 day, 1 week, 2 sprints - validations: - required: false - - - type: textarea - id: dependencies - attributes: - label: Dependencies / Related - description: Link related issues, epics, or documentation. - placeholder: "#123, #456, ADR link" - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Any other relevant information, links to discussions, or examples. - placeholder: Add code snippets, links to similar work, or other references. - validations: - required: false diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index c10fd04..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,125 +0,0 @@ -Copilot instructions - -Purpose -- Define consistent style; keep externally-visible behavior stable unless intentionally changing the contract. -- Sections order, bullet lists, and constraint words (Must / Must not / Prefer / Avoid) must be consistent across repos. -- Must keep one blank line at end of file. - -Context -- Scala library consumed via sbt in CI/CD; treat JDBC connections and DB state as boundary concerns. -- Must keep library API backward-compatible unless a version bump is intentional. - -Coding guidelines -- Must keep changes small and focused; prefer explicit over clever. -- Must keep pure logic free of environment access; route I/O through a single boundary module. -- Must keep externally-visible strings, formats, and exit codes stable unless intentional. - -Output discipline -- Aim ≤ 10 lines in final recap; link and summarize deltas rather than pasting large content. -- Prefer actionable bullets; avoid deep rationale unless requested. -- Code changes must end with: what changed / why / how to verify. - -PR Body Management -- Treat PR description as a changelog; append under `## Update [YYYY-MM-DD]` or `## Changes Added`. -- Must not rewrite the entire PR body; must reference the commit hash that introduced the change. - -Configuration -- Must read DB connection details from `database.properties` test resource; centralize in `ConnectionInfo`. -- Must not hard-code credentials; document required vs optional `ConnectionInfo` fields with defaults. - -Language and style -- Must target Scala 2.12 primary; cross-compile 2.11 and 2.13 where configured. -- Must add ScalaDoc for new public methods and classes. -- Must use slf4j (`LazyLogging` or explicit `Logger`), not `println`/`print`; pass logger arguments lazily (call-by-name), never pre-build interpolated strings. -- Must keep imports at top of file, grouped: stdlib → third-party → project-internal. -- Must include the Apache 2.0 copyright/license header in every source file using the project first-copyright year. -- Prefer `s"..."` interpolation for non-logging templates; avoid interpolation inside `logger.*` calls. - -Docstrings and comments -- ScalaDoc: short summary line first; prefer Parameters/Returns/Raises sections; avoid tutorials and long prose. -- Comments: comment intent/edge cases only; avoid restating the code. - -Patterns -- Prefer raising exceptions in leaf modules; translate to test/library failure at the entry point (`DBTestSuite.test()`). -- Must keep boundaries mockable; must not call real external systems (DB, APIs) in unit tests. - -Testing -- Must use ScalaTest under `balta/src/test/scala/`. -- Must test return values, exceptions, and error messages. -- Unit test files: name ends with `UnitTests`; must not require a DB connection; mock JDBC (`PreparedStatement`/`ResultSet`) if needed. -- Integration test files: name ends with `IntegrationTests`; must extend `DBTestSuite` or mix in `DBTestingConnection`. -- Must not access private members of the class under test. -- Prefer `AnyFunSuite`/`AnyFunSuiteLike`; use `AnyWordSpec` only when given/when/then adds clarity. -- Must place shared test helpers and fixtures in the root test package (`za.co.absa.db.balta`) and reuse them across tests; shared files are excluded from the filename-inspector check. -- Must not add top-level comments in `*Tests.scala` outside test methods; use nested `"..."` / `should` / `when` blocks (idiomatic to the chosen suite style) to separate groups. -- Prefer TDD workflow: - - Must create or update `SPEC.md` before writing any code, listing scenarios, inputs, and expected outputs. - - Must propose the full test case set (name + intent + input + expected output) and wait for user confirmation before coding. - - Must write all failing tests first (red), then implement until all pass (green). - - Must cover all distinct combinations; each test must state its scenario in the ScalaDoc. - - Must update `SPEC.md` after all tests pass with the confirmed test case table. -- When a unit test adds value — write one: - - Method has any own logic: branching (`if`/`match`), exception handling or swallowing, non-trivial transformation, config/resource read, or reflection. -- When to add to `jmf-rules.txt` instead of writing a unit test: - - Body is a single call with no own logic: forwards to another overload, calls its non-deprecated replacement, returns a field, or wraps a constructor with no transformation. - - Litmus: "Does this method have any logic of its own?" — No → JMF. -- Global rule collision check (CRITICAL): - - When adding any new method, check if it matches a pattern in the `# GLOBAL RULES` section of `jmf-rules.txt` (line ~22+). - - If method name matches a global rule AND the method has domain logic: immediately create an INCLUDE rescue rule (`+FQCN#method(*)`) in the `# INCLUDE RULES` section of `jmf-rules.txt`. - - High-risk method names: refer to `jmf-rules.txt` GLOBAL RULES section for complete list; most common collisions: `apply()`, `toString()`, `equals()`, `copy()`, `name()`, `groups()`, `optionalAttributes()`. - - Rationale: Broad global rules designed for compiler-generated boilerplate can silently hide 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 rescue from the `*$*#apply(*)` global rule in GLOBAL RULES section. -- Review rule — JMF drift check: when modifying a method that appears in `jmf-rules.txt`, verify its body still qualifies; if own logic has been added, remove the JMF rule and write a unit test instead. - -Tooling -- Must format with scalafmt (`.scalafmt.conf`); lint with scalastyle (`scalastyle-config.xml`) or wartremover as configured. -- Compiler warnings treated as errors where configured; coverage ≥ 80% via sbt-jacoco (excluding JMF-filtered methods in `jmf-rules.txt`). - -Coverage filtering (JMF) -- A method qualifies when its body is a single call with no own logic (see Testing section for the full decision rule and qualifying patterns). -- Must not add JMF rules for methods with branching logic, error handling, or non-trivial transformations. -- Rules file: `jmf-rules.txt`; one rule per line, `#` comments and blank lines ignored. -- Rule syntax: `#() [FLAGS] [PREDICATES]` - - FQCN_glob: dot-form class pattern (`*`, `*.model.*`, `com.example.*`; `$` for inner/companion classes). - - method_glob: glob on method name (`copy`, `get*`, `$anonfun$*`, `*_$eq`). - - descriptor_glob: JVM descriptor `(args)ret`; omitting or `(*)` means any args/any return. Must use JVM internal format: `(I)*` not `(int)`, `(Z)*` not `(boolean)`, `(Ljava/lang/String;)*` not `(java.lang.String)`. Non-matching descriptors are silently ignored — no warning is emitted. - - FLAGS (space/comma separated): `public`, `protected`, `private`, `synthetic`, `bridge`, `static`, `abstract`. - - PREDICATES: `ret:` (return type), `id:` (log label), `name-contains:`, `name-starts:`, `name-ends:`. -- Every rule Must include an `id:` label for traceability. -- Adoption order: start with CONSERVATIVE (case-class boilerplate, compiler synthetics), then STANDARD; use AGGRESSIVE only for DTO/auto-generated packages. -- Prefer narrow package scopes; prefer `synthetic`/`bridge` flags for compiler artifacts over broad wildcards. -- Must prefix project-specific FQCN globs with `*` so they match the full qualified class name (e.g. `*QueryResult#noMore()`, not `QueryResult#noMore()`). -- When adding a project-specific rule, add it under the `# PROJECT RULES` section with a comment explaining why the method qualifies. - -Quality gates -- sbt "testOnly *UnitTests" # unit tests, no DB needed -- sbt test # all tests, DB must be running -- sbt jacoco # coverage → balta/target/scala-*/jacoco/ -- sbt scalafmtCheck # format check -- sbt scalastyle # lint - -Common pitfalls to avoid -- Dependencies: verify compatibility across all cross-compiled Scala versions. -- Unsafe casts: avoid `.asInstanceOf`; document why when unavoidable. -- Logging: never pre-build log strings; always pass args lazily (call-by-name). -- Cleanup: remove unused imports/variables (scalac `-Ywarn-unused`). -- Stability: avoid changing externally-visible method signatures, SQL output, or parameter binding order. -- JMF silent failures: rules with non-matching FQCN or descriptor globs are silently ignored; always verify new rules take effect by comparing JaCoCo method-level output before and after. -- Scala reflection: case classes used with `currentMirror.reflectClass` in tests must be top-level (package scope), not inner classes — Scala reflection cannot handle inner class mirrors. - -Learned rules -- Must not change `QueryResultRow` getter return types or `stringPerConvention` output for existing scenarios. -- Must not change externally-visible SQL generation patterns without updating dependent tests. -- Scala 2.12 compiler artifacts that generate coverable bytecode: `$anonfun$` lambdas (ACC_SYNTHETIC), `$deserializeLambda$` (private static, not synthetic), Function1 mixin forwarders (`andThen`/`compose`), value class `$extension` methods, `$default$` parameter methods, Iterator trait mixin (~80+ forwarders). These are JMF candidates, not unit-test candidates. - -Repo additions -- Project name: balta -- Entry points: `build.sbt`, `balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala` -- Core package: `za.co.absa.db.balta`; supporting: `za.co.absa.db.mag` (naming conventions) -- DB boundary: `DBConnection` (JDBC wrapper), `DBTestSuite` (transaction rollback/commit) -- Contract-sensitive: SQL generation in `DBTable`/`DBFunction`; parameter binding order; `QueryResultRow` getter return types; `stringPerConvention` output -- Coverage tool: sbt-jacoco with JMF filter rules in `jmf-rules.txt` -- Commands: - - sbt "testOnly *UnitTests" # unit tests, no DB needed - - sbt test # all tests, DB must be running - - sbt jacoco # coverage report diff --git a/.github/copilot-review-rules.md b/.github/copilot-review-rules.md deleted file mode 100644 index 47fe590..0000000 --- a/.github/copilot-review-rules.md +++ /dev/null @@ -1,47 +0,0 @@ -# Copilot Review Rules - -Purpose -- Define consistent, concise, action-oriented review behavior. -- Use short headings and bullet lists; prefer do/avoid constraints over prose; point to code and impact. - -Mode: Default review -- Scope: single PR, normal risk. -- Priority order: correctness → security → tests → maintainability → style. -- Checks: - - Correctness: logic bugs, missing edge cases, regressions, contract changes. - - Security: unsafe input handling, secrets exposure, auth/authz issues, insecure defaults. - - Tests: tests exist for changed logic; success + failure paths covered. - - Maintainability: unnecessary complexity, duplication, unclear naming/structure. - - Style: flag only when readability or repo conventions are broken. -- Response format: short bullets; files + line ranges; severity groups (Blocker / Important / Nit); minimal actionable suggestions; no rewrites. - -Mode: Double-check review -- Scope: higher-risk PRs (security, infra/CI, wide refactors, data migrations, auth changes). -- Additional focus: - - Confirm previous review comments were addressed. - - Re-check: auth, permissions, secrets, persistence, external calls, concurrency. - - Hidden side effects: backward compatibility, failure modes, retries, idempotency. - - Safe defaults: least privilege, secure logging, safe error messages, missing-input behavior. -- Response format: comment only where risk/impact is non-trivial; no repeated style notes; call out explicit risk acceptance (what / why acceptable / mitigation). - -Commenting rules (all modes) -- Every comment must state: what the issue is · why it matters · minimal fix suggestion. -- Prefer linking to existing repo patterns over introducing new ones. -- If context is missing, ask a targeted question instead of assuming. - -Non-goals -- No refactors unrelated to the PR's intent. -- No formatting bikeshedding when the formatter/linter handles it. -- No architectural rewrites unless explicitly requested. - -Repo additions -- High-risk areas: - - DB transactions: `DBTestSuite` rollback/commit; do not change `persistData` behavior unintentionally. - - SQL injection: parameter binding uses `PreparedStatement`; no raw string concatenation of untrusted input. - - JDBC lifecycle: connections must be rolled back or committed; validate `try/finally` in `DBQuerySupport`. - - Contract-sensitive: `QueryResultRow` getter signatures; `stringPerConvention` output; `jmf-rules.txt`. - - Deprecations: do not extend `setParamNull`/`addNull` deprecated API surface. -- Required tests: - - `*UnitTests`: no DB connection; mock `PreparedStatement`/`ResultSet` if needed. - - `*IntegrationTests`: extend `DBTestSuite` or `DBTestingConnection`. - - New naming convention implementations must have a `UnitTests` covering all `LettersCase` variants. diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5973091..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - target-branch: "master" - schedule: - interval: "weekly" - day: "sunday" - labels: - - "auto update" - - "infrastructure" - - "no RN" - open-pull-requests-limit: 3 - commit-message: - prefix: "chore" - include: "scope" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 323ac01..0000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,11 +0,0 @@ - -## Overview - - -## Release Notes -- TBD: 1st item of release notes -- TBD: 2nd item of release notes - - -## Related -Closes #issue_number diff --git a/.github/workflows/assign_issue_to_project.yml b/.github/workflows/assign_issue_to_project.yml deleted file mode 100644 index 6ae7144..0000000 --- a/.github/workflows/assign_issue_to_project.yml +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright 2023 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Auto Assign Issue to Project - -on: - issues: - types: [opened, transferred] - -jobs: - add-to-project: - name: Add issue to project - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd - with: - project-url: https://github.com/orgs/AbsaOSS/projects/7 - github-token: ${{ secrets.PAT_REPO_PROJECT_DISCUSS }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index ca3c49c..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,77 +0,0 @@ -# -# Copyright 2023 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Build - -on: - # pull_request: - # branches: - # - '**' - # types: [ opened, synchronize, reopened ] - workflow_dispatch: - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - scala: [2.11.12, 2.12.18, 2.13.13] - - name: Scala ${{matrix.scala}} - - services: - postgres: - image: postgres:15 - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: mag_db - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 - - - name: Setup Scala - uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c - with: - java-version: "adopt@1.8" - - - name: Build and run unit tests - run: sbt ++${{matrix.scala}} test doc - - - name: Setup database - run: | - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/02_users.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/03_schema_testing.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/04_testing.base_types.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/05_testing._base_types_data.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/06_testing.pg_types.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/07_testing_pg_types_data.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/08_testing.simple_function.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/09_testing.table_lifecycle.ddl - - - name: Build and run integration tests - run: sbt ++${{matrix.scala}} testIT diff --git a/.github/workflows/check_pr_release_notes.yml b/.github/workflows/check_pr_release_notes.yml deleted file mode 100644 index 08e45a0..0000000 --- a/.github/workflows/check_pr_release_notes.yml +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright 2023 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Check PR Release Notes - -on: - # pull_request: - # types: [opened, synchronize, reopened, edited, labeled, unlabeled] - # branches: [ master ] - workflow_dispatch: - -env: - SKIP_LABEL: 'no RN' - RLS_NOTES_TAG_REGEX: 'Release Notes:' - -jobs: - check-release-notes: - runs-on: ubuntu-latest - - steps: - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - with: - python-version: '3.13' - - - name: Check presence of release notes in PR description - uses: AbsaOSS/release-notes-presence-check@8e586b26a5e27f899ee8590a5d988fd4780a3dbf - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - github-repository: ${{ github.repository }} - pr-number: ${{ github.event.number }} - title: "## [Rr]elease [Nn]otes" - skip-labels: 'no RN' - skip-placeholders: 'TBD' diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml deleted file mode 100644 index 8cd3e16..0000000 --- a/.github/workflows/jacoco_report.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: CI check JaCoCo code-coverage - -on: - pull_request: - branches: [ master ] - -env: - scalaLong: 2.12.18 - scalaShort: "2.12" - coverage-overall: 80.0 - coverage-changed-files: 80.0 - coverage-per-changed-file: 0.0 - -jobs: - detect: - name: Scala Changes Detection - runs-on: ubuntu-latest - outputs: - scala_changed: ${{ steps.changes.outputs.scala_changed }} - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - persist-credentials: false - fetch-depth: 0 - - - name: Check if Scala files changed (excluding project/) - id: changes - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - CHANGED_FILES=$(gh api \ - --paginate \ - "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \ - --jq '.[].filename - | select(endswith(".scala")) - | select(startswith("project/") | not)') - else - CHANGED_FILES=$(git diff --name-only "${{ github.sha }}~1" "${{ github.sha }}" -- '**/*.scala' | grep -v '^project/' || true) - fi - - if [[ -n "${CHANGED_FILES}" ]]; then - echo "scala_changed=true" >> "$GITHUB_OUTPUT" - else - echo "scala_changed=false" >> "$GITHUB_OUTPUT" - fi - - build-test-and-measure: - name: Build, Test and Measure - needs: detect - if: needs.detect.outputs.scala_changed == 'true' - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:15 - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: mag_db - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - name: Setup Scala - uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c - with: - java-version: "adopt@1.8" - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: "3.14" - - - name: Setup database - run: | - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/02_users.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/03_schema_testing.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/04_testing.base_types.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/05_testing._base_types_data.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/06_testing.pg_types.ddl - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/07_testing_pg_types_data.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/08_testing.simple_function.sql - psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/09_testing.table_lifecycle.ddl - - - name: Build and run tests - continue-on-error: true - id: jacocorun - run: sbt jacoco - - # - name: Check coverage thresholds and add reports in PR comments - # id: jacoco - # uses: MoranaApps/jacoco-report@master - # with: - # token: '${{ secrets.GITHUB_TOKEN }}' - # paths: | - # balta/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 - - - name: Check coverage thresholds and add reports in PR comments - id: jacoco - uses: MoranaApps/jacoco-report@master - with: - token: '${{ secrets.GITHUB_TOKEN }}' - paths: | - **/jacoco.xml - global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}' - fail-on-threshold: '' - - noop: - name: No Operation - needs: detect - if: needs.detect.outputs.scala_changed != 'true' - runs-on: ubuntu-latest - steps: - - run: echo "No changes in the *.scala files (excluding project/) — passing." diff --git a/.github/workflows/release_draft.yml b/.github/workflows/release_draft.yml deleted file mode 100644 index 6820997..0000000 --- a/.github/workflows/release_draft.yml +++ /dev/null @@ -1,134 +0,0 @@ -# -# Copyright 2021 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Draft Release -on: - workflow_dispatch: - inputs: - tag-name: - description: 'Name of git tag to be created, and then draft release created. Syntax: "v[0-9]+.[0-9]+.[0-9]+".' - required: true - from-tag-name: - description: 'Name of the git tag from which to detect changes from. Default value: latest tag. Syntax: "v[0-9]+.[0-9]+.[0-9]+".' - required: false - -jobs: - release-draft: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - fetch-depth: 0 - persist-credentials: false - - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - with: - python-version: '3.13' - - - name: Check Format of Received Tag - id: check-version-tag - uses: AbsaOSS/version-tag-check@4145e48bf3f77a5afff2ec9afdd8afb6b53bce34 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - github-repository: ${{ github.repository }} - version-tag: ${{ github.event.inputs.tag-name }} - - - name: Check Format of Received From Tag - if: ${{ github.event.inputs.from-tag-name }} - id: check-version-from-tag - uses: AbsaOSS/version-tag-check@4145e48bf3f77a5afff2ec9afdd8afb6b53bce34 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - github-repository: ${{ github.repository }} - version-tag: ${{ github.event.inputs.from-tag-name }} - should-exist: true - - - name: Generate Release Notes - id: generate_release_notes - uses: AbsaOSS/generate-release-notes@B90223510d1704301a36a36f0d86a72a0e72f0cf - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag-name: ${{ github.event.inputs.tag-name }} - from-tag-name: ${{ github.event.inputs.from-tag-name }} - chapters: | - - {"title": "Entries to skip 🚫", "label": "duplicate"} - - {"title": "Entries to skip 🚫", "label": "invalid"} - - {"title": "Entries to skip 🚫", "label": "wontfix"} - - {"title": "Entries to skip 🚫", "label": "no RN"} - - {"title": "Breaking Changes 💥", "label": "breaking change"} - - {"title": "Security updates 👮", "label": "security"} - - {"title": "New Features 🎉", "label": "enhancement"} - - {"title": "Bugfixes 🛠", "label": "bug"} - - {"title": "Epics 📖", "label": "epic"} - - {"title": "Infrastructure ⚙️", "label": "infrastructure"} - - {"title": "Silent-live 🤫", "label": "silent live"} - - {"title": "Documentation 📜", "label": "documentation"} - duplicity-scope: 'none' - warnings: true - skip-release-notes-labels: "no RN,duplicate,invalid,wontfix" - print-empty-chapters: false - row-format-issue: '_{title}_ {developers} in {number}' - row-format-pr: '_{title}_ {developers} in {number}' - row-format-link-pr: true - hierarchy: true - - - name: Create and Push Tag (standalone) - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 - with: - script: | - const tag = core.getInput('tag-name') - const ref = `refs/tags/${tag}`; - const sha = context.sha; // The SHA of the commit to tag - const tagMessage = `${tag} released by GitHub Action`; - - const tagObject = await github.rest.git.createTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag: tag, - message: tagMessage, - object: sha, - type: 'commit', - tagger: { - name: context.actor, - email: `${context.actor}@users.noreply.github.com`, - date: new Date().toISOString() - } - }); - - await github.rest.git.createRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: ref, - sha: tagObject.data.sha - }); - - console.log(`Tag created: ${tag}`); - github-token: ${{ secrets.GITHUB_TOKEN }} - tag-name: ${{ github.event.inputs.tag-name }} - - - name: Create draft release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - name: ${{ github.event.inputs.tag-name }} - body: ${{ steps.generate_release_notes.outputs.release-notes }} - tag_name: ${{ github.event.inputs.tag-name }} - draft: true - prerelease: false diff --git a/.github/workflows/release_publish.yml b/.github/workflows/release_publish.yml deleted file mode 100644 index f11b648..0000000 --- a/.github/workflows/release_publish.yml +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright 2023 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Release - publish artifacts -on: - release: - types: [released] - -jobs: - publish-to-sonatype: - name: Publish to Sonatype - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - fetch-depth: 0 - - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 - - - name: Setup Scala - uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c - with: - java-version: "adopt@1.8" - - - name: Run sbt ci-release - run: sbt ci-release - env: - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - PGP_SECRET: ${{ secrets.PGP_SECRET }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/.github/workflows/test_filenames_check.yml b/.github/workflows/test_filenames_check.yml deleted file mode 100644 index f17ea0c..0000000 --- a/.github/workflows/test_filenames_check.yml +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright 2023 ABSA Group Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -name: Test Filenames Check - -on: - # pull_request: - # branches: [ master ] - # types: [ opened, synchronize, reopened ] - workflow_dispatch: - -jobs: - test_filenames_check: - name: Test Filenames Check - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - - name: Filename Inspector - id: scan-test-files - uses: AbsaOSS/filename-inspector@355108975e656fac9faaa04209b6df3f9997c8fa - with: - name-patterns: '*UnitTests.*,*IntegrationTests.*' - paths: '**/src/test/scala/**' - report-format: 'console' - excludes: 'balta/src/test/scala/za/co/absa/db/balta/testing/**' - verbose-logging: 'false' - fail-on-violation: 'true' diff --git a/build.sbt b/build.sbt index e7fbab3..9b9abac 100644 --- a/build.sbt +++ b/build.sbt @@ -41,39 +41,3 @@ lazy val balta = (project in file("balta")) jmfReportFile := Some(target.value / "jmf-report.json"), jmfReportFormat := "json", ) - -lazy val calculator = (project in file("calculator")) - .enablePlugins(JacocoFilterPlugin) - .settings( - name := "balta-calculator", - scalacOptions ++= Seq( - "-deprecation", - "-feature", - ), - crossScalaVersions := supportedScalaVersions, - libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, - ) - -lazy val validator = (project in file("validator")) - .enablePlugins(JacocoFilterPlugin) - .settings( - name := "balta-validator", - scalacOptions ++= Seq( - "-deprecation", - "-feature", - ), - crossScalaVersions := supportedScalaVersions, - libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, - ) - -lazy val formatter = (project in file("formatter")) - .enablePlugins(JacocoFilterPlugin) - .settings( - name := "balta-formatter", - scalacOptions ++= Seq( - "-deprecation", - "-feature", - ), - crossScalaVersions := supportedScalaVersions, - libraryDependencies += "org.scalatest" %% "scalatest" % Versions.scalatest % Test, - ) diff --git a/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala b/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala deleted file mode 100644 index 202547b..0000000 --- a/calculator/src/main/scala/za/co/absa/db/balta/calculator/Calculator.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.calculator - -/** - * Simple arithmetic calculator used to produce measurable JaCoCo coverage data. - */ -class Calculator { - - /** - * Returns the sum of two integers. - * @param a first operand - * @param b second operand - * @return a + b - */ - def add(a: Int, b: Int): Int = a + b - - /** - * Returns the difference of two integers. - * @param a first operand - * @param b second operand - * @return a - b - */ - def subtract(a: Int, b: Int): Int = a - b - - /** - * Returns the product of two integers. - * @param a first operand - * @param b second operand - * @return a * b - */ - def multiply(a: Int, b: Int): Int = a * b - - /** - * Returns the integer quotient of a divided by b. - * @param a dividend - * @param b divisor; must not be zero - * @return a / b - * @throws ArithmeticException if b is zero - */ - def divide(a: Int, b: Int): Int = { - if (b == 0) throw new ArithmeticException("Division by zero") - a / b - } - - /** - * Returns the absolute value of an integer. - * @param a input value - * @return non-negative value of a - */ - def abs(a: Int): Int = if (a < 0) -a else a - - /** - * Returns the larger of two integers. - * @param a first value - * @param b second value - * @return the maximum of a and b - */ - def max(a: Int, b: Int): Int = if (a >= b) a else b -} diff --git a/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala b/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala deleted file mode 100644 index 24022d6..0000000 --- a/calculator/src/test/scala/za/co/absa/db/balta/calculator/CalculatorUnitTests.scala +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.calculator - -import org.scalatest.funsuite.AnyFunSuite - -class CalculatorUnitTests extends AnyFunSuite { - - private val calc = new Calculator - - test("add returns the sum of two positive integers") { - assert(calc.add(3, 4) == 7) - } - - test("add returns the sum when one operand is negative") { - assert(calc.add(-2, 5) == 3) - } - -// test("subtract returns the difference of two integers") { -// assert(calc.subtract(10, 4) == 6) -// } - -// test("subtract returns negative result when b is greater than a") { -// assert(calc.subtract(2, 9) == -7) -// } - -// test("multiply returns the product of two integers") { -// assert(calc.multiply(3, 7) == 21) -// } - -// test("multiply returns zero when one operand is zero") { -// assert(calc.multiply(0, 99) == 0) -// } - - test("divide returns the integer quotient") { - assert(calc.divide(10, 2) == 5) - } - - test("divide throws ArithmeticException when divisor is zero") { - intercept[ArithmeticException] { - calc.divide(5, 0) - } - } - -// test("abs returns the same value for a positive integer") { -// assert(calc.abs(7) == 7) -// } - -// test("abs returns the negated value for a negative integer") { -// assert(calc.abs(-7) == 7) -// } - -// test("abs returns zero for zero input") { -// assert(calc.abs(0) == 0) -// } - - test("max returns the first value when it is greater") { - assert(calc.max(9, 3) == 9) - } - - test("max returns the second value when it is greater") { - assert(calc.max(3, 9) == 9) - } - - test("max returns the common value when both are equal") { - assert(calc.max(5, 5) == 5) - } -} diff --git a/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala b/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala deleted file mode 100644 index d648913..0000000 --- a/formatter/src/main/scala/za/co/absa/db/balta/formatter/Formatter.scala +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.formatter - -/** - * String formatter used to produce measurable JaCoCo coverage data. - */ -class Formatter { - - /** - * Capitalizes the first character of a string, leaving the rest unchanged. - * Returns the input unchanged when it is null or empty. - * @param s input string - * @return string with the first character uppercased - */ - def capitalize(s: String): String = { - if (s == null || s.isEmpty) s - else s.head.toUpper + s.tail - } - - /** - * Truncates a string to at most maxLen characters, appending "..." when truncation occurs. - * @param s input string - * @param maxLen maximum allowed length before truncation - * @return the original string or its prefix followed by "..." - */ - def truncate(s: String, maxLen: Int): String = { - if (s.length <= maxLen) s - else s.take(maxLen) + "..." - } - - /** - * Wraps a string with a prefix and suffix. - * @param s input string - * @param prefix string prepended to s - * @param suffix string appended to s - * @return prefix + s + suffix - */ - def wrap(s: String, prefix: String, suffix: String): String = s"$prefix$s$suffix" - - /** - * Repeats a string a given number of times. - * Returns an empty string when times is zero or negative. - * @param s string to repeat - * @param times number of repetitions - * @return concatenation of s repeated times - */ - def repeat(s: String, times: Int): String = { - if (times <= 0) "" - else s * times - } -} diff --git a/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala b/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala deleted file mode 100644 index 6d94dfa..0000000 --- a/formatter/src/test/scala/za/co/absa/db/balta/formatter/FormatterUnitTests.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.formatter - -import org.scalatest.funsuite.AnyFunSuite - -class FormatterUnitTests extends AnyFunSuite { - - private val fmt = new Formatter - - test("capitalize uppercases the first character of a lowercase word") { - assert(fmt.capitalize("hello") == "Hello") - } - - test("capitalize leaves an already-capitalized string unchanged") { - assert(fmt.capitalize("Hello") == "Hello") - } - - test("capitalize returns an empty string unchanged") { - assert(fmt.capitalize("") == "") - } - - test("capitalize returns null unchanged") { - assert(fmt.capitalize(null) == null) - } - - test("capitalize handles a single-character string") { - assert(fmt.capitalize("a") == "A") - } - -// test("truncate returns the string unchanged when shorter than maxLen") { -// assert(fmt.truncate("hi", 10) == "hi") -// } - -// test("truncate returns the string unchanged when equal to maxLen") { -// assert(fmt.truncate("hello", 5) == "hello") -// } - -// test("truncate appends ellipsis when the string exceeds maxLen") { -// assert(fmt.truncate("hello world", 5) == "hello...") -// } - - test("wrap surrounds a string with the given prefix and suffix") { - assert(fmt.wrap("world", "Hello, ", "!") == "Hello, world!") - } - - test("wrap with empty prefix and suffix returns the original string") { - assert(fmt.wrap("world", "", "") == "world") - } - -// test("repeat returns the string concatenated the given number of times") { -// assert(fmt.repeat("ab", 3) == "ababab") -// } - -// test("repeat returns an empty string when times is zero") { -// assert(fmt.repeat("ab", 0) == "") -// } - -// test("repeat returns an empty string when times is negative") { -// assert(fmt.repeat("ab", -1) == "") -// } - -// test("repeat with times equal to one returns the original string") { -// assert(fmt.repeat("ab", 1) == "ab") -// } -} diff --git a/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala b/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala deleted file mode 100644 index 8e5b228..0000000 --- a/validator/src/main/scala/za/co/absa/db/balta/validator/Validator.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.validator - -/** - * Value and string validator used to produce measurable JaCoCo coverage data. - */ -class Validator { - - /** - * Returns true when the integer is strictly positive. - * @param value the integer to check - * @return true if value > 0 - */ - def isPositive(value: Int): Boolean = value > 0 - - /** - * Returns true when the string is non-null and non-empty. - * @param value the string to check - * @return true if value is not null and not empty - */ - def isNonEmpty(value: String): Boolean = value != null && value.nonEmpty - - /** - * Returns true when value falls within the inclusive range [min, max]. - * @param value the integer to check - * @param min lower bound (inclusive) - * @param max upper bound (inclusive) - * @return true if min <= value <= max - */ - def isInRange(value: Int, min: Int, max: Int): Boolean = value >= min && value <= max - - /** - * Classifies an integer as "negative", "zero", or "positive". - * @param value the integer to classify - * @return one of "negative", "zero", or "positive" - */ - def classify(value: Int): String = value match { - case v if v < 0 => "negative" - case 0 => "zero" - case _ => "positive" - } - - /** - * Returns the first non-null, non-empty string from the pair, or None if both are absent. - * @param primary preferred value - * @param secondary fallback value - * @return Some(first non-empty value) or None - */ - def firstNonEmpty(primary: String, secondary: String): Option[String] = { - if (isNonEmpty(primary)) Some(primary) - else if (isNonEmpty(secondary)) Some(secondary) - else None - } -} diff --git a/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala b/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala deleted file mode 100644 index f099321..0000000 --- a/validator/src/test/scala/za/co/absa/db/balta/validator/ValidatorUnitTests.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2023 ABSA Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.db.balta.validator - -import org.scalatest.funsuite.AnyFunSuite - -class ValidatorUnitTests extends AnyFunSuite { - - private val validator = new Validator - -// test("isPositive returns true for a positive integer") { -// assert(validator.isPositive(1)) -// } - -// test("isPositive returns false for zero") { -// assert(!validator.isPositive(0)) -// } - -// test("isPositive returns false for a negative integer") { -// assert(!validator.isPositive(-5)) -// } - - test("isNonEmpty returns true for a non-empty string") { - assert(validator.isNonEmpty("hello")) - } - - test("isNonEmpty returns false for an empty string") { - assert(!validator.isNonEmpty("")) - } - - test("isNonEmpty returns false for a null string") { - assert(!validator.isNonEmpty(null)) - } - -// test("isInRange returns true when value is within bounds") { -// assert(validator.isInRange(5, 1, 10)) -// } - -// test("isInRange returns true when value equals the lower bound") { -// assert(validator.isInRange(1, 1, 10)) -// } - -// test("isInRange returns true when value equals the upper bound") { -// assert(validator.isInRange(10, 1, 10)) -// } - -// test("isInRange returns false when value is below the lower bound") { -// assert(!validator.isInRange(0, 1, 10)) -// } - -// test("isInRange returns false when value is above the upper bound") { -// assert(!validator.isInRange(11, 1, 10)) -// } - - test("classify returns 'negative' for a negative integer") { - assert(validator.classify(-3) == "negative") - } - - test("classify returns 'zero' for zero") { - assert(validator.classify(0) == "zero") - } - - test("classify returns 'positive' for a positive integer") { - assert(validator.classify(7) == "positive") - } - -// test("firstNonEmpty returns the primary value when it is non-empty") { -// assert(validator.firstNonEmpty("first", "second") == Some("first")) -// } - -// test("firstNonEmpty returns the secondary value when primary is empty") { -// assert(validator.firstNonEmpty("", "second") == Some("second")) -// } - -// test("firstNonEmpty returns None when both values are empty") { -// assert(validator.firstNonEmpty("", "") == None) -// } - -// test("firstNonEmpty returns None when both values are null") { -// assert(validator.firstNonEmpty(null, null) == None) -// } -} From ffe42d18db4045928c12f9cf479f21a4567a62ac Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:05:49 +0200 Subject: [PATCH 06/10] feat: Add new issue templates for operational tasks, pointers, spikes, and technical debt - Created `operative_task.yml` for general operational and management tasks. - Introduced `pointer.yml` to track external work across repositories. - Added `spike_task.yml` for research and investigation tasks. - Implemented `technical_debt.yml` to document and address technical debt items. docs: Add Copilot instructions and review rules - Created `copilot-instructions.md` detailing coding guidelines, output discipline, and testing practices. - Added `copilot-review-rules.md` to define review behavior and commenting rules. chore: Configure Dependabot and GitHub workflows - Added `dependabot.yml` for automated dependency updates. - Created various workflows for issue assignment, build, release drafting, and publishing. - Implemented checks for release notes presence and JaCoCo code coverage. test: Add filename check workflow - Introduced `test_filenames_check.yml` to enforce naming conventions for test files. --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.yml | 82 +++++++++++ .github/ISSUE_TEMPLATE/devops_task.yml | 17 +++ .github/ISSUE_TEMPLATE/documentation_task.yml | 53 +++++++ .github/ISSUE_TEMPLATE/epic_task.yml | 47 ++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 65 +++++++++ .github/ISSUE_TEMPLATE/operative_task.yml | 55 +++++++ .github/ISSUE_TEMPLATE/pointer.yml | 71 ++++++++++ .github/ISSUE_TEMPLATE/spike_task.yml | 60 ++++++++ .github/ISSUE_TEMPLATE/technical_debt.yml | 96 +++++++++++++ .github/copilot-instructions.md | 125 ++++++++++++++++ .github/copilot-review-rules.md | 47 ++++++ .github/dependabot.yml | 16 +++ .github/pull_request_template.md | 11 ++ .github/workflows/assign_issue_to_project.yml | 30 ++++ .github/workflows/build.yml | 77 ++++++++++ .github/workflows/check_pr_release_notes.yml | 47 ++++++ .github/workflows/jacoco_report.yml | 131 +++++++++++++++++ .github/workflows/release_draft.yml | 134 ++++++++++++++++++ .github/workflows/release_publish.yml | 44 ++++++ .github/workflows/test_filenames_check.yml | 42 ++++++ 21 files changed, 1251 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/devops_task.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation_task.yml create mode 100644 .github/ISSUE_TEMPLATE/epic_task.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/operative_task.yml create mode 100644 .github/ISSUE_TEMPLATE/pointer.yml create mode 100644 .github/ISSUE_TEMPLATE/spike_task.yml create mode 100644 .github/ISSUE_TEMPLATE/technical_debt.yml create mode 100644 .github/copilot-instructions.md create mode 100644 .github/copilot-review-rules.md create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/assign_issue_to_project.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/check_pr_release_notes.yml create mode 100644 .github/workflows/jacoco_report.yml create mode 100644 .github/workflows/release_draft.yml create mode 100644 .github/workflows/release_publish.yml create mode 100644 .github/workflows/test_filenames_check.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9220c51 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @miroslavpojer @lsulak @salamonpavel @tmikula-dev @benedeki diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..07fc71a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,82 @@ +name: Bug Report +description: Create a report to help us improve +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Please supply complete reproduction details to speed up resolution. + + - type: textarea + id: bug-description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: Tell us what went wrong. + validations: + required: true + + - type: textarea + id: reproduction-steps + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior OR commands run + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. Enter value '...' + 4. See error + validations: + required: true + + - type: textarea + id: expected-state + attributes: + label: Expected state + description: A clear and concise description of what you expected to happen + placeholder: What should have happened? + validations: + required: true + + - type: dropdown + id: severity + attributes: + label: Impact / Severity + description: Level of impact + options: + - Blocker + - High + - Medium + - Low + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Attachments / Evidence + description: Logs, screenshots, traces. + placeholder: Paste relevant excerpts. + validations: + required: false + + - type: textarea + id: os-browser-version + attributes: + label: Desktop + description: What are your OS, browser, and version? + placeholder: | + OS: e.g., Windows 10, macOS 11.2, Ubuntu 20.04 + Browser: e.g., Chrome, Safari, Firefox + Version: e.g., 22 + validations: + required: false + + - type: textarea + id: related + attributes: + label: Related / References + description: Link other issues/PRs. + placeholder: "#456, #789" + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/devops_task.yml b/.github/ISSUE_TEMPLATE/devops_task.yml new file mode 100644 index 0000000..e7777bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/devops_task.yml @@ -0,0 +1,17 @@ +name: DevOps Task +description: Issue template for a task about setting up the project. Usually not touching the codebase. +labels: ["infrastructure"] +body: + - type: markdown + attributes: + value: | + A clear and concise description of the task to be done. + + - type: textarea + id: task-description + attributes: + label: The task + description: Short description of the task + placeholder: Describe what needs to be done + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/documentation_task.yml b/.github/ISSUE_TEMPLATE/documentation_task.yml new file mode 100644 index 0000000..4fc01c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_task.yml @@ -0,0 +1,53 @@ +name: Documentation +description: Update or create technical or operational documentation +labels: ["documentation"] +body: + - type: markdown + attributes: + value: | + Provide clear purpose and scope for the documentation update. + + - type: input + id: summary + attributes: + label: Summary + description: Short description of the documentation update. + placeholder: e.g. Add onboarding guide for new microservice + validations: + required: true + + - type: textarea + id: current-state + attributes: + label: Current State (If Updating) + description: Briefly describe existing content or gap. + placeholder: Existing runbook lacks failure recovery steps. + validations: + required: false + + - type: textarea + id: proposed-changes + attributes: + label: Proposed Changes + description: Key additions, removals, or restructures. + placeholder: Add section on X; remove deprecated Y. + validations: + required: true + + - type: textarea + id: dependencies + attributes: + label: Dependencies / References + description: Link related issues, PRs, external standards. + placeholder: "#123, ADR-7, compliance doc link." + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other information about the documentation update here. + placeholder: Add any other relevant context. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/epic_task.yml b/.github/ISSUE_TEMPLATE/epic_task.yml new file mode 100644 index 0000000..47c156f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/epic_task.yml @@ -0,0 +1,47 @@ +name: Epic +description: A larger task consisting of more deliverables. +labels: ["epic"] +body: + - type: markdown + attributes: + value: | + Use this template for large initiatives with multiple child issues. + + - type: textarea + id: background + attributes: + label: Background + description: High-level narrative and intended outcomes. + placeholder: Platform modernization initiative. + validations: + required: true + + - type: textarea + id: goals + attributes: + label: Goals + description: Bullet list of measurable goals. + placeholder: What do we want to accomplish? + validations: + required: true + + - type: textarea + id: subtasks + attributes: + label: Subtasks + description: List of planned child issues. + placeholder: | + - [ ] #123 Subtask 1 + - [ ] #223 Subtask 2 + - [ ] #323 Subtask 3 + validations: + required: false + + - type: textarea + id: references + attributes: + label: Related / References + description: Docs, ADRs, previous epics links. + placeholder: ADR-12 link, design doc. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..66331ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,65 @@ +name: Feature Request +description: Suggest an idea for this project. +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thank you for suggesting an improvement. Please provide clear, actionable detail. + + - type: textarea + id: feature-description + attributes: + label: Feature Description + description: A clear and concise description of what you want to happen. + placeholder: Describe the feature you'd like to see. + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Problem / Opportunity + description: What user/business problem does this solve? Who benefits? + placeholder: Current pain, limitation, or missed opportunity. + validations: + required: true + + - type: textarea + id: acceptance + attributes: + label: Acceptance Criteria + description: Clear, testable outcomes. Use bullet points. + placeholder: | + 1. User can... + 2. API returns... + 3. Performance within... + validations: + required: true + + - type: textarea + id: proposed-solution + attributes: + label: Proposed Solution + description: High-level approach. Mention alternatives if considered. + placeholder: Describe the direction without full implementation detail. + validations: + required: false + + - type: textarea + id: dependencies + attributes: + label: Dependencies / Related + description: Link related issues, epics, or external docs. + placeholder: "#123, #456, ADR link" + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other information or screenshots about the feature request here + placeholder: Add any other relevant context or screenshots + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/operative_task.yml b/.github/ISSUE_TEMPLATE/operative_task.yml new file mode 100644 index 0000000..14fd100 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/operative_task.yml @@ -0,0 +1,55 @@ +name: Operative / Management Task +description: General-purpose template for operational, management, and non-development tasks +labels: ["operative"] +body: + - type: markdown + attributes: + value: | + Use this flexible template for operational, management, planning, or administrative tasks. + + - type: input + id: summary + attributes: + label: Task Summary + description: Brief one-line description of the task + placeholder: e.g., Organize Q2 planning meeting, Update team guidelines. + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: Detailed description of what needs to be done and why + placeholder: Provide context, background, and objectives for this task + validations: + required: true + + - type: textarea + id: objectives + attributes: + label: Objectives / Goals + description: What are we trying to achieve? + placeholder: | + - Objective 1 + - Objective 2 + validations: + required: false + + - type: input + id: deadline + attributes: + label: Deadline / Target Date + description: When should this be completed? + placeholder: YYYY-MM-DD or "End of Sprint 5" + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other relevant information, links, or attachments + placeholder: Links to documents, previous discussions, related materials + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/pointer.yml b/.github/ISSUE_TEMPLATE/pointer.yml new file mode 100644 index 0000000..e879cdf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/pointer.yml @@ -0,0 +1,71 @@ +name: Pointer +description: Track external work referenced across repositories +labels: ["pointer"] +body: + - type: markdown + attributes: + value: | + Use pointer issues to track external work (other repos, third-party tasks) impacting this domain. + + - type: input + id: summary + attributes: + label: Summary + description: Short description of external work. + placeholder: e.g. Migration of auth service in repo X + validations: + required: true + + - type: textarea + id: references + attributes: + label: Source / External References + description: List external issues/PRs/links (one per line). + placeholder: | + repo-a#123 + repo-b#456 + Design doc link + validations: + required: true + + - type: dropdown + id: relationship + attributes: + label: Relationship Type + description: Nature of linkage + options: + - Dependency + - Blocking + - Follow-up + - Mirroring + - Audit + - Observability + validations: + required: true + + - type: textarea + id: reason + attributes: + label: Reason for Tracking + description: Why visibility matters (release, compliance, coordination). + placeholder: Release impact; compliance trace; dependency risk. + validations: + required: true + + - type: textarea + id: closure + attributes: + label: Closure Conditions + description: When can this pointer be closed? + placeholder: External PR merged + deployed + docs updated. + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other information about the pointer here. + placeholder: Add any other relevant context. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/spike_task.yml b/.github/ISSUE_TEMPLATE/spike_task.yml new file mode 100644 index 0000000..f5fd388 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/spike_task.yml @@ -0,0 +1,60 @@ +name: Spike +description: Issue template for spikes, research and investigation tasks. +labels: ["spike"] +body: + - type: markdown + attributes: + value: | + Use this form for research/investigation tasks. Keep scope tight. + + - type: textarea + id: background + attributes: + label: Background + description: Describe the problem or topic we need to understand. + placeholder: Tell us what you need to research or investigate. + validations: + required: true + + - type: textarea + id: questions + attributes: + label: Questions To Answer + description: List the key questions that need to be answered + placeholder: | + 1. Question one + 2. Question two + 3. Question three + validations: + required: true + + - type: textarea + id: desired-outcome + attributes: + label: Desired Outcome + description: List the desired outcomes of this spike ticket. + placeholder: What should be the result of this investigation? + validations: + required: true + + - type: checkboxes + id: tasks + attributes: + label: Tasks + options: + - label: Questions have been answered or we have a clearer idea of how to get to our goal + - label: Discussion with the team + - label: Documentation + - label: Create recommendations and new implementation tickets + + - type: textarea + id: additional-info + attributes: + label: Additional Info/Resources + description: Optional additional information or resources + placeholder: | + 1. Resource one + 2. Resource two + 3. Resource three + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/technical_debt.yml b/.github/ISSUE_TEMPLATE/technical_debt.yml new file mode 100644 index 0000000..a30adc7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/technical_debt.yml @@ -0,0 +1,96 @@ +name: Technical Debt +description: Track and address technical debt items to improve code quality, maintainability, or architecture. +labels: ["tech debt"] +body: + - type: markdown + attributes: + value: | + Use this template to document technical debt that should be addressed to improve long-term code health. + + - type: textarea + id: debt-description + attributes: + label: Description of Technical Debt + description: What is the current state that needs improvement? + placeholder: Describe the technical debt (outdated dependencies, code smells, architecture issues, etc.) + validations: + required: true + + - type: textarea + id: impact + attributes: + label: Impact of Technical Debt + description: What problems does this debt cause or risk causing? + placeholder: | + - Slows down development + - Increases bug risk + - Makes testing difficult + - Blocks future features + validations: + required: true + + - type: dropdown + id: category + attributes: + label: Category + description: Type of technical debt + options: + - Code Quality / Refactoring + - Architecture / Design + - Dependencies / Libraries + - Documentation + - Testing / Test Coverage + - Performance + - Security + - DevOps / Infrastructure + - Other + validations: + required: true + + - type: dropdown + id: priority + attributes: + label: Priority + description: How urgent is this to address? + options: + - High - Actively causing problems + - Medium - Should be addressed soon + - Low - Nice to have + validations: + required: true + + - type: textarea + id: proposed-solution + attributes: + label: Proposed Solution + description: How should this be addressed? Include alternatives if applicable. + placeholder: Describe the refactoring approach, migration plan, or improvement strategy. + validations: + required: false + + - type: input + id: effort-estimate + attributes: + label: Effort Estimate + description: Rough estimate of work required + placeholder: e.g., 1 day, 1 week, 2 sprints + validations: + required: false + + - type: textarea + id: dependencies + attributes: + label: Dependencies / Related + description: Link related issues, epics, or documentation. + placeholder: "#123, #456, ADR link" + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other relevant information, links to discussions, or examples. + placeholder: Add code snippets, links to similar work, or other references. + validations: + required: false diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..c10fd04 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,125 @@ +Copilot instructions + +Purpose +- Define consistent style; keep externally-visible behavior stable unless intentionally changing the contract. +- Sections order, bullet lists, and constraint words (Must / Must not / Prefer / Avoid) must be consistent across repos. +- Must keep one blank line at end of file. + +Context +- Scala library consumed via sbt in CI/CD; treat JDBC connections and DB state as boundary concerns. +- Must keep library API backward-compatible unless a version bump is intentional. + +Coding guidelines +- Must keep changes small and focused; prefer explicit over clever. +- Must keep pure logic free of environment access; route I/O through a single boundary module. +- Must keep externally-visible strings, formats, and exit codes stable unless intentional. + +Output discipline +- Aim ≤ 10 lines in final recap; link and summarize deltas rather than pasting large content. +- Prefer actionable bullets; avoid deep rationale unless requested. +- Code changes must end with: what changed / why / how to verify. + +PR Body Management +- Treat PR description as a changelog; append under `## Update [YYYY-MM-DD]` or `## Changes Added`. +- Must not rewrite the entire PR body; must reference the commit hash that introduced the change. + +Configuration +- Must read DB connection details from `database.properties` test resource; centralize in `ConnectionInfo`. +- Must not hard-code credentials; document required vs optional `ConnectionInfo` fields with defaults. + +Language and style +- Must target Scala 2.12 primary; cross-compile 2.11 and 2.13 where configured. +- Must add ScalaDoc for new public methods and classes. +- Must use slf4j (`LazyLogging` or explicit `Logger`), not `println`/`print`; pass logger arguments lazily (call-by-name), never pre-build interpolated strings. +- Must keep imports at top of file, grouped: stdlib → third-party → project-internal. +- Must include the Apache 2.0 copyright/license header in every source file using the project first-copyright year. +- Prefer `s"..."` interpolation for non-logging templates; avoid interpolation inside `logger.*` calls. + +Docstrings and comments +- ScalaDoc: short summary line first; prefer Parameters/Returns/Raises sections; avoid tutorials and long prose. +- Comments: comment intent/edge cases only; avoid restating the code. + +Patterns +- Prefer raising exceptions in leaf modules; translate to test/library failure at the entry point (`DBTestSuite.test()`). +- Must keep boundaries mockable; must not call real external systems (DB, APIs) in unit tests. + +Testing +- Must use ScalaTest under `balta/src/test/scala/`. +- Must test return values, exceptions, and error messages. +- Unit test files: name ends with `UnitTests`; must not require a DB connection; mock JDBC (`PreparedStatement`/`ResultSet`) if needed. +- Integration test files: name ends with `IntegrationTests`; must extend `DBTestSuite` or mix in `DBTestingConnection`. +- Must not access private members of the class under test. +- Prefer `AnyFunSuite`/`AnyFunSuiteLike`; use `AnyWordSpec` only when given/when/then adds clarity. +- Must place shared test helpers and fixtures in the root test package (`za.co.absa.db.balta`) and reuse them across tests; shared files are excluded from the filename-inspector check. +- Must not add top-level comments in `*Tests.scala` outside test methods; use nested `"..."` / `should` / `when` blocks (idiomatic to the chosen suite style) to separate groups. +- Prefer TDD workflow: + - Must create or update `SPEC.md` before writing any code, listing scenarios, inputs, and expected outputs. + - Must propose the full test case set (name + intent + input + expected output) and wait for user confirmation before coding. + - Must write all failing tests first (red), then implement until all pass (green). + - Must cover all distinct combinations; each test must state its scenario in the ScalaDoc. + - Must update `SPEC.md` after all tests pass with the confirmed test case table. +- When a unit test adds value — write one: + - Method has any own logic: branching (`if`/`match`), exception handling or swallowing, non-trivial transformation, config/resource read, or reflection. +- When to add to `jmf-rules.txt` instead of writing a unit test: + - Body is a single call with no own logic: forwards to another overload, calls its non-deprecated replacement, returns a field, or wraps a constructor with no transformation. + - Litmus: "Does this method have any logic of its own?" — No → JMF. +- Global rule collision check (CRITICAL): + - When adding any new method, check if it matches a pattern in the `# GLOBAL RULES` section of `jmf-rules.txt` (line ~22+). + - If method name matches a global rule AND the method has domain logic: immediately create an INCLUDE rescue rule (`+FQCN#method(*)`) in the `# INCLUDE RULES` section of `jmf-rules.txt`. + - High-risk method names: refer to `jmf-rules.txt` GLOBAL RULES section for complete list; most common collisions: `apply()`, `toString()`, `equals()`, `copy()`, `name()`, `groups()`, `optionalAttributes()`. + - Rationale: Broad global rules designed for compiler-generated boilerplate can silently hide 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 rescue from the `*$*#apply(*)` global rule in GLOBAL RULES section. +- Review rule — JMF drift check: when modifying a method that appears in `jmf-rules.txt`, verify its body still qualifies; if own logic has been added, remove the JMF rule and write a unit test instead. + +Tooling +- Must format with scalafmt (`.scalafmt.conf`); lint with scalastyle (`scalastyle-config.xml`) or wartremover as configured. +- Compiler warnings treated as errors where configured; coverage ≥ 80% via sbt-jacoco (excluding JMF-filtered methods in `jmf-rules.txt`). + +Coverage filtering (JMF) +- A method qualifies when its body is a single call with no own logic (see Testing section for the full decision rule and qualifying patterns). +- Must not add JMF rules for methods with branching logic, error handling, or non-trivial transformations. +- Rules file: `jmf-rules.txt`; one rule per line, `#` comments and blank lines ignored. +- Rule syntax: `#() [FLAGS] [PREDICATES]` + - FQCN_glob: dot-form class pattern (`*`, `*.model.*`, `com.example.*`; `$` for inner/companion classes). + - method_glob: glob on method name (`copy`, `get*`, `$anonfun$*`, `*_$eq`). + - descriptor_glob: JVM descriptor `(args)ret`; omitting or `(*)` means any args/any return. Must use JVM internal format: `(I)*` not `(int)`, `(Z)*` not `(boolean)`, `(Ljava/lang/String;)*` not `(java.lang.String)`. Non-matching descriptors are silently ignored — no warning is emitted. + - FLAGS (space/comma separated): `public`, `protected`, `private`, `synthetic`, `bridge`, `static`, `abstract`. + - PREDICATES: `ret:` (return type), `id:` (log label), `name-contains:`, `name-starts:`, `name-ends:`. +- Every rule Must include an `id:` label for traceability. +- Adoption order: start with CONSERVATIVE (case-class boilerplate, compiler synthetics), then STANDARD; use AGGRESSIVE only for DTO/auto-generated packages. +- Prefer narrow package scopes; prefer `synthetic`/`bridge` flags for compiler artifacts over broad wildcards. +- Must prefix project-specific FQCN globs with `*` so they match the full qualified class name (e.g. `*QueryResult#noMore()`, not `QueryResult#noMore()`). +- When adding a project-specific rule, add it under the `# PROJECT RULES` section with a comment explaining why the method qualifies. + +Quality gates +- sbt "testOnly *UnitTests" # unit tests, no DB needed +- sbt test # all tests, DB must be running +- sbt jacoco # coverage → balta/target/scala-*/jacoco/ +- sbt scalafmtCheck # format check +- sbt scalastyle # lint + +Common pitfalls to avoid +- Dependencies: verify compatibility across all cross-compiled Scala versions. +- Unsafe casts: avoid `.asInstanceOf`; document why when unavoidable. +- Logging: never pre-build log strings; always pass args lazily (call-by-name). +- Cleanup: remove unused imports/variables (scalac `-Ywarn-unused`). +- Stability: avoid changing externally-visible method signatures, SQL output, or parameter binding order. +- JMF silent failures: rules with non-matching FQCN or descriptor globs are silently ignored; always verify new rules take effect by comparing JaCoCo method-level output before and after. +- Scala reflection: case classes used with `currentMirror.reflectClass` in tests must be top-level (package scope), not inner classes — Scala reflection cannot handle inner class mirrors. + +Learned rules +- Must not change `QueryResultRow` getter return types or `stringPerConvention` output for existing scenarios. +- Must not change externally-visible SQL generation patterns without updating dependent tests. +- Scala 2.12 compiler artifacts that generate coverable bytecode: `$anonfun$` lambdas (ACC_SYNTHETIC), `$deserializeLambda$` (private static, not synthetic), Function1 mixin forwarders (`andThen`/`compose`), value class `$extension` methods, `$default$` parameter methods, Iterator trait mixin (~80+ forwarders). These are JMF candidates, not unit-test candidates. + +Repo additions +- Project name: balta +- Entry points: `build.sbt`, `balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala` +- Core package: `za.co.absa.db.balta`; supporting: `za.co.absa.db.mag` (naming conventions) +- DB boundary: `DBConnection` (JDBC wrapper), `DBTestSuite` (transaction rollback/commit) +- Contract-sensitive: SQL generation in `DBTable`/`DBFunction`; parameter binding order; `QueryResultRow` getter return types; `stringPerConvention` output +- Coverage tool: sbt-jacoco with JMF filter rules in `jmf-rules.txt` +- Commands: + - sbt "testOnly *UnitTests" # unit tests, no DB needed + - sbt test # all tests, DB must be running + - sbt jacoco # coverage report diff --git a/.github/copilot-review-rules.md b/.github/copilot-review-rules.md new file mode 100644 index 0000000..47fe590 --- /dev/null +++ b/.github/copilot-review-rules.md @@ -0,0 +1,47 @@ +# Copilot Review Rules + +Purpose +- Define consistent, concise, action-oriented review behavior. +- Use short headings and bullet lists; prefer do/avoid constraints over prose; point to code and impact. + +Mode: Default review +- Scope: single PR, normal risk. +- Priority order: correctness → security → tests → maintainability → style. +- Checks: + - Correctness: logic bugs, missing edge cases, regressions, contract changes. + - Security: unsafe input handling, secrets exposure, auth/authz issues, insecure defaults. + - Tests: tests exist for changed logic; success + failure paths covered. + - Maintainability: unnecessary complexity, duplication, unclear naming/structure. + - Style: flag only when readability or repo conventions are broken. +- Response format: short bullets; files + line ranges; severity groups (Blocker / Important / Nit); minimal actionable suggestions; no rewrites. + +Mode: Double-check review +- Scope: higher-risk PRs (security, infra/CI, wide refactors, data migrations, auth changes). +- Additional focus: + - Confirm previous review comments were addressed. + - Re-check: auth, permissions, secrets, persistence, external calls, concurrency. + - Hidden side effects: backward compatibility, failure modes, retries, idempotency. + - Safe defaults: least privilege, secure logging, safe error messages, missing-input behavior. +- Response format: comment only where risk/impact is non-trivial; no repeated style notes; call out explicit risk acceptance (what / why acceptable / mitigation). + +Commenting rules (all modes) +- Every comment must state: what the issue is · why it matters · minimal fix suggestion. +- Prefer linking to existing repo patterns over introducing new ones. +- If context is missing, ask a targeted question instead of assuming. + +Non-goals +- No refactors unrelated to the PR's intent. +- No formatting bikeshedding when the formatter/linter handles it. +- No architectural rewrites unless explicitly requested. + +Repo additions +- High-risk areas: + - DB transactions: `DBTestSuite` rollback/commit; do not change `persistData` behavior unintentionally. + - SQL injection: parameter binding uses `PreparedStatement`; no raw string concatenation of untrusted input. + - JDBC lifecycle: connections must be rolled back or committed; validate `try/finally` in `DBQuerySupport`. + - Contract-sensitive: `QueryResultRow` getter signatures; `stringPerConvention` output; `jmf-rules.txt`. + - Deprecations: do not extend `setParamNull`/`addNull` deprecated API surface. +- Required tests: + - `*UnitTests`: no DB connection; mock `PreparedStatement`/`ResultSet` if needed. + - `*IntegrationTests`: extend `DBTestSuite` or `DBTestingConnection`. + - New naming convention implementations must have a `UnitTests` covering all `LettersCase` variants. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5973091 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "master" + schedule: + interval: "weekly" + day: "sunday" + labels: + - "auto update" + - "infrastructure" + - "no RN" + open-pull-requests-limit: 3 + commit-message: + prefix: "chore" + include: "scope" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..323ac01 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ + +## Overview + + +## Release Notes +- TBD: 1st item of release notes +- TBD: 2nd item of release notes + + +## Related +Closes #issue_number diff --git a/.github/workflows/assign_issue_to_project.yml b/.github/workflows/assign_issue_to_project.yml new file mode 100644 index 0000000..6ae7144 --- /dev/null +++ b/.github/workflows/assign_issue_to_project.yml @@ -0,0 +1,30 @@ +# +# Copyright 2023 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Auto Assign Issue to Project + +on: + issues: + types: [opened, transferred] + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd + with: + project-url: https://github.com/orgs/AbsaOSS/projects/7 + github-token: ${{ secrets.PAT_REPO_PROJECT_DISCUSS }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ca3c49c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,77 @@ +# +# Copyright 2023 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Build + +on: + # pull_request: + # branches: + # - '**' + # types: [ opened, synchronize, reopened ] + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + scala: [2.11.12, 2.12.18, 2.13.13] + + name: Scala ${{matrix.scala}} + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: mag_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + + - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 + + - name: Setup Scala + uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c + with: + java-version: "adopt@1.8" + + - name: Build and run unit tests + run: sbt ++${{matrix.scala}} test doc + + - name: Setup database + run: | + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/02_users.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/03_schema_testing.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/04_testing.base_types.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/05_testing._base_types_data.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/06_testing.pg_types.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/07_testing_pg_types_data.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/08_testing.simple_function.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/09_testing.table_lifecycle.ddl + + - name: Build and run integration tests + run: sbt ++${{matrix.scala}} testIT diff --git a/.github/workflows/check_pr_release_notes.yml b/.github/workflows/check_pr_release_notes.yml new file mode 100644 index 0000000..08e45a0 --- /dev/null +++ b/.github/workflows/check_pr_release_notes.yml @@ -0,0 +1,47 @@ +# +# Copyright 2023 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Check PR Release Notes + +on: + # pull_request: + # types: [opened, synchronize, reopened, edited, labeled, unlabeled] + # branches: [ master ] + workflow_dispatch: + +env: + SKIP_LABEL: 'no RN' + RLS_NOTES_TAG_REGEX: 'Release Notes:' + +jobs: + check-release-notes: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + with: + python-version: '3.13' + + - name: Check presence of release notes in PR description + uses: AbsaOSS/release-notes-presence-check@8e586b26a5e27f899ee8590a5d988fd4780a3dbf + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + github-repository: ${{ github.repository }} + pr-number: ${{ github.event.number }} + title: "## [Rr]elease [Nn]otes" + skip-labels: 'no RN' + skip-placeholders: 'TBD' diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml new file mode 100644 index 0000000..8cd3e16 --- /dev/null +++ b/.github/workflows/jacoco_report.yml @@ -0,0 +1,131 @@ +name: CI check JaCoCo code-coverage + +on: + pull_request: + branches: [ master ] + +env: + scalaLong: 2.12.18 + scalaShort: "2.12" + coverage-overall: 80.0 + coverage-changed-files: 80.0 + coverage-per-changed-file: 0.0 + +jobs: + detect: + name: Scala Changes Detection + runs-on: ubuntu-latest + outputs: + scala_changed: ${{ steps.changes.outputs.scala_changed }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + persist-credentials: false + fetch-depth: 0 + + - name: Check if Scala files changed (excluding project/) + id: changes + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + CHANGED_FILES=$(gh api \ + --paginate \ + "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \ + --jq '.[].filename + | select(endswith(".scala")) + | select(startswith("project/") | not)') + else + CHANGED_FILES=$(git diff --name-only "${{ github.sha }}~1" "${{ github.sha }}" -- '**/*.scala' | grep -v '^project/' || true) + fi + + if [[ -n "${CHANGED_FILES}" ]]; then + echo "scala_changed=true" >> "$GITHUB_OUTPUT" + else + echo "scala_changed=false" >> "$GITHUB_OUTPUT" + fi + + build-test-and-measure: + name: Build, Test and Measure + needs: detect + if: needs.detect.outputs.scala_changed == 'true' + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: mag_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: Setup Scala + uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c + with: + java-version: "adopt@1.8" + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.14" + + - name: Setup database + run: | + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/02_users.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/03_schema_testing.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/04_testing.base_types.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/05_testing._base_types_data.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/06_testing.pg_types.ddl + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/07_testing_pg_types_data.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/08_testing.simple_function.sql + psql postgresql://postgres:postgres@localhost:5432/mag_db -f balta/src/test/resources/db/postgres/09_testing.table_lifecycle.ddl + + - name: Build and run tests + continue-on-error: true + id: jacocorun + run: sbt jacoco + + # - name: Check coverage thresholds and add reports in PR comments + # id: jacoco + # uses: MoranaApps/jacoco-report@master + # with: + # token: '${{ secrets.GITHUB_TOKEN }}' + # paths: | + # balta/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 + + - name: Check coverage thresholds and add reports in PR comments + id: jacoco + uses: MoranaApps/jacoco-report@master + with: + token: '${{ secrets.GITHUB_TOKEN }}' + paths: | + **/jacoco.xml + global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}' + fail-on-threshold: '' + + noop: + name: No Operation + needs: detect + if: needs.detect.outputs.scala_changed != 'true' + runs-on: ubuntu-latest + steps: + - run: echo "No changes in the *.scala files (excluding project/) — passing." diff --git a/.github/workflows/release_draft.yml b/.github/workflows/release_draft.yml new file mode 100644 index 0000000..fba0c7d --- /dev/null +++ b/.github/workflows/release_draft.yml @@ -0,0 +1,134 @@ +# +# Copyright 2021 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Draft Release +on: + workflow_dispatch: + inputs: + tag-name: + description: 'Name of git tag to be created, and then draft release created. Syntax: "v[0-9]+.[0-9]+.[0-9]+".' + required: true + from-tag-name: + description: 'Name of the git tag from which to detect changes from. Default value: latest tag. Syntax: "v[0-9]+.[0-9]+.[0-9]+".' + required: false + +jobs: + release-draft: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + with: + python-version: '3.1' + + - name: Check Format of Received Tag + id: check-version-tag + uses: AbsaOSS/version-tag-check@4145e48bf3f77a5afff2ec9afdd8afb6b53bce34 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + github-repository: ${{ github.repository }} + version-tag: ${{ github.event.inputs.tag-name }} + + - name: Check Format of Received From Tag + if: ${{ github.event.inputs.from-tag-name }} + id: check-version-from-tag + uses: AbsaOSS/version-tag-check@4145e48bf3f77a5afff2ec9afdd8afb6b53bce34 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + github-repository: ${{ github.repository }} + version-tag: ${{ github.event.inputs.from-tag-name }} + should-exist: true + + - name: Generate Release Notes + id: generate_release_notes + uses: AbsaOSS/generate-release-notes@B90223510d1704301a36a36f0d86a72a0e72f0cf + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag-name: ${{ github.event.inputs.tag-name }} + from-tag-name: ${{ github.event.inputs.from-tag-name }} + chapters: | + - {"title": "Entries to skip 🚫", "label": "duplicate"} + - {"title": "Entries to skip 🚫", "label": "invalid"} + - {"title": "Entries to skip 🚫", "label": "wontfix"} + - {"title": "Entries to skip 🚫", "label": "no RN"} + - {"title": "Breaking Changes 💥", "label": "breaking change"} + - {"title": "Security updates 👮", "label": "security"} + - {"title": "New Features 🎉", "label": "enhancement"} + - {"title": "Bugfixes 🛠", "label": "bug"} + - {"title": "Epics 📖", "label": "epic"} + - {"title": "Infrastructure ⚙️", "label": "infrastructure"} + - {"title": "Silent-live 🤫", "label": "silent live"} + - {"title": "Documentation 📜", "label": "documentation"} + duplicity-scope: 'none' + warnings: true + skip-release-notes-labels: "no RN,duplicate,invalid,wontfix" + print-empty-chapters: false + row-format-issue: '_{title}_ {developers} in {number}' + row-format-pr: '_{title}_ {developers} in {number}' + row-format-link-pr: true + hierarchy: true + + - name: Create and Push Tag (standalone) + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 + with: + script: | + const tag = core.getInput('tag-name') + const ref = `refs/tags/${tag}`; + const sha = context.sha; // The SHA of the commit to tag + const tagMessage = `${tag} released by GitHub Action`; + + const tagObject = await github.rest.git.createTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: tag, + message: tagMessage, + object: sha, + type: 'commit', + tagger: { + name: context.actor, + email: `${context.actor}@users.noreply.github.com`, + date: new Date().toISOString() + } + }); + + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: ref, + sha: tagObject.data.sha + }); + + console.log(`Tag created: ${tag}`); + github-token: ${{ secrets.GITHUB_TOKEN }} + tag-name: ${{ github.event.inputs.tag-name }} + + - name: Create draft release + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: ${{ github.event.inputs.tag-name }} + body: ${{ steps.generate_release_notes.outputs.release-notes }} + tag_name: ${{ github.event.inputs.tag-name }} + draft: true + prerelease: false diff --git a/.github/workflows/release_publish.yml b/.github/workflows/release_publish.yml new file mode 100644 index 0000000..f11b648 --- /dev/null +++ b/.github/workflows/release_publish.yml @@ -0,0 +1,44 @@ +# +# Copyright 2023 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Release - publish artifacts +on: + release: + types: [released] + +jobs: + publish-to-sonatype: + name: Publish to Sonatype + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + fetch-depth: 0 + - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 + + - name: Setup Scala + uses: olafurpg/setup-scala@32ffa16635ff8f19cc21ea253a987f0fdf29844c + with: + java-version: "adopt@1.8" + + - name: Run sbt ci-release + run: sbt ci-release + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/.github/workflows/test_filenames_check.yml b/.github/workflows/test_filenames_check.yml new file mode 100644 index 0000000..f17ea0c --- /dev/null +++ b/.github/workflows/test_filenames_check.yml @@ -0,0 +1,42 @@ +# +# Copyright 2023 ABSA Group Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Test Filenames Check + +on: + # pull_request: + # branches: [ master ] + # types: [ opened, synchronize, reopened ] + workflow_dispatch: + +jobs: + test_filenames_check: + name: Test Filenames Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + + - name: Filename Inspector + id: scan-test-files + uses: AbsaOSS/filename-inspector@355108975e656fac9faaa04209b6df3f9997c8fa + with: + name-patterns: '*UnitTests.*,*IntegrationTests.*' + paths: '**/src/test/scala/**' + report-format: 'console' + excludes: 'balta/src/test/scala/za/co/absa/db/balta/testing/**' + verbose-logging: 'false' + fail-on-violation: 'true' From 4ffb2cf7b437c4fcc903e958bbbbc0ac1aa4c555 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:06:04 +0200 Subject: [PATCH 07/10] chore: Update JaCoCo report action configuration for improved coverage reporting --- .github/workflows/jacoco_report.yml | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index 8cd3e16..6bd6ae9 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -98,29 +98,19 @@ jobs: id: jacocorun run: sbt jacoco - # - name: Check coverage thresholds and add reports in PR comments - # id: jacoco - # uses: MoranaApps/jacoco-report@master - # with: - # token: '${{ secrets.GITHUB_TOKEN }}' - # paths: | - # balta/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 - - name: Check coverage thresholds and add reports in PR comments id: jacoco uses: MoranaApps/jacoco-report@master with: token: '${{ secrets.GITHUB_TOKEN }}' paths: | - **/jacoco.xml - global-thresholds: '${{ env.coverage-overall }}*${{ env.coverage-changed-files }}*${{ env.coverage-per-changed-file }}' - fail-on-threshold: '' + balta/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 noop: name: No Operation From b905b93357d312495936b5e4a7eb06526c7d61d0 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:09:12 +0200 Subject: [PATCH 08/10] chore: Update workflow triggers for pull requests in multiple YAML files --- .github/workflows/build.yml | 9 ++++----- .github/workflows/check_pr_release_notes.yml | 7 +++---- .github/workflows/jacoco_report.yml | 4 ++-- .github/workflows/release_draft.yml | 2 +- .github/workflows/test_filenames_check.yml | 7 +++---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca3c49c..f25b4d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,11 +17,10 @@ name: Build on: - # pull_request: - # branches: - # - '**' - # types: [ opened, synchronize, reopened ] - workflow_dispatch: + pull_request: + branches: + - '**' + types: [ opened, synchronize, reopened ] jobs: test: diff --git a/.github/workflows/check_pr_release_notes.yml b/.github/workflows/check_pr_release_notes.yml index 08e45a0..7e3a35d 100644 --- a/.github/workflows/check_pr_release_notes.yml +++ b/.github/workflows/check_pr_release_notes.yml @@ -17,10 +17,9 @@ name: Check PR Release Notes on: - # pull_request: - # types: [opened, synchronize, reopened, edited, labeled, unlabeled] - # branches: [ master ] - workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened, edited, labeled, unlabeled] + branches: [ master ] env: SKIP_LABEL: 'no RN' diff --git a/.github/workflows/jacoco_report.yml b/.github/workflows/jacoco_report.yml index 6bd6ae9..2a1a2d5 100644 --- a/.github/workflows/jacoco_report.yml +++ b/.github/workflows/jacoco_report.yml @@ -78,7 +78,7 @@ jobs: java-version: "adopt@1.8" - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: "3.14" @@ -100,7 +100,7 @@ jobs: - name: Check coverage thresholds and add reports in PR comments id: jacoco - uses: MoranaApps/jacoco-report@master + uses: MoranaApps/jacoco-report@52a5f9c278aaf61700c9975e61a0853875c9d156 with: token: '${{ secrets.GITHUB_TOKEN }}' paths: | diff --git a/.github/workflows/release_draft.yml b/.github/workflows/release_draft.yml index fba0c7d..6820997 100644 --- a/.github/workflows/release_draft.yml +++ b/.github/workflows/release_draft.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: - python-version: '3.1' + python-version: '3.13' - name: Check Format of Received Tag id: check-version-tag diff --git a/.github/workflows/test_filenames_check.yml b/.github/workflows/test_filenames_check.yml index f17ea0c..44d296c 100644 --- a/.github/workflows/test_filenames_check.yml +++ b/.github/workflows/test_filenames_check.yml @@ -17,10 +17,9 @@ name: Test Filenames Check on: - # pull_request: - # branches: [ master ] - # types: [ opened, synchronize, reopened ] - workflow_dispatch: + pull_request: + branches: [ master ] + types: [ opened, synchronize, reopened ] jobs: test_filenames_check: From 0a7a437b7926e24b80a42321104cfe28ad35c16a Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:18:52 +0200 Subject: [PATCH 09/10] Last experiment. --- balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala b/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala index 63317b5..5d308be 100644 --- a/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala +++ b/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala @@ -156,6 +156,7 @@ abstract class DBTestSuite(val persistDataOverride: Option[Boolean] = None) exte Params.addNull() } + // private functions private def readConnectionInfoFromConfig: ConnectionInfo = { DBTestSuite.connectionInfoFromResourceConfig("/database.properties") From aaf9c1bed2533774e76723210725115909998a60 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Mon, 18 May 2026 09:22:22 +0200 Subject: [PATCH 10/10] Remove last test change. --- balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala b/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala index 5d308be..63317b5 100644 --- a/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala +++ b/balta/src/main/scala/za/co/absa/db/balta/DBTestSuite.scala @@ -156,7 +156,6 @@ abstract class DBTestSuite(val persistDataOverride: Option[Boolean] = None) exte Params.addNull() } - // private functions private def readConnectionInfoFromConfig: ConnectionInfo = { DBTestSuite.connectionInfoFromResourceConfig("/database.properties")