From 5c0027cfab6f4c6679839a6ce23fb5d9ad846d23 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Sun, 6 Sep 2026 13:08:49 +0200 Subject: [PATCH 1/9] replace Checkstyle with ErrorProne --- .githooks/pre-commit | 2 +- .github/workflows/release.yml | 3 - .github/workflows/test.yml | 3 - .gitignore | 2 - .idea/checkstyle-idea.xml | 25 ---- README.md | 15 --- build.gradle.kts | 49 +------- errorprone.args | 117 ++++++++++++++++++ gradle/libs.versions.toml | 19 +-- lombok.config | 3 + .../postgresql/crd/grant/GrantReconciler.java | 2 +- .../postgresql/crd/grant/GrantService.java | 3 +- .../crd/grant/GrantReconcilerTest.java | 2 - .../crd/role/RoleReconcilerTest.java | 2 +- .../aboutbits/postgresql/helm/HelmTest.java | 1 - settings.gradle.kts | 27 ---- 16 files changed, 128 insertions(+), 147 deletions(-) delete mode 100644 .idea/checkstyle-idea.xml create mode 100644 errorprone.args diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 369f90c..fbdd2b6 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -3,4 +3,4 @@ set -e set -o pipefail -./gradlew --console=colored checkstyleMain checkstyleTest +./gradlew --console=colored compileJava compileTestJava diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a577b4..cb3258e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,9 +44,6 @@ jobs: shell: bash - name: Build package run: ./gradlew --console=colored build -x test - env: - GITHUB_USER_NAME: ${{ github.actor }} - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: aboutbits/github-actions-docker/build-push@v1 with: username: ${{ github.actor }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96a3be2..4997018 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,3 @@ jobs: :operator:test --fail-fast -Dquarkus.test.profile=test-pg${{ matrix.postgres-version }} - env: - GITHUB_USER_NAME: ${{ github.actor }} - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f3e9c81..ade063c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ ### PostgreSQL Operator ### -config/ # Written by the fabric8 Kubernetes client when the tests run against the Dev Service operator/.kube/ @@ -17,7 +16,6 @@ operator/.kube/ .idea/* !.idea/codeStyles !.idea/.gitignore -!.idea/checkstyle-idea.xml !.idea/encodings.xml !.idea/misc.xml !.idea/sqldialects.xml diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml deleted file mode 100644 index c6f79d7..0000000 --- a/.idea/checkstyle-idea.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - 13.4.2 - JavaOnly - - - - \ No newline at end of file diff --git a/README.md b/README.md index 1dbda91..629202b 100644 --- a/README.md +++ b/README.md @@ -222,21 +222,6 @@ To build the project, the following prerequisites must be met: ### Setup -To get started, you first need to configure the GitHub Gradle Packages registry to be able to pull the [AbouBits Java Checkstyle Config](https://github.com/aboutbits/java-checkstyle-config) from the GitHub Packages registry. - -Follow https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry -The guide basically tells you to click on `Generate new token (classic)` on https://github.com/settings/tokens, add the permission `read:packages` and copy the token which we need below. - -If it does not exist yet, create a file `~/.gradle/gradle.properties` in your home directory and add the following lines. - -```properties -gpr.user= -# The token generated above -gpr.key= -``` - -Then call: - ```bash make init diff --git a/build.gradle.kts b/build.gradle.kts index 85ff274..3d80ead 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,3 @@ -import net.ltgt.gradle.errorprone.CheckSeverity import net.ltgt.gradle.errorprone.errorprone import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent @@ -6,7 +5,6 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { idea java - checkstyle id("io.quarkus").apply(false) alias(libs.plugins.axionReleasePlugin) alias(libs.plugins.errorPronePlugin) @@ -31,20 +29,10 @@ version = scmVersion.version allprojects { group = "it.aboutbits.postgresql" version = rootProject.version - - tasks.withType().configureEach { - dependsOn(":checkstyleExtractConfig") - - reports { - html.required = false - xml.required = false - } - } } subprojects { apply(plugin = "java") - apply(plugin = "checkstyle") apply(plugin = rootProject.libs.plugins.errorPronePlugin.get().pluginId) java { @@ -82,10 +70,8 @@ subprojects { options.compilerArgs.add("-parameters") options.errorprone { - check("NullAway", CheckSeverity.ERROR) - check("RequireExplicitNullMarking", CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "it.aboutbits.postgresql") - option("NullAway:JSpecifyMode", "true") + // The checks live in errorprone.args, see https://github.com/tbroyer/gradle-errorprone-plugin#argument-files + argumentFiles.from(rootProject.layout.projectDirectory.file("errorprone.args")) } } @@ -126,34 +112,3 @@ subprojects { } } } - -val checkstyleConfig: Configuration by configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true -} - -dependencies { - /** - * AboutBits Libraries - */ - checkstyleConfig(libs.checkstyleConfig) -} - -tasks.register("checkstyleExtractConfig") { - description = "Extracts the AboutBits Checkstyle configuration from the classpath." - group = JavaBasePlugin.CHECK_TASK_NAME - - from(zipTree(checkstyleConfig.singleFile)) { - include("checkstyle.xml", "checkstyle-suppressions.xml") - } - into(layout.projectDirectory.dir("config/checkstyle/")) -} - -checkstyle { - toolVersion = libs.versions.checkstyle.get() - isShowViolations = true - configFile = rootProject.file("config/checkstyle/checkstyle.xml") - configProperties = mapOf( - "suppressionFile" to rootProject.file("config/checkstyle/checkstyle-suppressions.xml") - ) -} diff --git a/errorprone.args b/errorprone.args new file mode 100644 index 0000000..00cf495 --- /dev/null +++ b/errorprone.args @@ -0,0 +1,117 @@ +# Error Prone and NullAway configuration. +# +# build.gradle.kts reads this file through options.errorprone.argumentFiles. +# +# Rules for this file: +# 1. Write one flag per line. +# 2. Write a check as the Xep prefix, then the check name, then the severity. +# A severity is ERROR, WARN or OFF. Omit the severity to keep the default. +# 3. Write a check option as the XepOpt prefix, then the key, then the value. +# 4. Never write an "at" sign in a comment. Error Prone reads it as a file reference. +# 5. Never start a comment word with a dash. Error Prone reads it as a flag. +# 6. This file cannot reference a second argument file. + +# Nullness +-Xep:NullAway:ERROR +-XepOpt:NullAway:AnnotatedPackages=it.aboutbits.postgresql +-XepOpt:NullAway:JSpecifyMode=true +-Xep:AnnotationPosition:ERROR +-Xep:EqualsMissingNullable:ERROR +-Xep:MultipleNullnessAnnotations:ERROR +-Xep:RequireExplicitNullMarking:ERROR + +# Javadoc +-Xep:AlmostJavadoc:ERROR +-Xep:DepAnn:ERROR +-Xep:EmptyBlockTag:ERROR +-Xep:EscapedEntity:ERROR +-Xep:InvalidBlockTag:ERROR +-Xep:InvalidInlineTag:ERROR +-Xep:InvalidLink:ERROR +-Xep:InvalidParam:ERROR +-Xep:InvalidThrows:ERROR +-Xep:MalformedInlineTag:ERROR +-Xep:MissingSummary:ERROR +-Xep:NotJavadoc:ERROR +-Xep:PreferThrowsTag:ERROR +-Xep:UnescapedEntity:ERROR +-Xep:UnrecognisedJavadocTag:ERROR + +# Imports +-Xep:BadImport:ERROR +-Xep:RemoveUnusedImports:ERROR +-Xep:UnnecessaryStaticImport:ERROR +-Xep:WildcardImport:ERROR + +# Naming and source layout +-Xep:ClassName:ERROR +-Xep:ConstantField:ERROR +-Xep:DefaultPackage:ERROR +-Xep:MultipleTopLevelClasses:ERROR +-Xep:PackageLocation:ERROR + +# Blocks, statements and switches +-Xep:EmptyCatch:ERROR +-Xep:EmptyIf:ERROR +-Xep:EmptyTopLevelDeclaration:ERROR +-Xep:MissingBraces:ERROR +-Xep:MissingCasesInEnumSwitch:ERROR +-Xep:MissingDefault:ERROR +-Xep:MultiVariableDeclaration:ERROR +-Xep:SwitchDefault:ERROR +-Xep:UseEnumSwitch:ERROR + +# Class design +-Xep:InterfaceWithOnlyStatics:ERROR +-Xep:MutablePublicArray:ERROR +-Xep:PrivateConstructorForUtilityClass:ERROR +-Xep:ProtectedMembersInFinalClass:ERROR +-Xep:TooManyParameters:ERROR + +# Correctness +-Xep:AmbiguousMethodReference:ERROR +-Xep:AssignmentExpression:ERROR +-Xep:BadInstanceof:ERROR +-Xep:CheckReturnValue:ERROR +-Xep:EqualsIncompatibleType:ERROR +-Xep:FallThrough:ERROR +-Xep:Finally:ERROR +-Xep:FunctionalInterfaceClash:ERROR +-Xep:HidingField:ERROR +# Important due to performance reasons, see +# https://github.com/uber/NullAway/wiki/Configuration#perform-exhaustive-override-checks +-Xep:MissingOverride:ERROR +-Xep:MockNotUsedInProduction:ERROR +-Xep:NarrowingCompoundAssignment:ERROR +-Xep:Overrides:ERROR +-Xep:PrimitiveArrayPassedToVarargsMethod:ERROR +-Xep:ReferenceEquality:ERROR +-Xep:SelfComparison:ERROR +-Xep:StaticQualifiedUsingExpression:ERROR +-Xep:SymbolToString:ERROR +-Xep:TypeToString:ERROR +-Xep:UnusedException:ERROR +-Xep:UnusedMethod:ERROR +-Xep:UnusedNestedClass:ERROR +-Xep:UnusedVariable:ERROR + +# Redundant code and house style +-Xep:BooleanLiteral:ERROR +-Xep:ComplexBooleanConstant:ERROR +-Xep:ExplicitArrayForVarargs:ERROR +-Xep:LongLiteralLowerCaseSuffix:ERROR +-Xep:MixedArrayDimensions:ERROR +-Xep:StringConcatToTextBlock:ERROR +-Xep:TryWithResourcesVariable:ERROR +-Xep:UnnecessaryAnonymousClass:ERROR +-Xep:UnnecessaryAssignment:ERROR +-Xep:UnnecessaryBoxedAssignment:ERROR +-Xep:UnnecessaryBoxedVariable:ERROR +-Xep:UnnecessaryFinal:ERROR +-Xep:UnnecessaryLambda:ERROR +-Xep:UnnecessaryMethodReference:ERROR +-Xep:UnnecessaryParentheses:ERROR +-Xep:Varifier:ERROR + +# Turn off as this check is too noisy +-Xep:StringSplitter:OFF diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1228662..506b34a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,4 @@ [versions] -## AboutBits Libraries ## -checkstyleConfig = "2.0.0-RC2" - # Axion Release Plugin # axionReleasePlugin = "1.21.1" @@ -17,8 +14,8 @@ scram-client = "3.2" assertj = "3.27.7" checkstyle = "13.4.2" datafaker = "2.5.4" -errorProne = "2.49.0" -errorPronePlugin = "5.1.0" +errorProne = "2.50.0" +errorPronePlugin = "5.1.1" nullAway = "0.13.4" [plugins] @@ -36,13 +33,6 @@ errorPronePlugin = { id = "net.ltgt.errorprone", version.ref = "errorPronePlugin jooqPlugin = { id = "org.jooq.jooq-codegen-gradle", version.ref = "jooq" } [libraries] -## AboutBits Libraries ## - -# https://github.com/aboutbits/java-checkstyle-config -checkstyleConfig = { group = "it.aboutbits", name = "java-checkstyle-config", version.ref = "checkstyleConfig" } - -## Libraries ## - # jOOQ # # https://www.jooq.org/ # https://www.jooq.org/download/versions @@ -87,11 +77,6 @@ scram-client = { group = "com.ongres.scram", name = "scram-client", version.ref # https://mvnrepository.com/artifact/org.assertj/assertj-core assertj = { group = "org.assertj", name = "assertj-core", version.ref = "assertj" } -# https://checkstyle.org/ -# https://github.com/checkstyle/checkstyle -# https://mvnrepository.com/artifact/com.puppycrawl.tools/checkstyle -checkstyle = { group = "com.puppycrawl.tools", name = "checkstyle", version.ref = "checkstyle" } - # https://datafaker.net/ # https://github.com/datafaker-net/datafaker # https://mvnrepository.com/artifact/net.datafaker/datafaker diff --git a/lombok.config b/lombok.config index a8fed54..fc8f4e3 100644 --- a/lombok.config +++ b/lombok.config @@ -7,3 +7,6 @@ lombok.addNullAnnotations = jspecify # Required for NullAway lombok.addLombokGeneratedAnnotation = true + +# Enable CheckReturnValue annotation on generated code for Error Prone and static analysis +lombok.checkReturnValueAnnotation = lombok diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantReconciler.java b/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantReconciler.java index ead867b..1aba2a2 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantReconciler.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantReconciler.java @@ -214,7 +214,7 @@ protected CRStatus newStatus() { return new CRStatus(); } - @SuppressWarnings({"checkstyle:MethodLength", "java:S3776"}) + @SuppressWarnings("java:S3776") private UpdateControl reconcileInTransaction( DSLContext tx, Grant resource, diff --git a/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantService.java b/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantService.java index c411175..4255943 100644 --- a/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantService.java +++ b/operator/src/main/java/it/aboutbits/postgresql/crd/grant/GrantService.java @@ -45,7 +45,6 @@ public class GrantService { /// @param tx The DSLContext for database operations. /// @param spec The GrantSpec containing the specification details. /// @return A map with object names as keys and lists of Privilege as values. - @SuppressWarnings("checkstyle:MethodLength") public Map> determineCurrentObjectPrivileges( DSLContext tx, GrantSpec spec @@ -216,7 +215,7 @@ public Map> determineCurrentObjectPrivileges( /// @param spec the GrantSpec object containing specifications about the target database objects and privileges /// @return a map where the keys represent object names and the values indicate ownership status, /// or `null` if the object does not exist - @SuppressWarnings({"checkstyle:MethodLength", "java:S3776"}) + @SuppressWarnings("java:S3776") public Map determineObjectExistenceAndOwnership( DSLContext tx, GrantSpec spec diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java index f37df7f..80bcfe7 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java @@ -737,7 +737,6 @@ void grantOnTable( ); } - @SuppressWarnings("checkstyle:MethodLength") @ParameterizedTest @MethodSource("provideAllSupportedPrivileges") @DisplayName("Should grant and revoke privileges on all tables") @@ -1058,7 +1057,6 @@ void grantOnSequence() { ); } - @SuppressWarnings("checkstyle:MethodLength") @Test @DisplayName("Should grant and revoke privileges on all sequences") void grantOnAllSequences() { diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java index 89551fc..26bee55 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/role/RoleReconcilerTest.java @@ -868,7 +868,7 @@ void deleteRole_removesFromDatabase() { .until(() -> !roleService.roleExists(dsl, role.getSpec())); } - private @Nullable T getRoleFlagValue( + private @Nullable T getRoleFlagValue( DSLContext dsl, String roleName, Field field diff --git a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java index 6e284a2..dac3efb 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java @@ -71,7 +71,6 @@ class HelmTest { this.rootValuesAlias = rootValuesAlias; } - @SuppressWarnings("checkstyle:MethodLength") @Test @DisplayName("When the Helm chart is installed, the operator deployment should be created") void helmInstall_createsDeployment() throws IOException { diff --git a/settings.gradle.kts b/settings.gradle.kts index 05bf6d6..7670f4e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,33 +23,6 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - val githubUser = providers.gradleProperty("gpr.user") - .orElse(providers.environmentVariable("GITHUB_USER_NAME")) - val githubToken = providers.gradleProperty("gpr.key") - .orElse(providers.environmentVariable("GITHUB_ACCESS_TOKEN")) - - fun addGitHubRepo(name: String): MavenArtifactRepository { - return maven { - this.name = name - url = uri("https://maven.pkg.github.com/aboutbits/$name") - credentials { - username = githubUser.orNull - password = githubToken.orNull - } - } - } - - // https://docs.gradle.org/current/userguide/best_practices_dependencies.html#use_content_filtering - exclusiveContent { - forRepositories( - addGitHubRepo("java-checkstyle-config"), - mavenLocal() - ) - filter { - includeGroupAndSubgroups("it.aboutbits") - } - } - mavenCentral() mavenLocal() } From 1672b025f1dc276ebd3ff3c2d92a68718a45bde9 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Sun, 6 Sep 2026 19:47:02 +0200 Subject: [PATCH 2/9] enable javac -Xlint warning flags --- build.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 3d80ead..e4ea3b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -69,6 +69,11 @@ subprojects { options.encoding = "UTF-8" options.compilerArgs.add("-parameters") + // Source code: A category that an Error Prone check already owns is deliberately absent + options.compilerArgs.add("-Xlint:deprecation,removal,unchecked,cast,rawtypes,divzero,this-escape,identity,text-blocks,dangling-doc-comments,restricted") + // The build itself: command-line options, path entries, output file collisions + options.compilerArgs.add("-Xlint:options,path,output-file-clash") + options.errorprone { // The checks live in errorprone.args, see https://github.com/tbroyer/gradle-errorprone-plugin#argument-files argumentFiles.from(rootProject.layout.projectDirectory.file("errorprone.args")) From 717e5c918a171af7397cbe6af94be943fa6c45fd Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Sun, 6 Sep 2026 20:19:48 +0200 Subject: [PATCH 3/9] fix deprecation warnings --- .../crd/grant/GrantReconcilerTest.java | 2 +- .../aboutbits/postgresql/helm/HelmTest.java | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java index 80bcfe7..36b017f 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/grant/GrantReconcilerTest.java @@ -1294,7 +1294,7 @@ private void createTableWithSerial( ) { try (var dsl = postgreSQLContextFactory.getDSLContext(clusterConnection, databaseName)) { dsl.createTable(quotedName(schemaName, tableName)) - .column("id", SQLDataType.BIGINT.identity(true)) + .column("id", SQLDataType.BIGINT.generatedByDefaultAsIdentity()) .execute(); } } diff --git a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java index dac3efb..c53dd23 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java @@ -1,11 +1,13 @@ package it.aboutbits.postgresql.helm; +import com.fasterxml.jackson.databind.JsonNode; import io.fabric8.kubernetes.api.model.ConfigBuilder; import io.fabric8.kubernetes.api.model.LocalObjectReference; import io.fabric8.kubernetes.api.model.Volume; import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Serialization; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.common.process.ProcessBuilder; @@ -35,6 +37,7 @@ @NullMarked class HelmTest { private static final String ENV_VAR_KUBECONFIG = "KUBECONFIG"; + private static final KubernetesSerialization KUBERNETES_SERIALIZATION = new KubernetesSerialization(); /// The Pod and Container list fields that the chart exposes as free-form Helm values. private static final List LIST_VALUES = List.of( @@ -84,21 +87,19 @@ void helmInstall_createsDeployment() throws IOException { // 1. Verify files exist and contain expected data // ./Chart.yaml @SuppressWarnings("unchecked") - Map chartMetadata = Serialization.yamlMapper() - .readValue( - chartPath.resolve("Chart.yaml").toFile(), - Map.class - ); + Map chartMetadata = KUBERNETES_SERIALIZATION.unmarshal( + Files.newInputStream(chartPath.resolve("Chart.yaml")), + Map.class + ); assertThat(chartMetadata.get("name")).isEqualTo(chartName); // ./values.yaml @SuppressWarnings("unchecked") - Map values = Serialization.yamlMapper() - .readValue( - chartPath.resolve("values.yaml").toFile(), - Map.class - ); + Map values = KUBERNETES_SERIALIZATION.unmarshal( + Files.newInputStream(chartPath.resolve("values.yaml")), + Map.class + ); assertThat(values).containsKey(rootValuesAlias); @@ -123,8 +124,10 @@ void helmInstall_createsDeployment() throws IOException { // ./values.schema.json // The type must be declared for every list value, otherwise the generated schema // falls back to `string` and `helm install` rejects a list. - var valuesSchema = Serialization.jsonMapper() - .readTree(chartPath.resolve("values.schema.json").toFile()); + var valuesSchema = KUBERNETES_SERIALIZATION.unmarshal( + Files.newInputStream(chartPath.resolve("values.schema.json")), + JsonNode.class + ); for (var listValue : LIST_VALUES) { var schemaProperty = valuesSchema.at("/properties/%s/properties/%s".formatted( From e13c50b27ba1f5ef7b23cf343d14609314aee91c Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Sun, 6 Sep 2026 20:19:53 +0200 Subject: [PATCH 4/9] fix unchecked warnings --- .../ClusterConnectionReconcilerErrorTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java index e45799a..8b91783 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java @@ -52,8 +52,9 @@ void setUp() { resource.setSpec(spec); resource.setMetadata(metadata); - //noinspection unchecked - context = mock(Context.class); + @SuppressWarnings("unchecked") + Context mockedContext = mock(Context.class); + context = mockedContext; } @Test From e0a353f223d81a4a6ee1c70a6cef63361f290650 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 11 Sep 2026 13:44:51 +0200 Subject: [PATCH 5/9] drop the unchecked suppressions that the warning fixes left behind `HelmTest` uses the static, non-deprecated `Serialization.unmarshal` with a Jackson `TypeReference`, so the `KubernetesSerialization` constant and two `@SuppressWarnings("unchecked")` go away. `ClusterConnectionReconcilerErrorTest` uses the reified `mock()` of Mockito 5, which needs no suppression and no temporary variable. --- .../ClusterConnectionReconcilerErrorTest.java | 4 +--- .../it/aboutbits/postgresql/helm/HelmTest.java | 17 ++++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java index 8b91783..e2a8ba8 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/crd/clusterconnection/ClusterConnectionReconcilerErrorTest.java @@ -52,9 +52,7 @@ void setUp() { resource.setSpec(spec); resource.setMetadata(metadata); - @SuppressWarnings("unchecked") - Context mockedContext = mock(Context.class); - context = mockedContext; + context = mock(); } @Test diff --git a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java index c53dd23..740a0eb 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java @@ -1,5 +1,6 @@ package it.aboutbits.postgresql.helm; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import io.fabric8.kubernetes.api.model.ConfigBuilder; import io.fabric8.kubernetes.api.model.LocalObjectReference; @@ -7,7 +8,6 @@ import io.fabric8.kubernetes.api.model.VolumeMount; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Serialization; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.common.process.ProcessBuilder; @@ -37,7 +37,6 @@ @NullMarked class HelmTest { private static final String ENV_VAR_KUBECONFIG = "KUBECONFIG"; - private static final KubernetesSerialization KUBERNETES_SERIALIZATION = new KubernetesSerialization(); /// The Pod and Container list fields that the chart exposes as free-form Helm values. private static final List LIST_VALUES = List.of( @@ -86,19 +85,19 @@ void helmInstall_createsDeployment() throws IOException { // 1. Verify files exist and contain expected data // ./Chart.yaml - @SuppressWarnings("unchecked") - Map chartMetadata = KUBERNETES_SERIALIZATION.unmarshal( + Map chartMetadata = Serialization.unmarshal( Files.newInputStream(chartPath.resolve("Chart.yaml")), - Map.class + new TypeReference>() { + } ); assertThat(chartMetadata.get("name")).isEqualTo(chartName); // ./values.yaml - @SuppressWarnings("unchecked") - Map values = KUBERNETES_SERIALIZATION.unmarshal( + Map values = Serialization.unmarshal( Files.newInputStream(chartPath.resolve("values.yaml")), - Map.class + new TypeReference>() { + } ); assertThat(values).containsKey(rootValuesAlias); @@ -124,7 +123,7 @@ void helmInstall_createsDeployment() throws IOException { // ./values.schema.json // The type must be declared for every list value, otherwise the generated schema // falls back to `string` and `helm install` rejects a list. - var valuesSchema = KUBERNETES_SERIALIZATION.unmarshal( + var valuesSchema = Serialization.unmarshal( Files.newInputStream(chartPath.resolve("values.schema.json")), JsonNode.class ); From 00a03683c194cce41ab130a6354d1ea3c0c5e2e1 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 11 Sep 2026 13:47:21 +0200 Subject: [PATCH 6/9] self-review fixes: raise Lombok to 1.18.48, drop stale Checkstyle version, add README lead sentence Lombok added lombok.checkReturnValueAnnotation in 1.18.48, so the new lombok.config line had no effect on 1.18.46. The checkstyle version entry had no remaining reference. The README Setup heading lost its lead sentence when the registry section was removed. --- README.md | 2 ++ gradle/libs.versions.toml | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 629202b..cb8b5c2 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,8 @@ To build the project, the following prerequisites must be met: ### Setup +Build the project with: + ```bash make init diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 506b34a..8b7ae9f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,14 +5,13 @@ axionReleasePlugin = "1.21.1" ## Libraries ## jooq = "3.21.4" jSpecify = "1.0.0" -lombok = "1.18.46" +lombok = "1.18.48" postgresql = "42.7.11" quarkiverse-helm = "1.4.0" scram-client = "3.2" ## Testing ## assertj = "3.27.7" -checkstyle = "13.4.2" datafaker = "2.5.4" errorProne = "2.50.0" errorPronePlugin = "5.1.1" From f982915a0d4ee64df374a4b279607dd5b408af5c Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Mon, 5 Oct 2026 16:41:40 +0200 Subject: [PATCH 7/9] read the Helm chart files through `KubernetesSerialization` in `HelmTest` Assisted-by: Claude:claude-opus-5-5 --- .../test/java/it/aboutbits/postgresql/helm/HelmTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java index 740a0eb..fbe6a0d 100644 --- a/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java +++ b/operator/src/test/java/it/aboutbits/postgresql/helm/HelmTest.java @@ -83,9 +83,11 @@ void helmInstall_createsDeployment() throws IOException { .withFailMessage("Helm chart not found at %s. Ensure that the chart is generated before running this test.", chartPath) .exists(); + var serialization = kubernetesClient.getKubernetesSerialization(); + // 1. Verify files exist and contain expected data // ./Chart.yaml - Map chartMetadata = Serialization.unmarshal( + Map chartMetadata = serialization.unmarshal( Files.newInputStream(chartPath.resolve("Chart.yaml")), new TypeReference>() { } @@ -94,7 +96,7 @@ void helmInstall_createsDeployment() throws IOException { assertThat(chartMetadata.get("name")).isEqualTo(chartName); // ./values.yaml - Map values = Serialization.unmarshal( + Map values = serialization.unmarshal( Files.newInputStream(chartPath.resolve("values.yaml")), new TypeReference>() { } @@ -123,7 +125,7 @@ void helmInstall_createsDeployment() throws IOException { // ./values.schema.json // The type must be declared for every list value, otherwise the generated schema // falls back to `string` and `helm install` rejects a list. - var valuesSchema = Serialization.unmarshal( + var valuesSchema = serialization.unmarshal( Files.newInputStream(chartPath.resolve("values.schema.json")), JsonNode.class ); From ce8a2128666fffd7382a75d855afe040351ffdb7 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Thu, 22 Oct 2026 12:15:35 +0200 Subject: [PATCH 8/9] fail the build on every compiler warning with -Werror Assisted-by: Claude:claude-opus-5-5 --- build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index e4ea3b9..e1d60af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,6 +73,8 @@ subprojects { options.compilerArgs.add("-Xlint:deprecation,removal,unchecked,cast,rawtypes,divzero,this-escape,identity,text-blocks,dangling-doc-comments,restricted") // The build itself: command-line options, path entries, output file collisions options.compilerArgs.add("-Xlint:options,path,output-file-clash") + // Every warning fails the build: javac lint and Error Prone checks at WARN alike + options.compilerArgs.add("-Werror") options.errorprone { // The checks live in errorprone.args, see https://github.com/tbroyer/gradle-errorprone-plugin#argument-files From e4e1e3a6ce5428786244ba44ea5c086920651dbd Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Thu, 22 Oct 2026 12:17:14 +0200 Subject: [PATCH 9/9] enable the overloads, varargs and try lint categories Error Prone covers these categories only in part. A probe showed javac warnings with no Error Prone counterpart, for example an unreferenced try-with-resources variable or a redundant @SafeVarargs. Assisted-by: Claude:claude-opus-5-5 --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e1d60af..86b8965 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -69,8 +69,8 @@ subprojects { options.encoding = "UTF-8" options.compilerArgs.add("-parameters") - // Source code: A category that an Error Prone check already owns is deliberately absent - options.compilerArgs.add("-Xlint:deprecation,removal,unchecked,cast,rawtypes,divzero,this-escape,identity,text-blocks,dangling-doc-comments,restricted") + // Source code: A category that an Error Prone check fully owns is deliberately absent + options.compilerArgs.add("-Xlint:deprecation,removal,unchecked,cast,rawtypes,divzero,this-escape,identity,overloads,varargs,try,text-blocks,dangling-doc-comments,restricted") // The build itself: command-line options, path entries, output file collisions options.compilerArgs.add("-Xlint:options,path,output-file-clash") // Every warning fails the build: javac lint and Error Prone checks at WARN alike