From 11aac10c1a677fc48775a67a57d4c51c6c0b6fad Mon Sep 17 00:00:00 2001 From: Laura Kassovic Date: Tue, 7 Apr 2026 11:02:59 -0700 Subject: [PATCH 1/2] Upgrade to Gradle 9.0.0: replace SourceTask in GenerateChecksums and disable failOnNoDiscoveredTests --- build.gradle | 36 ++++++++++--------- .../groovy/t5build/GenerateChecksums.groovy | 7 ++-- .../tapestry.testing-base-convention.gradle | 20 ++++++++--- gradle/wrapper/gradle-wrapper.properties | 2 +- tapestry-webresources/build.gradle | 4 +-- 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index cfa3c0394..2ec20121c 100755 --- a/build.gradle +++ b/build.gradle @@ -117,6 +117,10 @@ subprojects { } } + tasks.withType(Test).configureEach { + failOnNoDiscoveredTests = false + } + tasks.named('assemble') { dependsOn processResources, compileJava, jar } @@ -236,7 +240,7 @@ tasks.register('aggregateJavadoc', Javadoc) { dependsOn configurations.javadoc maxMemory = '512m' - destinationDir = layout.buildDirectory.dir('documentation/javadocs').get().asFile + destinationDirectory = layout.buildDirectory.dir('documentation/javadocs') def tapestryStylesheet = file('src/javadoc/stylesheet7.css') int thisYear = java.time.Year.now().getValue() @@ -293,7 +297,7 @@ tasks.register('aggregateJavadoc', Javadoc) { doLast { copy { from allMainJavaSrcDirs - into aggregateJavadoc.destinationDir + into aggregateJavadoc.destinationDirectory exclude '**/*.java' exclude '**/*.xdoc' exclude '**/package.html' @@ -301,7 +305,7 @@ tasks.register('aggregateJavadoc', Javadoc) { copy { from file('src/javadoc/images') - into aggregateJavadoc.destinationDir + into aggregateJavadoc.destinationDirectory } } } @@ -389,7 +393,7 @@ tasks.register('zippedSources', Zip) { destinationDirectory = layout.buildDirectory archiveBaseName = 'apache-tapestry' - version project.version + archiveVersion = project.version archiveClassifier = 'sources' from project.projectDir @@ -415,7 +419,7 @@ tasks.register('zippedApidoc', Zip) { destinationDirectory = layout.buildDirectory archiveBaseName = 'apache-tapestry' - version project.version + archiveVersion = project.version archiveClassifier = 'apidocs' from(file('src/docroot-template')) { @@ -437,8 +441,8 @@ tasks.register('zippedApidoc', Zip) { } tasks.register('zippedBinaries', Zip) { - group 'Release artifact' - description 'Zip archive of binaries of each sub-project' + group = 'Release artifact' + description = 'Zip archive of binaries of each sub-project' // TODO: Plus dependencies? // This may create a few unwanted dependencies, but does @@ -447,7 +451,7 @@ tasks.register('zippedBinaries', Zip) { destinationDirectory = layout.buildDirectory archiveBaseName = 'apache-tapestry' - version project.version + archiveVersion = project.version archiveClassifier = 'bin' // This is via some experimentation @@ -488,10 +492,10 @@ if (canDeploy) { } tasks.register('generateChecksums', GenerateChecksums) { - group 'Release artifact' - description 'Creates MD5/SHA256 checksums for archives of source and JavaDoc' + group = 'Release artifact' + description = 'Creates MD5/SHA256 checksums for archives of source and JavaDoc' - source tasks.withType(Zip) + source.from(tasks.withType(Zip)) outputDir = layout.buildDirectory.dir('checksums').get().getAsFile() } @@ -510,18 +514,18 @@ if (canDeploy) { // to all Apache mirrors (after about a 24 hour delay). tasks.register('copyArchives', Copy) { - group 'Release artifact' - description 'Copies build archives (source, bin, docs) to a configured deployment folder, along with MD5 and SHA-256 checksums and PGP signatures (if signing is enabled)' + group = 'Release artifact' + description = 'Copies build archives (source, bin, docs) to a configured deployment folder, along with MD5 and SHA-256 checksums and PGP signatures (if signing is enabled)' - destinationDir = file(archiveDeployFolder.get()) + into file(archiveDeployFolder.get()) from tasks.generateChecksums from configurations.uploads.allArtifacts.files } tasks.register('generateRelease') { - group 'Release artifact' - description 'Generates and uploads a final release to Apache Nexus and copies archives for deployment' + group = 'Release artifact' + description = 'Generates and uploads a final release to Apache Nexus and copies archives for deployment' dependsOn subprojects.assemble, subprojects.uploadPublished, subprojects.publish, copyArchives } diff --git a/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy b/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy index dd9f9997f..e9d38cd6b 100644 --- a/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy +++ b/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy @@ -1,16 +1,14 @@ package t5build import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.SourceTask import org.gradle.api.file.ConfigurableFileCollection import java.security.MessageDigest -class GenerateChecksums extends SourceTask { +class GenerateChecksums extends DefaultTask { enum Algorithm { MD5('MD5', 32, 'md5'), @@ -27,6 +25,9 @@ class GenerateChecksums extends SourceTask { } } + @InputFiles + ConfigurableFileCollection source = project.objects.fileCollection() + @OutputDirectory File outputDir diff --git a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle index 35dd0f937..b061ae5ff 100644 --- a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle +++ b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle @@ -6,6 +6,18 @@ dependencies { testRuntimeOnly libs.slf4j.simple } +// Assign unique ports per project to avoid conflicts when tests run in parallel. +// Uses a hash of the project path to deterministically assign ports in the range [10000, 59998]. +// Subprojects can override these by setting 'tapestry.port' / 'tapestry.ssl-port' explicitly. +def portSeed = Math.abs(project.path.hashCode()) +def uniqueHttpPort = 10000 + ((portSeed % 25000) * 2) +def uniqueSslPort = uniqueHttpPort + 1 + +// Capture project identity at configuration time to avoid accessing project at execution time +// (Task.project at execution time is deprecated and will fail in Gradle 10.0) +def projectPath = project.path +def projectName = project.name + tasks.withType(Test).configureEach { testTask -> maxHeapSize = '600M' @@ -21,7 +33,7 @@ tasks.withType(Test).configureEach { testTask -> environment.LANG = 'en_US.UTF-8' testLogging { - exceptionFormat 'full' + exceptionFormat = 'full' events 'passed', 'skipped', 'failed' showStandardStreams = true } @@ -42,7 +54,7 @@ tasks.withType(Test).configureEach { testTask -> } total++ if (total % 25 == 0) { - logger.lifecycle "Project ${project.name}: Tests run: ${total}, Passed: ${passed}, Failed: ${failed}, Skipped: ${skipped}" + logger.lifecycle "Project ${projectName}: Tests run: ${total}, Passed: ${passed}, Failed: ${failed}, Skipped: ${skipped}" } } @@ -50,13 +62,11 @@ tasks.withType(Test).configureEach { testTask -> // The root suite has a null parent. We only want to log the final summary. if (descriptor.parent == null) { total = passed + failed + skipped - // Using project.path gives a clear identifier like ":tapestry-core" - def projectName = project.path // Don't log if no tests were run if (total > 0) { logger.lifecycle "------------------------------------------------------------------------" - logger.lifecycle "Test Results for ${projectName}" + logger.lifecycle "Test Results for ${projectPath}" logger.lifecycle " Tests run: ${total}, Passed: ${passed}, Failed: ${failed}, Skipped: ${skipped}" logger.lifecycle "------------------------------------------------------------------------" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da47..2a84e188b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/tapestry-webresources/build.gradle b/tapestry-webresources/build.gradle index f7d063c8a..d4ca00535 100644 --- a/tapestry-webresources/build.gradle +++ b/tapestry-webresources/build.gradle @@ -30,8 +30,8 @@ dependencies { test { systemProperties( - 'geb.build.reportsDir': "$reporting.baseDir/geb", - 'tapestry.compiled-asset-cache-dir': "$buildDir/compiled-asset-cache", + 'geb.build.reportsDir': "${reporting.baseDirectory.get().asFile}/geb", + 'tapestry.compiled-asset-cache-dir': "${layout.buildDirectory.get().asFile}/compiled-asset-cache", 'tapestry.production-mode': 'false', 'tapestry.compress-whitespace': 'false', 'tapestry.combine-scripts': 'false' From f5411eebd93f2996a02cd3f3c1cf4dcddb3ca4b5 Mon Sep 17 00:00:00 2001 From: Laura Kassovic Date: Tue, 7 Apr 2026 11:13:38 -0700 Subject: [PATCH 2/2] remove parallel changes from this PR --- .../groovy/tapestry.testing-base-convention.gradle | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle index b061ae5ff..3df2d4820 100644 --- a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle +++ b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle @@ -6,18 +6,6 @@ dependencies { testRuntimeOnly libs.slf4j.simple } -// Assign unique ports per project to avoid conflicts when tests run in parallel. -// Uses a hash of the project path to deterministically assign ports in the range [10000, 59998]. -// Subprojects can override these by setting 'tapestry.port' / 'tapestry.ssl-port' explicitly. -def portSeed = Math.abs(project.path.hashCode()) -def uniqueHttpPort = 10000 + ((portSeed % 25000) * 2) -def uniqueSslPort = uniqueHttpPort + 1 - -// Capture project identity at configuration time to avoid accessing project at execution time -// (Task.project at execution time is deprecated and will fail in Gradle 10.0) -def projectPath = project.path -def projectName = project.name - tasks.withType(Test).configureEach { testTask -> maxHeapSize = '600M'