From 1886aefe777648bff1e26a8c54e2363571cb3d60 Mon Sep 17 00:00:00 2001 From: Julien Truffaut Date: Fri, 4 Sep 2026 14:44:51 +0200 Subject: [PATCH 1/4] Move Scala 3 to 3.9.0 LTS Scala 3.9.0 is the new LTS line, superseding 3.3.x. The compiler no longer accepts a Java 11 output target: 3.9.0 sets minReleaseVersion = 17, so `-release:11` is rejected outright with "11 is not a valid choice for -java-output-version". Scala 3 artifacts therefore now require JDK 17 at runtime, and the JVM target moves to 17. For the same reason the temurin@11 CI leg cannot build Scala 3 at all under 3.9 -- its supported release range computes as (17 to 11), i.e. empty -- so the matrix moves to temurin@17 and the workflows are regenerated. The scalacOptions conditional was keyed off a "3.3." version prefix. Left as-is it would have stopped matching and silently dropped the release flag from Scala 3 JVM builds. It is now keyed off the binary version. `-Yfuture-lazy-vals` is dropped with it: the setting no longer exists in 3.9.0, so passing it would fail the build. Scala 2.13 keeps -release:8. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QRbVCYWp3bquNaXPYW12JY --- .github/workflows/ci.yml | 42 ++++++++++++++++++++-------------------- build.sbt | 11 ++++------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cebee1379..e0351fe73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: matrix: os: [ubuntu-22.04] scala: [2.13, 3] - java: [temurin@11, temurin@25] + java: [temurin@17, temurin@25] project: [rootJS, rootJVM, rootNative] exclude: - project: rootJS @@ -47,17 +47,17 @@ jobs: - name: Setup sbt uses: sbt/setup-sbt@v1 - - name: Setup Java (temurin@11) - id: setup-java-temurin-11 - if: matrix.java == 'temurin@11' + - name: Setup Java (temurin@17) + id: setup-java-temurin-17 + if: matrix.java == 'temurin@17' uses: actions/setup-java@v5 with: distribution: temurin - java-version: 11 + java-version: 17 cache: sbt - name: sbt update - if: matrix.java == 'temurin@11' && steps.setup-java-temurin-11.outputs.cache-hit == 'false' + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' run: sbt +update - name: Setup Java (temurin@25) @@ -77,7 +77,7 @@ jobs: run: sbt githubWorkflowCheck - name: Check formatting - if: matrix.java == 'temurin@11' && matrix.os == 'ubuntu-22.04' + if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04' run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' scalafmtCheckAll 'project /' scalafmtSbtCheck - name: scalaJSLink @@ -92,11 +92,11 @@ jobs: run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test - name: Check binary compatibility - if: matrix.java == 'temurin@11' && matrix.os == 'ubuntu-22.04' + if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04' run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues - name: Generate API documentation - if: matrix.java == 'temurin@11' && matrix.os == 'ubuntu-22.04' + if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04' run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc - name: Run documentation @@ -137,7 +137,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04] - java: [temurin@11] + java: [temurin@17] runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) @@ -148,17 +148,17 @@ jobs: - name: Setup sbt uses: sbt/setup-sbt@v1 - - name: Setup Java (temurin@11) - id: setup-java-temurin-11 - if: matrix.java == 'temurin@11' + - name: Setup Java (temurin@17) + id: setup-java-temurin-17 + if: matrix.java == 'temurin@17' uses: actions/setup-java@v5 with: distribution: temurin - java-version: 11 + java-version: 17 cache: sbt - name: sbt update - if: matrix.java == 'temurin@11' && steps.setup-java-temurin-11.outputs.cache-hit == 'false' + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' run: sbt +update - name: Setup Java (temurin@25) @@ -269,7 +269,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04] - java: [temurin@11] + java: [temurin@17] runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) @@ -280,17 +280,17 @@ jobs: - name: Setup sbt uses: sbt/setup-sbt@v1 - - name: Setup Java (temurin@11) - id: setup-java-temurin-11 - if: matrix.java == 'temurin@11' + - name: Setup Java (temurin@17) + id: setup-java-temurin-17 + if: matrix.java == 'temurin@17' uses: actions/setup-java@v5 with: distribution: temurin - java-version: 11 + java-version: 17 cache: sbt - name: sbt update - if: matrix.java == 'temurin@11' && steps.setup-java-temurin-11.outputs.cache-hit == 'false' + if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' run: sbt +update - name: Setup Java (temurin@25) diff --git a/build.sbt b/build.sbt index 21e789c2a..1c48d72c2 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ inThisBuild( ) }, githubWorkflowJavaVersions := Seq( - JavaSpec.temurin("11"), + JavaSpec.temurin("17"), JavaSpec.temurin("25") ), githubWorkflowPublishPostamble += WorkflowStep.Sbt( @@ -115,7 +115,7 @@ lazy val buildSettings = Seq( lazy val catsVersion = "2.13.0" lazy val scala2Version = "2.13.18" -lazy val scala3Version = "3.3.8" +lazy val scala3Version = "3.9.0" lazy val scalaNextVersion = "3.8.4" lazy val cats = Def.setting("org.typelevel" %%% "cats-core" % catsVersion) @@ -146,13 +146,10 @@ lazy val defaultReleaseOption = "-release:8" lazy val monocleSettings = buildSettings lazy val monocleJvmSettings = monocleSettings ++ Seq( scalacOptions ++= { - if (scalaVersion.value.startsWith("3.3.")) { + if (scalaBinaryVersion.value == "3") { Seq( - "-Yfuture-lazy-vals", - "-release:11" + "-release:17" ) - } else if (scalaBinaryVersion.value == "3") { - Nil } else { Seq( defaultReleaseOption From b501208e7fc09773cdff3fd6d7f91a379511442b Mon Sep 17 00:00:00 2001 From: Julien Truffaut Date: Fri, 4 Sep 2026 14:54:21 +0200 Subject: [PATCH 2/4] Apply the release flag on JS and Native too The JS and Native settings added -release:8 unconditionally, which only worked because Scala 3 was pinned to 3.3.8. Scala 3 has rejected targets below 17 since 3.8 (minReleaseVersion = 17), so under 3.9.0 both fail with "8 is not a valid choice for -java-output-version". Route all three platforms through a single releaseOption keyed off the binary version, so Scala 3 gets -release:17 and Scala 2.13 keeps -release:8 everywhere. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QRbVCYWp3bquNaXPYW12JY --- build.sbt | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 1c48d72c2..f873bf94d 100644 --- a/build.sbt +++ b/build.sbt @@ -142,26 +142,23 @@ lazy val scalaNativeSettings = Seq( ) lazy val defaultReleaseOption = "-release:8" +lazy val scala3ReleaseOption = "-release:17" + +// Scala 3 has not accepted a target below 17 since 3.8 (minReleaseVersion = 17), +// so the release flag has to follow the binary version on every platform. +lazy val releaseOption = Def.setting { + if (scalaBinaryVersion.value == "3") scala3ReleaseOption else defaultReleaseOption +} lazy val monocleSettings = buildSettings lazy val monocleJvmSettings = monocleSettings ++ Seq( - scalacOptions ++= { - if (scalaBinaryVersion.value == "3") { - Seq( - "-release:17" - ) - } else { - Seq( - defaultReleaseOption - ) - } - } + scalacOptions += releaseOption.value ) lazy val monocleJsSettings = monocleSettings ++ scalajsSettings ++ Seq( - scalacOptions += defaultReleaseOption + scalacOptions += releaseOption.value ) lazy val monocleNativeSettings = monocleSettings ++ scalaNativeSettings ++ Seq( - scalacOptions += defaultReleaseOption + scalacOptions += releaseOption.value ) lazy val root = tlCrossRootProject.aggregate( From c10b996defb1ec681f6b82020002b1648691399f Mon Sep 17 00:00:00 2001 From: Julien Truffaut Date: Fri, 4 Sep 2026 15:01:51 +0200 Subject: [PATCH 3/4] Move scalaNextVersion to 3.10.0-RC1 scalaNextTest depends on core, which is now built by 3.9.0 and emits TASTy 28.9. The 3.8.4 compiler reads at most 28.8, so the Scala Next jobs failed with "Forward incompatible TASTy file has version 28.9 [...] expected stable TASTy from 28.0 to 28.8". The project exists to exercise the compiler line ahead of the one we publish, so it has to move past 3.9.0 rather than trail it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QRbVCYWp3bquNaXPYW12JY --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index f873bf94d..0867780dc 100644 --- a/build.sbt +++ b/build.sbt @@ -116,7 +116,7 @@ lazy val buildSettings = Seq( lazy val catsVersion = "2.13.0" lazy val scala2Version = "2.13.18" lazy val scala3Version = "3.9.0" -lazy val scalaNextVersion = "3.8.4" +lazy val scalaNextVersion = "3.10.0-RC1" lazy val cats = Def.setting("org.typelevel" %%% "cats-core" % catsVersion) lazy val catsFree = Def.setting("org.typelevel" %%% "cats-free" % catsVersion) From 65ec6a49419890ea0254a5c23f9cb9cf1cce158c Mon Sep 17 00:00:00 2001 From: Julien Truffaut Date: Fri, 4 Sep 2026 15:13:25 +0200 Subject: [PATCH 4/4] Remove scalaNextTest, fold its tests into test scalaNextTest existed for one reason: NamedTuple support could not be tested on the version we published against, because scala/NamedTuple does not exist in 3.3.x at all. It held two files and needed a separate compiler, a version skew, and a scalacOptions workaround to carry them. On 3.9.0 NamedTuple is present and no longer experimental, so the two tests compile and run in the ordinary test project on the same compiler as everything else. That removes the project, scalaNextVersion (and with it the 3.10.0-RC1 prerelease from CI), the "-release:8"/"-Ykind-projector" removal that only papered over the version mismatch, and three CI steps. The tests move to test/shared/src/test/scala-3, so they stay Scala 3 only and 2.13 does not see them. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QRbVCYWp3bquNaXPYW12JY --- .github/workflows/ci.yml | 14 +-------- build.sbt | 29 +++---------------- .../monocle/focus/NamedTupleFocusTest.scala | 0 .../monocle/focus/NamedTupleLawTest.scala | 0 4 files changed, 5 insertions(+), 38 deletions(-) rename {scalaNextTest => test}/shared/src/test/scala-3/monocle/focus/NamedTupleFocusTest.scala (100%) rename {scalaNextTest => test}/shared/src/test/scala-3/monocle/focus/NamedTupleLawTest.scala (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0351fe73..df6fc757a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,18 +103,6 @@ jobs: if: matrix.scala == '2.13' && matrix.project == 'rootJVM' run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' docs/mdoc - - name: Run Scala Next Tests (jvm) - if: matrix.java == 'temurin@25' && matrix.scala == '3' - run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' scalaNextTestJVM/test - - - name: Run Scala Next Tests (js) - if: matrix.java == 'temurin@25' && matrix.scala == '3' - run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' scalaNextTestJS/test - - - name: Run Scala Next Tests (native) - if: matrix.java == 'temurin@25' && matrix.scala == '3' - run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' scalaNextTestNative/test - - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) run: mkdir -p unsafe/.js/target generic/.jvm/target law/.js/target macro/.jvm/target unsafe/.native/target state/.jvm/target core/native/target unsafe/.jvm/target macro/.native/target core/js/target macro/.js/target law/.jvm/target generic/.js/target core/jvm/target refined/.native/target law/.native/target refined/.js/target refined/.jvm/target state/.native/target state/.js/target generic/.native/target project/target @@ -309,7 +297,7 @@ jobs: - name: Submit Dependencies uses: scalacenter/sbt-dependency-submission@v2 with: - modules-ignore: monocle-test_2.13 monocle-test_3 monocle-test_native0.5_2.13 monocle-test_native0.5_3 rootjs_2.13 rootjs_3 monocle-docs_2.13 scalanexttest_3 rootjvm_2.13 rootjvm_3 rootnative_2.13 rootnative_3 scalanexttest_native0.5_3 scalanexttest_sjs1_3 monocle-test_sjs1_2.13 monocle-test_sjs1_3 monocle-example_2.13 monocle-example_3 monocle-bench_2.13 monocle-bench_3 + modules-ignore: monocle-test_2.13 monocle-test_3 monocle-test_native0.5_2.13 monocle-test_native0.5_3 rootjs_2.13 rootjs_3 monocle-docs_2.13 rootjvm_2.13 rootjvm_3 rootnative_2.13 rootnative_3 monocle-test_sjs1_2.13 monocle-test_sjs1_3 monocle-example_2.13 monocle-example_3 monocle-bench_2.13 monocle-bench_3 configs-ignore: test scala-tool scala-doc-tool test-internal validate-steward: diff --git a/build.sbt b/build.sbt index 0867780dc..e7626b598 100644 --- a/build.sbt +++ b/build.sbt @@ -29,13 +29,7 @@ inThisBuild( name = Some("Run documentation"), cond = Some(s"matrix.scala == '2.13' && matrix.project == 'rootJVM'") ) - ) ++ scalaNextTest.projects.map { case (platform, project) => - WorkflowStep.Sbt( - List(s"${project.id}/test"), - name = Some(s"Run Scala Next Tests (${platform.identifier})"), - cond = Some(s"matrix.java == 'temurin@25' && matrix.scala == '3'") - ) - }, + ), githubWorkflowJavaVersions := Seq( JavaSpec.temurin("17"), JavaSpec.temurin("25") @@ -113,10 +107,9 @@ lazy val buildSettings = Seq( } ) -lazy val catsVersion = "2.13.0" -lazy val scala2Version = "2.13.18" -lazy val scala3Version = "3.9.0" -lazy val scalaNextVersion = "3.10.0-RC1" +lazy val catsVersion = "2.13.0" +lazy val scala2Version = "2.13.18" +lazy val scala3Version = "3.9.0" lazy val cats = Def.setting("org.typelevel" %%% "cats-core" % catsVersion) lazy val catsFree = Def.setting("org.typelevel" %%% "cats-free" % catsVersion) @@ -144,8 +137,6 @@ lazy val scalaNativeSettings = Seq( lazy val defaultReleaseOption = "-release:8" lazy val scala3ReleaseOption = "-release:17" -// Scala 3 has not accepted a target below 17 since 3.8 (minReleaseVersion = 17), -// so the release flag has to follow the binary version on every platform. lazy val releaseOption = Def.setting { if (scalaBinaryVersion.value == "3") scala3ReleaseOption else defaultReleaseOption } @@ -306,18 +297,6 @@ lazy val test = crossProject(JVMPlatform, JSPlatform, NativePlatform) ) ) -lazy val scalaNextTest = crossProject(JVMPlatform, JSPlatform, NativePlatform) - .dependsOn(test % "test->test") - .jvmSettings(monocleJvmSettings) - .jsSettings(monocleJsSettings) - .nativeSettings(monocleNativeSettings) - .enablePlugins(NoPublishPlugin) - .settings( - crossScalaVersions := Seq(scalaNextVersion), - libraryDependencies ++= Seq(munitDiscipline.value), - scalacOptions --= Seq("-release:8", "-Ykind-projector") - ) - lazy val bench = project .dependsOn(core.jvm, generic.jvm, macros.jvm) .settings(moduleName := "monocle-bench") diff --git a/scalaNextTest/shared/src/test/scala-3/monocle/focus/NamedTupleFocusTest.scala b/test/shared/src/test/scala-3/monocle/focus/NamedTupleFocusTest.scala similarity index 100% rename from scalaNextTest/shared/src/test/scala-3/monocle/focus/NamedTupleFocusTest.scala rename to test/shared/src/test/scala-3/monocle/focus/NamedTupleFocusTest.scala diff --git a/scalaNextTest/shared/src/test/scala-3/monocle/focus/NamedTupleLawTest.scala b/test/shared/src/test/scala-3/monocle/focus/NamedTupleLawTest.scala similarity index 100% rename from scalaNextTest/shared/src/test/scala-3/monocle/focus/NamedTupleLawTest.scala rename to test/shared/src/test/scala-3/monocle/focus/NamedTupleLawTest.scala