From 333a3e5098a9578a4ec300fbbe37fb8baf99f62e Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:36:50 -0500 Subject: [PATCH 1/6] [hibernate#2492] build(hr-print-resolved-version): move to java and explicit task without project use Using a valid input can be cacheable https://docs.gradle.org/current/userguide/configuration_cache.html#build_configuration_inputs --- .../groovy/hr-print-resolved-version.gradle | 27 ++++---------- .../env/PrintResolvedVersionsTask.java | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 local-build-plugins/src/main/java/org/hibernate/reactive/env/PrintResolvedVersionsTask.java diff --git a/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle b/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle index 9dea09b82..ba44fadc2 100644 --- a/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle +++ b/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle @@ -1,31 +1,16 @@ +import org.hibernate.reactive.env.PrintResolvedVersionsTask + // Task to print the resolved versions of Hibernate ORM and Vert.x -tasks.register( "printResolvedVersions" ) { +tasks.register( "printResolvedVersions", PrintResolvedVersionsTask ) { + classpath.from( configurations.compileClasspath ) description = "Print the resolved hibernate-orm-core and vert.x versions" - doLast { - def hibernateCoreVersion = "n/a" - def vertxVersion = "n/a" - - // Resolve Hibernate Core and Vert.x versions from compile classpath - configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> - if (artifact.moduleVersion.id.name == 'hibernate-core') { - hibernateCoreVersion = artifact.moduleVersion.id.version - } - if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { - vertxVersion = artifact.moduleVersion.id.version - } - } - - // Print the resolved versions - println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" - println "Resolved Vert.x SQL client Version: ${vertxVersion}" - } } // Make the version printing task run before tests and JavaExec tasks tasks.withType( Test ).configureEach { - dependsOn printResolvedVersions + dependsOn( tasks.named( "printResolvedVersions" ) ) } tasks.withType( JavaExec ).configureEach { - dependsOn printResolvedVersions + dependsOn( tasks.named( "printResolvedVersions" ) ) } \ No newline at end of file diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/PrintResolvedVersionsTask.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/PrintResolvedVersionsTask.java new file mode 100644 index 000000000..e890d3ae8 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/PrintResolvedVersionsTask.java @@ -0,0 +1,36 @@ +package org.hibernate.reactive.env; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.TaskAction; + +import org.jetbrains.annotations.NotNull; + +@CacheableTask +public abstract class PrintResolvedVersionsTask extends DefaultTask { + + @Classpath + @NotNull + public abstract ConfigurableFileCollection getClasspath(); + + @TaskAction + public void printVersions() { + var hibernateCoreVersion = "n/a"; + var vertxVersion = "n/a"; + + for ( final var file : getClasspath().getFiles() ) { + String name = file.getName(); + if ( name.startsWith( "hibernate-core-" ) && name.endsWith( ".jar" ) ) { + hibernateCoreVersion = name.substring( "hibernate-core-".length(), name.length() - 4 ); + } + if ( name.startsWith( "vertx-sql-client-" ) && name.endsWith( ".jar" ) ) { + vertxVersion = name.substring( "vertx-sql-client-".length(), name.length() - 4 ); + } + } + + System.out.println( "Resolved Hibernate ORM Core Version: " + hibernateCoreVersion ); + System.out.println( "Resolved Vert.x SQL client Version: " + vertxVersion ); + } +} From e79098167d63d9b5f35c2c121ff40c6be1293c15 Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:40:13 -0500 Subject: [PATCH 2/6] [hibernate#2492] build(hr-test-containers): replace config of 'test' with a custom test task configuration of properties is set up before execute test and print a summary after suite. Without use of project is cacheable --- .../src/main/groovy/hr-test-containers.gradle | 89 ++++--------- .../hibernate/reactive/env/TestDbTask.java | 122 ++++++++++++++++++ 2 files changed, 146 insertions(+), 65 deletions(-) create mode 100644 local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java diff --git a/local-build-plugins/src/main/groovy/hr-test-containers.gradle b/local-build-plugins/src/main/groovy/hr-test-containers.gradle index fad66398f..5f7c99674 100644 --- a/local-build-plugins/src/main/groovy/hr-test-containers.gradle +++ b/local-build-plugins/src/main/groovy/hr-test-containers.gradle @@ -1,79 +1,38 @@ -// Print a summary of the results of the tests (number of failures, successes and skipped) -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -// Example: -// gradle test -Pdb=MySQL -test { - def selectedDb - - doFirst { - selectedDb = project.hasProperty( 'db' ) - ? project.properties['db'] - : 'PostgreSQL' - systemProperty 'db', selectedDb - } - afterSuite { desc, result -> - loggingSummary( selectedDb, result, desc ) - } -} +import org.hibernate.reactive.env.TestDbTask -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - showStandardStreams = project.hasProperty('showStandardOutput') - showStackTraces = true - exceptionFormat = 'full' - displayGranularity = 1 - events = ['PASSED', 'FAILED', 'SKIPPED'] - } - systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false' - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' +def dbs = ['MariaDB', 'MySQL', 'PostgreSQL', 'DB2', 'CockroachDB', 'MSSQLServer', 'Oracle'] - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.properties['includeTests'] ?: '*' as String - } +// create a task for each DB, now is static configure and not dynamic with rule pattern +// and run the tests on the selected db +// Example: +// gradlew testDbMySQL testDbDB2 +dbs.each { db -> + tasks.register( "testDb${db}", TestDbTask ) { + dbName = db + description = "Run tests for ${db}" } } -def createTestDbTask(dbName) { - tasks.register( "testDb${dbName}", Test ) { - description = "Run tests for ${dbName}" - - doFirst() { - systemProperty 'db', dbName - } - afterSuite { desc, result -> - loggingSummary( dbName, result, desc ) - } +// replace default task 'test' with custom TestDbTask to always configure with cache safe task +tasks.replace( "test", TestDbTask ).configure { + dbName = project.findProperty( "db" ) ?: "PostgreSQL" + dockerEnabled = project.hasProperty( "docker" ) + if ( project.hasProperty( "includeTests" ) ) { + includeTests = project.property( "includeTests" ) } + description = "Default test task using TestDbTask" } -// Rule to recognize calls to testDb -// and run the tests on the selected db -// Example: -// gradle testDbMySQL testDbDB2 -tasks.addRule( "Pattern testDb" ) { String taskName -> - if ( taskName.startsWith( "testDb" ) ) { - def dbName = taskName.substring( "testDb".length() ) - createTestDbTask( dbName ) +// configure all testDbTask with docker and filter test +tasks.withType( TestDbTask ).configureEach { + dockerEnabled = project.hasProperty( 'docker' ) + if ( project.hasProperty( 'includeTests' ) ) { + includeTests = project.property( 'includeTests' ) } } -// The dbs we want to test when running testAll -def dbs = ['MariaDB', 'MySQL', 'PostgreSQL', 'DB2', 'CockroachDB', 'MSSQLServer', 'Oracle'] -dbs.forEach { createTestDbTask it } - +// task with all database tasks.register( "testAll", Test ) { description = "Run tests for ${dbs}" - dependsOn = dbs.collect( [] as HashSet ) { db -> "testDb${db}" } + dependsOn( dbs.collect { "testDb${it}" } ) } diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java new file mode 100644 index 000000000..ced8bfc12 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java @@ -0,0 +1,122 @@ +package org.hibernate.reactive.env; + +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.testing.Test; +import org.gradle.api.tasks.testing.TestDescriptor; +import org.gradle.api.tasks.testing.TestResult; + +import groovy.lang.Closure; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@CacheableTask +public abstract class TestDbTask extends Test { + + @NotNull + private String dbName = "PostgreSQL"; + private boolean dockerEnabled = false; + @Nullable + private String includeTests = null; + private boolean showStandardStreams = false; + + @Input + @Optional + @NotNull + public String getDbName() { + return dbName; + } + + public void setDbName(final @NotNull String dbName) { + this.dbName = dbName; + } + + @Input + public boolean isDockerEnabled() { + return dockerEnabled; + } + + public void setDockerEnabled(final boolean dockerEnabled) { + this.dockerEnabled = dockerEnabled; + } + + @Input + @Optional + @Nullable + public String getIncludeTests() { + return includeTests; + } + + public void setIncludeTests(final @Nullable String includeTests) { + this.includeTests = includeTests; + } + + @Input + public boolean isShowStandardStreams() { + return showStandardStreams; + } + + public void setShowStandardStreams(boolean showStandardStreams) { + this.showStandardStreams = showStandardStreams; + } + + public TestDbTask() { + // Default logging configuration + setDefaultCharacterEncoding( "UTF-8" ); + useJUnitPlatform(); + final var testLogging = getTestLogging(); + testLogging.setShowStandardStreams( showStandardStreams ); + testLogging.setShowStackTraces( true ); + testLogging.setExceptionFormat( "full" ); + testLogging.setDisplayGranularity( 1 ); + testLogging.events( "PASSED", "FAILED", "SKIPPED" ); + + // enforcing Hibernate internal state + systemProperty( "org.hibernate.reactive.common.InternalStateAssertions.ENFORCE", "true" ); + + // Add afterSuite hook + afterSuite( new Closure( this, this ) { + public Object doCall(TestDescriptor desc, TestResult result) { + logSummary( desc, result ); + return null; + } + } ); + } + + @Override + public void executeTests() { + // Apply system properties before running + systemProperty( "db", dbName ); + systemProperty( "docker", dockerEnabled ? "true" : "false" ); + getTestLogging().setShowStandardStreams( showStandardStreams ); + + if ( includeTests != null && !includeTests.isEmpty() ) { + getFilter().includeTestsMatching( includeTests ); + } + + super.executeTests(); + } + + /** + * Print a summary of the results of the tests (number of failures, successes and skipped) + * + * @param desc the test descriptor + * @param result the test result + */ + private void logSummary(final @NotNull TestDescriptor desc, final @NotNull TestResult result) { + if ( desc.getParent() == null ) { // outermost suite + final var output = String.format( + "%s results: %s (%d tests, %d passed, %d failed, %d skipped)", + dbName, + result.getResultType(), + result.getTestCount(), + result.getSuccessfulTestCount(), + result.getFailedTestCount(), + result.getSkippedTestCount() + ); + final var line = "-".repeat( output.length() + 1 ); + getLogger().lifecycle( "\n{}\n{}\n{}", line, output, line ); + } + } +} From 230b23df7637ad693f0600e1f1881c37982c9a72 Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:41:30 -0500 Subject: [PATCH 3/6] [hibernate#2492] build(gradle): enable configuration cache, configure on demand, caching run parallel don't have perfect support with test containers, have problems with ports --- gradle.properties | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gradle.properties b/gradle.properties index d098a7035..af06041aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,14 @@ toolchain.compiler.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOf toolchain.javadoc.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 toolchain.launcher.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8 +# asciidoctor gradle plugin don't support configuration cache +org.gradle.configuration-cache=true +org.gradle.configureondemand=true +# configuration of testcontainers need to be updated to prevent bind the same port +org.gradle.parallel=false +org.gradle.caching=true +org.gradle.daemon=true + # JDK auto-detection is not quite ready yet in Gradle 6.7. # On Fedora in particular, if you have the package java-1.8.0-openjdk-headless-1.8.0.265.b01-1.fc32.x86_64 installed, # Gradle will look for the Java binaries in /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.265.b01-1.fc32.x86_64/bin/java From 337332d51e456dd0b3ee95b3b9275b9f130993f1 Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Wed, 3 Sep 2025 12:33:17 -0500 Subject: [PATCH 4/6] [hibernate#2492] build(documentation): mark not compatible with configuration cache, refactor use of project ascii doctor plugin is not compatible yet https://github.com/asciidoctor/asciidoctor-gradle-plugin/pull/730 --- documentation/build.gradle | 42 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/documentation/build.gradle b/documentation/build.gradle index 01c67f967..cd3f823ef 100644 --- a/documentation/build.gradle +++ b/documentation/build.gradle @@ -101,30 +101,40 @@ asciidoctor { enabled = false } -def renderReferenceDocumentationTask = tasks.register( 'renderReferenceDocumentation', AsciidoctorTask ) { - description = 'Renders the Reference Documentation in HTML format using Asciidoctor.' - sourceDir = file( 'src/main/asciidoc/reference' ) +def renderReferenceDocumentationTask = tasks.register( "renderReferenceDocumentation", AsciidoctorTask ) { + description = "Renders the Reference Documentation in HTML format using Asciidoctor." + notCompatibleWithConfigurationCache( "AsciidoctorGradlePlugin does not support configuration cache yet" ) + + // use of provider to obtain info and cache the value + def versionFamily = providers.provider { project.projectVersion.family } + def fullVersion = providers.provider { project.version.toString() } + def asciidocReference = layout.projectDirectory.dir("src/main/asciidoc/reference") + + sourceDir = asciidocReference.asFile + sources { - include 'index.adoc' + include( "index.adoc" ) } resources { - from( sourceDir ) { - include 'images/**' - include 'css/**' + from( asciidocReference ) { + include( "images/**", "css/**" ) } } - outputDir = project.layout.buildDirectory.dir( "asciidoc/reference/html_single" ).get().asFile - options logDocuments: true + outputDir = layout.buildDirectory.dir("asciidoc/reference/html_single").get().asFile + + options( logDocuments: true ) - attributes icons: 'font', - 'source-highlighter': 'rouge', - experimental: true, - linkcss: true, - majorMinorVersion: project.projectVersion.family, - fullVersion: project.version.toString(), - docinfo: 'private' + attributes( + "icons": "font", + "source-highlighter": "rouge", + "experimental": true, + "linkcss": true, + "majorMinorVersion": versionFamily.get(), + "fullVersion": fullVersion.get(), + "docinfo": "private" + ) } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 2ee04777fd998fc64518030cb3f7864bf3bb08da Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:11:29 -0500 Subject: [PATCH 5/6] [hibernate#2492] perf(test db task local): remove use of groovy closure and explicit get input of properties Gradle need to know all input and prevent side effect with dynamic behaviors --- .../hibernate/reactive/env/TestDbTask.java | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java index ced8bfc12..08aa55b36 100644 --- a/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/TestDbTask.java @@ -1,17 +1,18 @@ package org.hibernate.reactive.env; -import org.gradle.api.tasks.CacheableTask; +import java.util.HashMap; +import java.util.Map; + import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.testing.Test; import org.gradle.api.tasks.testing.TestDescriptor; +import org.gradle.api.tasks.testing.TestListener; import org.gradle.api.tasks.testing.TestResult; -import groovy.lang.Closure; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -@CacheableTask public abstract class TestDbTask extends Test { @NotNull @@ -61,6 +62,14 @@ public void setShowStandardStreams(boolean showStandardStreams) { this.showStandardStreams = showStandardStreams; } + @Input + public Map getCustomSystemProperties() { + final Map props = new HashMap<>(); + props.put("db", dbName); + props.put("docker", dockerEnabled ? "true" : "false"); + return props; + } + public TestDbTask() { // Default logging configuration setDefaultCharacterEncoding( "UTF-8" ); @@ -75,11 +84,27 @@ public TestDbTask() { // enforcing Hibernate internal state systemProperty( "org.hibernate.reactive.common.InternalStateAssertions.ENFORCE", "true" ); - // Add afterSuite hook - afterSuite( new Closure( this, this ) { - public Object doCall(TestDescriptor desc, TestResult result) { - logSummary( desc, result ); - return null; + addTestListener( new TestListener() { + + @Override + public void beforeSuite(TestDescriptor suite) { + /* Do nothing */ + } + + // Add afterSuite hook + @Override + public void afterSuite(TestDescriptor suite, TestResult result) { + logSummary( suite, result ); + } + + @Override + public void beforeTest(TestDescriptor testDescriptor) { + /* Do nothing */ + } + + @Override + public void afterTest(TestDescriptor testDescriptor, TestResult result) { + /* Do nothing */ } } ); } @@ -87,8 +112,7 @@ public Object doCall(TestDescriptor desc, TestResult result) { @Override public void executeTests() { // Apply system properties before running - systemProperty( "db", dbName ); - systemProperty( "docker", dockerEnabled ? "true" : "false" ); + getCustomSystemProperties().forEach(this::systemProperty); getTestLogging().setShowStandardStreams( showStandardStreams ); if ( includeTests != null && !includeTests.isEmpty() ) { From 533c040f07a0a66ecc27d45253629f73a2d7a8c2 Mon Sep 17 00:00:00 2001 From: Rey <89825425+Kingg22@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:13:22 -0500 Subject: [PATCH 6/6] [hibernate#2492] fix(hr test container local plugin): remove replace test with custom task, configure it instead --- .../src/main/groovy/hr-test-containers.gradle | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/local-build-plugins/src/main/groovy/hr-test-containers.gradle b/local-build-plugins/src/main/groovy/hr-test-containers.gradle index 5f7c99674..1520dd753 100644 --- a/local-build-plugins/src/main/groovy/hr-test-containers.gradle +++ b/local-build-plugins/src/main/groovy/hr-test-containers.gradle @@ -13,14 +13,53 @@ dbs.each { db -> } } -// replace default task 'test' with custom TestDbTask to always configure with cache safe task -tasks.replace( "test", TestDbTask ).configure { - dbName = project.findProperty( "db" ) ?: "PostgreSQL" - dockerEnabled = project.hasProperty( "docker" ) - if ( project.hasProperty( "includeTests" ) ) { - includeTests = project.property( "includeTests" ) +// configure default task 'test' with same config of TestDbTask, with cache safe +tasks.named( "test", Test ).configure { t -> + def dbName = providers.gradleProperty( "db" ).orElse( "PostgreSQL" ) + def dockerEnabled = providers.gradleProperty( "docker" ).isPresent() + def includeTests = providers.gradleProperty( "includeTests" ).orNull + def showStandardStreams = providers.gradleProperty( "showStandardOutput" ).isPresent() + + t.systemProperty( "db", dbName.get() ) + t.systemProperty( "docker", dockerEnabled ? "true" : "false" ) + t.systemProperty( "org.hibernate.reactive.common.InternalStateAssertions.ENFORCE", "true" ) + + if ( includeTests ) { + t.filter { f -> f.includeTestsMatching( includeTests ) } + } + + t.defaultCharacterEncoding = "UTF-8" + t.useJUnitPlatform() + t.testLogging { + it.showStandardStreams = showStandardStreams + it.showStackTraces = true + it.exceptionFormat = 'full' + it.displayGranularity = 1 + it.events = ['PASSED', 'FAILED', 'SKIPPED'] } - description = "Default test task using TestDbTask" + + t.addTestListener( new TestListener() { + void beforeSuite(TestDescriptor suite) { + /* Do nothing */ + } + + void beforeTest(TestDescriptor testDescriptor) { + /* Do nothing */ + } + + void afterTest(TestDescriptor testDescriptor, TestResult result) { + /* Do nothing */ + } + + // Add afterSuite hook + void afterSuite(TestDescriptor desc, TestResult result) { + if ( !desc.parent ) { + def output = "${dbName.get()} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" + def line = '-' * (output.length() + 1) + logger.lifecycle( "\n${line}\n${output}\n${line}" ) + } + } + } ) } // configure all testDbTask with docker and filter test