From c12270e342a839b6403dbfceffcebe1d3ce9293d Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 18 Oct 2025 12:38:10 +0200 Subject: [PATCH 1/3] Use scala 3 version in examples --- build.mill | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.mill b/build.mill index 439d7b06be..e94d2c5924 100644 --- a/build.mill +++ b/build.mill @@ -239,12 +239,12 @@ def zippedExamples = Task { f / last / "build.mill", os.read(f / last / "build.mill") .replaceAll("package build.*", "package build") - .replaceAll("def moduleDeps =.*", "") + .replaceAll("def moduleDeps =.*\n", "") .replaceAll("///", "//|") .replaceAll("app =>", "") .replaceFirst( "object app extends.*\ntrait AppModule extends CrossScalaModule(.*)\\{", - s"object app extends ScalaModule $$1\\{\n def scalaVersion = \"${scala213}\"") + s"object app extends ScalaModule $$1\\{\n def scalaVersion = \"${scala3}\"") .replaceAll("build.scala3", s"\"${scala3}\"") .replaceFirst( "def mvnDeps = Seq\\[Dep\\]\\(", From 8c2e8f53796b8f06e4f51701b6f0eaac03d58bad Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 18 Oct 2025 14:34:58 +0200 Subject: [PATCH 2/3] Modify examples to build with latest scala 3 - while still depending on the 3.3 LTS compiled cask There is still some compiler bug with implicit conversions and `data = ` parameter of requests.get. (i.e. it doesnt unwrap named arguments, so tries to insert `data = ` as an argument of the implicit conversion method and fails) --- build.mill | 5 +-- .../app/src/MinimalApplication.scala | 9 ++--- .../app/test/src/ExampleTests.scala | 8 ++--- example/minimalApplication/package.mill | 4 +-- .../app/src/MinimalApplicationCross.scala | 14 ++++++++ .../app/test/src/ExampleTests.scala | 33 +++++++++++++++++++ example/minimalApplicationCross/package.mill | 18 ++++++++++ example/todo/package.mill | 6 ++-- example/todoDb/app/src/TodoMvcDb.scala | 2 +- .../todoDb/app/test/src/ExampleTests.scala | 2 +- example/todoDb/package.mill | 6 ++-- .../app/src/TodoMvcDbWithLoom.scala | 2 +- .../app/test/src/ExampleTests.scala | 2 +- example/todoDbWithLoom/package.mill | 7 ++-- 14 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 example/minimalApplicationCross/app/src/MinimalApplicationCross.scala create mode 100644 example/minimalApplicationCross/app/test/src/ExampleTests.scala create mode 100644 example/minimalApplicationCross/package.mill diff --git a/build.mill b/build.mill index e94d2c5924..b97fb5d6b7 100644 --- a/build.mill +++ b/build.mill @@ -11,6 +11,7 @@ import mill.util.VcsVersion val scala213 = "2.13.15" val scala212 = "2.12.20" val scala3 = "3.3.4" +val scala3Latest = "3.7.3" val scalaJS = "1.17.0" val communityBuildDottyVersion = sys.props.get("dottyVersion").toList @@ -244,8 +245,8 @@ def zippedExamples = Task { .replaceAll("app =>", "") .replaceFirst( "object app extends.*\ntrait AppModule extends CrossScalaModule(.*)\\{", - s"object app extends ScalaModule $$1\\{\n def scalaVersion = \"${scala3}\"") - .replaceAll("build.scala3", s"\"${scala3}\"") + s"object app extends ScalaModule $$1\\{\n def scalaVersion = \"${scala3Latest}\"") + .replaceAll("build.scala3", s"\"${scala3Latest}\"") .replaceFirst( "def mvnDeps = Seq\\[Dep\\]\\(", "def mvnDeps = Seq(\n mvn\"com.lihaoyi::cask:" + releaseTag + "\"," diff --git a/example/minimalApplication/app/src/MinimalApplication.scala b/example/minimalApplication/app/src/MinimalApplication.scala index 68eb8d0540..30f872e85b 100644 --- a/example/minimalApplication/app/src/MinimalApplication.scala +++ b/example/minimalApplication/app/src/MinimalApplication.scala @@ -1,14 +1,11 @@ package app -object MinimalApplication extends cask.MainRoutes{ +object MinimalApplication extends cask.MainRoutes: @cask.get("/") - def hello() = { + def hello() = "Hello World!" - } @cask.post("/do-thing") - def doThing(request: cask.Request) = { + def doThing(request: cask.Request) = request.text().reverse - } initialize() -} diff --git a/example/minimalApplication/app/test/src/ExampleTests.scala b/example/minimalApplication/app/test/src/ExampleTests.scala index cd04898c05..89c58ea9d4 100644 --- a/example/minimalApplication/app/test/src/ExampleTests.scala +++ b/example/minimalApplication/app/test/src/ExampleTests.scala @@ -1,10 +1,10 @@ package app import io.undertow.Undertow -import utest._ +import utest.* -object ExampleTests extends TestSuite{ - def withServer[T](example: cask.main.Main)(f: String => T): T = { +object ExampleTests extends TestSuite: + def withServer[T](example: cask.main.Main)(f: String => T): T = val server = Undertow.builder .addHttpListener(8081, "localhost") .setHandler(example.defaultHandler) @@ -14,7 +14,6 @@ object ExampleTests extends TestSuite{ try f("http://localhost:8081") finally server.stop() res - } val tests = Tests { test("MinimalApplication") - withServer(MinimalApplication) { host => @@ -30,4 +29,3 @@ object ExampleTests extends TestSuite{ requests.delete(s"$host/do-thing", check = false).statusCode ==> 405 } } -} diff --git a/example/minimalApplication/package.mill b/example/minimalApplication/package.mill index ce24a7d034..43e6ec9ec6 100644 --- a/example/minimalApplication/package.mill +++ b/example/minimalApplication/package.mill @@ -1,10 +1,10 @@ package build.example.minimalApplication import mill._, scalalib._ -object app extends Cross[AppModule](build.scalaVersions) +object app extends Cross[AppModule](build.scala3Latest) trait AppModule extends CrossScalaModule{ - def moduleDeps = Seq(build.cask(crossScalaVersion)) + def moduleDeps = Seq(build.cask(build.scala3)) def mvnDeps = Seq[Dep]( ) diff --git a/example/minimalApplicationCross/app/src/MinimalApplicationCross.scala b/example/minimalApplicationCross/app/src/MinimalApplicationCross.scala new file mode 100644 index 0000000000..fbd4f751d1 --- /dev/null +++ b/example/minimalApplicationCross/app/src/MinimalApplicationCross.scala @@ -0,0 +1,14 @@ +package app +object MinimalApplicationCross extends cask.MainRoutes{ + @cask.get("/") + def hello() = { + "Hello World!" + } + + @cask.post("/do-thing") + def doThing(request: cask.Request) = { + request.text().reverse + } + + initialize() +} diff --git a/example/minimalApplicationCross/app/test/src/ExampleTests.scala b/example/minimalApplicationCross/app/test/src/ExampleTests.scala new file mode 100644 index 0000000000..cd04898c05 --- /dev/null +++ b/example/minimalApplicationCross/app/test/src/ExampleTests.scala @@ -0,0 +1,33 @@ +package app +import io.undertow.Undertow + +import utest._ + +object ExampleTests extends TestSuite{ + def withServer[T](example: cask.main.Main)(f: String => T): T = { + val server = Undertow.builder + .addHttpListener(8081, "localhost") + .setHandler(example.defaultHandler) + .build + server.start() + val res = + try f("http://localhost:8081") + finally server.stop() + res + } + + val tests = Tests { + test("MinimalApplication") - withServer(MinimalApplication) { host => + val success = requests.get(host) + + success.text() ==> "Hello World!" + success.statusCode ==> 200 + + requests.get(s"$host/doesnt-exist", check = false).statusCode ==> 404 + + requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh" + + requests.delete(s"$host/do-thing", check = false).statusCode ==> 405 + } + } +} diff --git a/example/minimalApplicationCross/package.mill b/example/minimalApplicationCross/package.mill new file mode 100644 index 0000000000..c693a73a16 --- /dev/null +++ b/example/minimalApplicationCross/package.mill @@ -0,0 +1,18 @@ +package build.example.minimalApplicationCross +import mill._, scalalib._ + +object app extends Cross[AppModule](build.scalaVersions) +trait AppModule extends CrossScalaModule{ + + def moduleDeps = Seq(build.cask(crossScalaVersion)) + + def mvnDeps = Seq[Dep]( + ) + object test extends ScalaTests with TestModule.Utest{ + + def mvnDeps = Seq( + mvn"com.lihaoyi::utest::0.8.4", + mvn"com.lihaoyi::requests::0.9.0", + ) + } +} diff --git a/example/todo/package.mill b/example/todo/package.mill index cb23224082..e45e7c899f 100644 --- a/example/todo/package.mill +++ b/example/todo/package.mill @@ -1,14 +1,14 @@ package build.example.todo import mill._, scalalib._ -object app extends Cross[AppModule](build.scala213) +object app extends Cross[AppModule](build.scala3Latest) trait AppModule extends CrossScalaModule{ - def moduleDeps = Seq(build.cask(crossScalaVersion)) + def moduleDeps = Seq(build.cask(build.scala3)) def mvnDeps = Seq[Dep]( mvn"org.xerial:sqlite-jdbc:3.42.0.0", - mvn"com.lihaoyi::scalasql:0.1.0", + mvn"com.lihaoyi::scalasql:0.2.2", mvn"com.lihaoyi::scalatags:0.12.0", mvn"org.slf4j:slf4j-simple:1.7.30", ) diff --git a/example/todoDb/app/src/TodoMvcDb.scala b/example/todoDb/app/src/TodoMvcDb.scala index 413db40bfb..d715008251 100644 --- a/example/todoDb/app/src/TodoMvcDb.scala +++ b/example/todoDb/app/src/TodoMvcDb.scala @@ -24,7 +24,7 @@ object TodoMvcDb extends cask.MainRoutes{ case class Todo[T[_]](id: T[Int], checked: T[Boolean], text: T[String]) object Todo extends scalasql.Table[Todo]{ - implicit def todoRW = upickle.default.macroRW[Todo[Sc]] + given todoRW: upickle.default.ReadWriter[Todo[Sc]] = upickle.default.macroRW[Todo[Sc]] } sqliteClient.getAutoCommitClientConnection.updateRaw( diff --git a/example/todoDb/app/test/src/ExampleTests.scala b/example/todoDb/app/test/src/ExampleTests.scala index 0446a86bad..5ff1b149b3 100644 --- a/example/todoDb/app/test/src/ExampleTests.scala +++ b/example/todoDb/app/test/src/ExampleTests.scala @@ -36,7 +36,7 @@ object ExampleTests extends TestSuite{ requests.post(s"$host/add", data = "new Task") // Make sure endpoint failures do not commit their transaction - requests.post(s"$host/add", data = "FORCE FAILURE", check = false).statusCode ==> 500 + requests.post(s"$host/add", data = ("FORCE FAILURE": requests.RequestBlob), check = false).statusCode ==> 500 requests.get(s"$host/list/active").text() ==> """[{"id":3,"checked":false,"text":"new Task"}]""" diff --git a/example/todoDb/package.mill b/example/todoDb/package.mill index 7561877844..feb02276f3 100644 --- a/example/todoDb/package.mill +++ b/example/todoDb/package.mill @@ -1,14 +1,14 @@ package build.example.todoDb import mill._, scalalib._ -object app extends Cross[AppModule](build.scala213) +object app extends Cross[AppModule](build.scala3Latest) trait AppModule extends CrossScalaModule{ - def moduleDeps = Seq(build.cask(crossScalaVersion)) + def moduleDeps = Seq(build.cask(build.scala3)) def mvnDeps = Seq[Dep]( mvn"org.xerial:sqlite-jdbc:3.42.0.0", - mvn"com.lihaoyi::scalasql:0.1.0", + mvn"com.lihaoyi::scalasql:0.2.2", ) object test extends ScalaTests with TestModule.Utest{ diff --git a/example/todoDbWithLoom/app/src/TodoMvcDbWithLoom.scala b/example/todoDbWithLoom/app/src/TodoMvcDbWithLoom.scala index 98210fbd8e..394ebc7b52 100644 --- a/example/todoDbWithLoom/app/src/TodoMvcDbWithLoom.scala +++ b/example/todoDbWithLoom/app/src/TodoMvcDbWithLoom.scala @@ -31,7 +31,7 @@ object TodoMvcDbWithLoom extends cask.MainRoutes { case class Todo[T[_]](id: T[Int], checked: T[Boolean], text: T[String]) object Todo extends scalasql.Table[Todo]{ - implicit def todoRW = upickle.default.macroRW[Todo[Sc]] + given todoRW: upickle.default.ReadWriter[Todo[Sc]] = upickle.default.macroRW[Todo[Sc]] } sqliteClient.getAutoCommitClientConnection.updateRaw( diff --git a/example/todoDbWithLoom/app/test/src/ExampleTests.scala b/example/todoDbWithLoom/app/test/src/ExampleTests.scala index 3882a97ab2..8bbf3dab90 100644 --- a/example/todoDbWithLoom/app/test/src/ExampleTests.scala +++ b/example/todoDbWithLoom/app/test/src/ExampleTests.scala @@ -36,7 +36,7 @@ object ExampleTests extends TestSuite{ requests.post(s"$host/add", data = "new Task") // Make sure endpoint failures do not commit their transaction - requests.post(s"$host/add", data = "FORCE FAILURE", check = false).statusCode ==> 500 + requests.post(s"$host/add", data = ("FORCE FAILURE": requests.RequestBlob), check = false).statusCode ==> 500 requests.get(s"$host/list/active").text() ==> """[{"id":3,"checked":false,"text":"new Task"}]""" diff --git a/example/todoDbWithLoom/package.mill b/example/todoDbWithLoom/package.mill index 18dc7b32e9..563619781e 100644 --- a/example/todoDbWithLoom/package.mill +++ b/example/todoDbWithLoom/package.mill @@ -2,7 +2,7 @@ package build.example.todoDbWithLoom import mill._, scalalib._ import mill.api.ModuleRef -object app extends Cross[AppModule](build.scala213) +object app extends Cross[AppModule](build.scala3Latest) trait AppModule extends CrossScalaModule{ private def parseJvmArgs(argsStr: String) = { @@ -28,11 +28,11 @@ trait AppModule extends CrossScalaModule{ def jvmId = "temurin:23.0.1" def jvmIndexVersion = "latest.release" - def moduleDeps = Seq(build.cask(crossScalaVersion)) + def moduleDeps = Seq(build.cask(build.scala3)) def mvnDeps = Seq[Dep]( mvn"org.xerial:sqlite-jdbc:3.42.0.0", - mvn"com.lihaoyi::scalasql:0.1.0", + mvn"com.lihaoyi::scalasql:0.2.2", ) object test extends ScalaTests with TestModule.Utest { @@ -42,4 +42,3 @@ trait AppModule extends CrossScalaModule{ ) } } - From 829ab9022e41c871b61d7f73d04e3d225870509a Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 18 Oct 2025 14:54:42 +0200 Subject: [PATCH 3/3] build MinimalExampleCross --- build.mill | 1 + example/minimalApplicationCross/app/test/src/ExampleTests.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.mill b/build.mill index b97fb5d6b7..8f4836d07f 100644 --- a/build.mill +++ b/build.mill @@ -207,6 +207,7 @@ def zippedExamples = Task { build.example.formJsonPost.moduleDir, build.example.httpMethods.moduleDir, build.example.minimalApplication.moduleDir, + build.example.minimalApplicationCross.moduleDir, build.example.minimalApplication2.moduleDir, build.example.minimalApplicationWithLoom.moduleDir, build.example.redirectAbort.moduleDir, diff --git a/example/minimalApplicationCross/app/test/src/ExampleTests.scala b/example/minimalApplicationCross/app/test/src/ExampleTests.scala index cd04898c05..32322f3e7c 100644 --- a/example/minimalApplicationCross/app/test/src/ExampleTests.scala +++ b/example/minimalApplicationCross/app/test/src/ExampleTests.scala @@ -17,7 +17,7 @@ object ExampleTests extends TestSuite{ } val tests = Tests { - test("MinimalApplication") - withServer(MinimalApplication) { host => + test("MinimalApplication") - withServer(MinimalApplicationCross) { host => val success = requests.get(host) success.text() ==> "Hello World!"