diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dbe911d7..09599e062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to ReadingBat Core are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [3.2.1] - 2026-07-03 + +A maintenance release: a Gradle 9.6.1 upgrade, a routine dependency refresh, and two build/test polish items. No functional changes to the running server. + +### Changed + +- Reworked the `configureVersions()` pre-release filter in the root `build.gradle.kts`: a candidate is now rejected only when the *current* version is stable, so dependencies intentionally tracked on a pre-release line (e.g. the detekt 2.0 alpha) still surface newer pre-releases. The qualifier regex now matches both dash-style (`-alpha`) and dot-style (Netty's `.Beta1`) suffixes while leaving stable classifiers like `-jre` / `.Final` / `-macos` alone +- Marked the `dependencyUpdates` task incompatible with the configuration cache so `make versions` no longer trips the cache +- `TestSupport.forEachAnswer` now accepts a `suspend` block, matching the suspend `functionInfo()` call site introduced in 3.2.0 +- Bumped version to 3.2.1 + +### Dependencies + +- Gradle wrapper 9.5.1 → 9.6.1 +- common-utils 2.9.2 → 2.9.3 +- Ktor 3.5.0 → 3.5.1 +- Exposed 1.3.0 → 1.3.1 +- Kotest 6.2.0 → 6.2.1 +- Flyway 12.8.1 → 12.10.0 +- PostgreSQL driver 42.7.11 → 42.7.12 +- Google Cloud SQL socket factory 1.28.4 → 1.28.6 +- Playwright 1.60.0 → 1.61.0 +- detekt 2.0.0-alpha.4 → 2.0.0-alpha.5 +- Vanniktech maven-publish 0.36.0 → 0.37.0 + ## [3.2.0] - 2026-06-15 A security-hardening release. A multi-agent security review surfaced 48 confirmed findings (7 high, 19 medium, 22 low); all 48 are addressed here, alongside the build/tooling cleanup below. diff --git a/CLAUDE.md b/CLAUDE.md index 224c64ed8..95e8d2ac7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Run a single test by name: `./gradlew :readingbat-core:test --tests "EndpointTest.Simple endpoint tests"` - Run application: `make run` or `./gradlew run` -Gradle 9.5.0 with `org.gradle.parallel=true` and `org.gradle.configuration-cache=true` enabled by default. The version +Gradle 9.6.1 with `org.gradle.parallel=true` and `org.gradle.configuration-cache=true` enabled by default. The version catalog (`gradle/libs.versions.toml`) is the single source of truth for plugin, dependency, **and toolchain** versions — the `gradle-wrapper` and `jvm` keys are read by `build.gradle.kts` (via `libs.versions.jvm`) and by the Makefile (the `upgrade-wrapper` target derives `GRADLE_VERSION` from the `gradle-wrapper` key in the catalog). Project version comes from `gradle.properties` @@ -159,7 +159,7 @@ The `readingbat-kotest` module provides `TestSupport` with helpers: ### Key Dependencies -- **common-utils** 2.9.2 (BOM from `com.github.pambrose`): shared utility library providing core-utils, email-utils, +- **common-utils** 2.9.3 (BOM from `com.github.pambrose`): shared utility library providing core-utils, email-utils, exposed-utils, ktor-client/server-utils, script-utils, etc. (`respondWith`/`redirectTo` take a `suspend` block as of 2.9.2) - **prometheus-proxy** 3.2.0: metrics collection - **Kover** 0.9.8: code coverage, applied to every subproject and aggregated at the root; CI uploads diff --git a/README.md b/README.md index 37bb82b4f..6296df0af 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ A Kotlin-based framework for creating interactive programming challenges and edu ReadingBat Core is built using modern Kotlin technologies: -- **Web Framework**: Ktor 3.5.0 with CIO engine +- **Web Framework**: Ktor 3.5.1 with CIO engine - **Database**: PostgreSQL with Exposed ORM (`exposed-kotlin-datetime`) and HikariCP connection pooling - **Authentication**: OAuth (GitHub, Google, verified-email required) with signed + encrypted session cookies - **Script Execution**: JSR-223 scripting engines for safe code evaluation -- **Build System**: Gradle 9.5 with Kotlin DSL, multi-module structure, and configuration cache enabled +- **Build System**: Gradle 9.6 with Kotlin DSL, multi-module structure, and configuration cache enabled - **Serialization**: kotlinx.serialization for JSON processing - **Testing**: Kotest framework with Playwright for E2E testing diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e9c91c74a..c3e4bc3c2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,23 @@ # Release Notes +## v3.2.1 — 2026-07-03 + +Maintenance release. A Gradle 9.6.1 upgrade, a routine dependency refresh, and two small build/test polish items. No functional changes to the running server, and no configuration or upgrade steps are required — this is a drop-in bump from 3.2.0. + +### Highlights + +- **Gradle 9.6.1.** The wrapper moves from 9.5.1 to 9.6.1. +- **Smarter pre-release version filter.** `configureVersions()` now rejects a pre-release candidate only when the *current* dependency is stable, so libraries deliberately tracked on a pre-release line (e.g. the detekt 2.0 alpha) keep surfacing newer pre-releases in `make versions`. The qualifier regex now catches both dash-style (`-alpha`) and dot-style (Netty's `.Beta1`) suffixes while leaving stable classifiers like `-jre` / `.Final` / `-macos` alone, and the `dependencyUpdates` task is marked incompatible with the configuration cache. +- **`TestSupport.forEachAnswer` takes a `suspend` block**, matching the suspend `functionInfo()` call site introduced in 3.2.0. + +### Dependencies + +Gradle 9.5.1 → 9.6.1 · common-utils 2.9.2 → 2.9.3 · Ktor 3.5.0 → 3.5.1 · Exposed 1.3.0 → 1.3.1 · Kotest 6.2.0 → 6.2.1 · Flyway 12.8.1 → 12.10.0 · PostgreSQL driver 42.7.11 → 42.7.12 · Cloud SQL socket factory 1.28.4 → 1.28.6 · Playwright 1.60.0 → 1.61.0 · detekt 2.0.0-alpha.4 → 2.0.0-alpha.5 · maven-publish 0.36.0 → 0.37.0 + +**Full Changelog**: https://github.com/readingbat/readingbat-core/compare/3.2.0...3.2.1 + +--- + ## v3.2.0 — 2026-06-15 Security-hardening release. A multi-agent security review surfaced 48 confirmed findings (7 high, 19 medium, 22 low); all 48 are addressed here. The headline items close an authentication bypass, an RCE, two IDOR/authorization gaps, and stored/reflected XSS, plus a batch of WebSocket and caching reliability fixes. The 3.1.9-era build/tooling cleanup ships in the same release. diff --git a/build.gradle.kts b/build.gradle.kts index b446dd403..38f831241 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,12 @@ +import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.SourcesJar import dev.detekt.gradle.Detekt import dev.detekt.gradle.extensions.DetektExtension import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.kotlin.dsl.withType plugins { alias(libs.plugins.kotlin.jvm) @@ -201,13 +203,22 @@ fun Project.configureTesting() { } fun Project.configureVersions() { - // Match preview/unstable qualifiers anchored on a separator so plain substrings like - // "DEV" inside a real version (e.g. "1.0-developer") don't trigger false rejections. - val nonStableRegex = Regex("(?i)[-.](RC|BETA|ALPHA|M\\d+|SNAPSHOT|DEV|PREVIEW|EAP|CR)\\b") - - tasks.withType().configureEach { + // A pre-release qualifier is a `.` or `-` delimiter followed by a known unstable + // keyword. `m\d` matches milestones (`-M1`/`.M2`) without catching stable classifiers + // like `-macos`/`-MR1`, and the `[.-]` delimiter catches both dash-style (`-alpha`) + // and dot-style (Netty's `.Beta1`) qualifiers while leaving `-jre`/`.Final` stable. + val preReleaseQualifier = + Regex("""[.-](rc|beta|alpha|m\d|cr|snapshot|eap|dev|milestone|pre)""", RegexOption.IGNORE_CASE) + + fun isNonStable(version: String): Boolean = preReleaseQualifier.containsMatchIn(version) + + tasks.withType().configureEach { + notCompatibleWithConfigurationCache("the dependency updates plugin is not compatible with the configuration cache") + // Reject a pre-release candidate only when the current version is stable. For + // dependencies we intentionally track on a pre-release line (e.g. a detekt + // alpha), newer pre-releases are still surfaced as available updates. rejectVersionIf { - nonStableRegex.containsMatchIn(candidate.version) + isNonStable(candidate.version) && !isNonStable(currentVersion) } } } diff --git a/gradle.properties b/gradle.properties index 09e4e1394..4d499b133 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=com.readingbat -version=3.2.0 +version=3.2.1 kotlin.code.style=official org.gradle.jvmargs=-Xmx8g -Dfile.encoding=UTF-8 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 535b06894..5b89165b7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,42 +1,42 @@ [versions] # Toolchain versions -gradle-wrapper = "9.5.1" +gradle-wrapper = "9.6.1" jvm = "17" # Plugin versions buildconfig = "6.0.10" -detekt = "2.0.0-alpha.4" +detekt = "2.0.0-alpha.5" kotlin = "2.4.0" kotlinter = "5.5.0" kover = "0.9.8" dokka = "2.2.0" -maven-publish = "0.36.0" +maven-publish = "0.37.0" versions = "0.54.0" # Library versions -cloud = "1.28.4" +cloud = "1.28.6" commons = "1.15.0" -exposed = "1.3.0" +exposed = "1.3.1" flexmark = "0.64.8" -flyway = "12.8.1" +flyway = "12.10.0" github-api = "1.330" hikari = "7.1.0" java-scriptengine = "2.0.0" khealth = "3.0.1" -kotest = "6.2.0" -ktor = "3.5.0" +kotest = "6.2.1" +ktor = "3.5.1" logback = "1.5.18" logging = "7.0.12" pgjdbc = "0.8.9" -playwright = "1.60.0" -postgres = "42.7.11" +playwright = "1.61.0" +postgres = "42.7.12" prometheus = "0.16.0" prometheus-proxy = "3.2.0" python = "2.7.4" resend = "4.13.0" serialization = "1.11.0" testcontainers = "1.21.4" -utils = "2.9.2" +utils = "2.9.3" [libraries] # Serialization diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df6a6ad76..a9db11550 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-9.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/gradlew b/gradlew index b9bb139f7..249efbb03 100755 --- a/gradlew +++ b/gradlew @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: diff --git a/gradlew.bat b/gradlew.bat index aa5f10b06..8508ef684 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,7 +19,7 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## @@ -72,7 +72,7 @@ echo location of your Java installation. 1>&2 -@rem Execute Gradle +@rem Execute gradlew @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel diff --git a/llms.txt b/llms.txt index 4274b876d..8417e54ec 100644 --- a/llms.txt +++ b/llms.txt @@ -6,13 +6,13 @@ ReadingBat Core is a web application built with Ktor that serves programming cha ## Key Technologies -- Kotlin 2.4.0, Ktor 3.5.0 (CIO engine), Exposed ORM 1.3.0 with kotlinx-datetime, PostgreSQL +- Kotlin 2.4.0, Ktor 3.5.1 (CIO engine), Exposed ORM 1.3.1 with kotlinx-datetime, PostgreSQL - Kotlinx.html for server-side HTML generation (no templates) -- Kotest 6.2.0 for testing, Playwright for E2E tests -- Gradle 9.5.1 multi-module build with version catalog (libs.versions.toml) as the single source of truth for plugin, dependency, and toolchain (`gradle`, `jvm`) versions; parallel project execution and configuration cache enabled +- Kotest 6.2.1 for testing, Playwright for E2E tests +- Gradle 9.6.1 multi-module build with version catalog (libs.versions.toml) as the single source of truth for plugin, dependency, and toolchain (`gradle`, `jvm`) versions; parallel project execution and configuration cache enabled - Kover 0.9.8 for code coverage, uploaded to Codecov from CI (per-area components defined in `codecov.yml`) - Detekt 2.0 alpha (`dev.detekt` plugin) for static analysis; Kotlinter for ktlint style -- common-utils 2.9.2 (shared utility BOM from `com.github.pambrose`) +- common-utils 2.9.3 (shared utility BOM from `com.github.pambrose`) - Java 17 toolchain ## Project Structure diff --git a/readingbat-kotest/src/main/kotlin/com/readingbat/kotest/TestSupport.kt b/readingbat-kotest/src/main/kotlin/com/readingbat/kotest/TestSupport.kt index b6ee50d62..848a361a8 100644 --- a/readingbat-kotest/src/main/kotlin/com/readingbat/kotest/TestSupport.kt +++ b/readingbat-kotest/src/main/kotlin/com/readingbat/kotest/TestSupport.kt @@ -177,7 +177,7 @@ object TestSupport { fun FunctionInfo.answerFor(index: Int) = ChallengeAnswer(this, index) - suspend fun Challenge.forEachAnswer(block: (ChallengeAnswer) -> Unit) = + suspend fun Challenge.forEachAnswer(block: suspend (ChallengeAnswer) -> Unit) = functionInfo().apply { (0 until questionCount).toList().forAll { i -> block(ChallengeAnswer(this, i)) } } diff --git a/website/readingbat-core/docs/release-notes.md b/website/readingbat-core/docs/release-notes.md index ba3afcfbf..955689bff 100644 --- a/website/readingbat-core/docs/release-notes.md +++ b/website/readingbat-core/docs/release-notes.md @@ -6,6 +6,22 @@ icon: lucide/tag For the complete, commit-level history see [`CHANGELOG.md`](https://github.com/readingbat/readingbat-core/blob/master/CHANGELOG.md) in the repository. +## v3.2.1 — 2026-07-03 + +Maintenance release: a Gradle 9.6.1 upgrade, a routine dependency refresh, and two small build/test polish items. No functional changes to the running server and no upgrade steps — a drop-in bump from 3.2.0. + +### Changed + +- **Gradle 9.6.1** — the wrapper moves from 9.5.1 to 9.6.1. +- **Smarter pre-release version filter** — `configureVersions()` rejects a pre-release candidate only when the *current* dependency is stable, so libraries tracked on a pre-release line (e.g. the detekt 2.0 alpha) keep surfacing newer pre-releases. The `dependencyUpdates` task is also marked incompatible with the configuration cache. +- **`TestSupport.forEachAnswer` takes a `suspend` block** — matching the suspend `functionInfo()` call site introduced in 3.2.0. + +### Dependencies + +Gradle 9.5.1 → 9.6.1 · common-utils 2.9.2 → 2.9.3 · Ktor 3.5.0 → 3.5.1 · Exposed 1.3.0 → 1.3.1 · Kotest 6.2.0 → 6.2.1 · Flyway 12.8.1 → 12.10.0 · PostgreSQL driver 42.7.11 → 42.7.12 · Cloud SQL socket factory 1.28.4 → 1.28.6 · Playwright 1.60.0 → 1.61.0 · detekt 2.0.0-alpha.4 → 2.0.0-alpha.5 · maven-publish 0.36.0 → 0.37.0 + +[Full changelog: 3.2.0…3.2.1](https://github.com/readingbat/readingbat-core/compare/3.2.0...3.2.1) + ## v3.2.0 — 2026-06-15 Security-hardening release. A multi-agent security review surfaced 48 confirmed findings (7 high, 19 medium, 22 low); all 48 are addressed here, alongside a batch of WebSocket/caching reliability fixes and the 3.1.9-era build/tooling cleanup.