diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7a25fe..51a2dcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,39 +1,70 @@ -name: routing +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Continuous Integration + on: - push: - branches: - - master pull_request: - branches: - - master + branches: [master] + push: + branches: [master] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + jobs: - test: - runs-on: ubuntu-latest + build: + name: Build and Test strategy: fail-fast: false matrix: - java: [11, 17, 21] + os: [ubuntu-latest] + scala: [2.12.20] + java: [temurin@11, temurin@17, temurin@21] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Setup Java ${{ matrix.java }} - uses: actions/setup-java@v2 + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v4 with: distribution: temurin - java-version: ${{ matrix.java }} + java-version: 11 + cache: sbt - - name: Cache sbt - uses: actions/cache@v2 + - name: Setup Java (temurin@17) + if: matrix.java == 'temurin@17' + uses: actions/setup-java@v4 with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - - name: sbt test + distribution: temurin + java-version: 17 + cache: sbt + + - name: Setup Java (temurin@21) + if: matrix.java == 'temurin@21' + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + cache: sbt + + - name: Setup sbt + uses: sbt/setup-sbt@v1 + + - name: Check that workflows are up to date + run: sbt '++ ${{ matrix.scala }}' githubWorkflowCheck + + - name: Build project run: sbt test - - name: sbt mdoc + - name: Build docs + if: matrix.java == 'temurin@21' run: sbt mdoc diff --git a/.github/workflows/clean.yml b/.github/workflows/clean.yml new file mode 100644 index 0000000..bfc865d --- /dev/null +++ b/.github/workflows/clean.yml @@ -0,0 +1,60 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Clean + +on: push + +jobs: + delete-artifacts: + name: Delete Artifacts + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Delete artifacts + shell: bash {0} + run: | + # Customize those three lines with your repository and credentials: + REPO=${GITHUB_API_URL}/repos/${{ github.repository }} + + # A shortcut to call GitHub API. + ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } + + # A temporary file which receives HTTP response headers. + TMPFILE=$(mktemp) + + # An associative array, key: artifact name, value: number of artifacts of that name. + declare -A ARTCOUNT + + # Process all artifacts on this repository, loop on returned "pages". + URL=$REPO/actions/artifacts + while [[ -n "$URL" ]]; do + + # Get current page, get response headers in a temporary file. + JSON=$(ghapi --dump-header $TMPFILE "$URL") + + # Get URL of next page. Will be empty if we are at the last page. + URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') + rm -f $TMPFILE + + # Number of artifacts on this page: + COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) + + # Loop on all artifacts on this page. + for ((i=0; $i < $COUNT; i++)); do + + # Get name of artifact and count instances of this name. + name=$(jq <<<$JSON -r ".artifacts[$i].name?") + ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) + + id=$(jq <<<$JSON -r ".artifacts[$i].id?") + size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) + printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size + ghapi -X DELETE $REPO/actions/artifacts/$id + done + done diff --git a/bench/src/main/scala/routing/bench/Http4sBenchmark.scala b/bench/src/main/scala/routing/bench/Http4sBenchmark.scala index ecce65a..9048cd3 100644 --- a/bench/src/main/scala/routing/bench/Http4sBenchmark.scala +++ b/bench/src/main/scala/routing/bench/Http4sBenchmark.scala @@ -2,7 +2,7 @@ package routing package bench import cats.effect.IO -import cats.effect.unsafe.implicits.global // 0.23, 1.0.0-M41 +import cats.effect.unsafe.implicits.global import org.http4s._ import org.http4s.dsl.io._ import org.openjdk.jmh.annotations._ @@ -36,9 +36,8 @@ object http4sHelper extends BenchmarkHelper[Request[IO], Request[IO] => IO[Respo runIO(routes.run(request).value.flatMap(_.get.as[String])) } -class Http4sBenchmark_0_22 { // 0.22 class Http4sBenchmark_0_23 { // 0.23 -class Http4sBenchmark_1_0_0_M41 { // 1.0.0-M41 +class Http4sBenchmark_1_0_0_M44 { // 1.0.0-M44 import http4sHelper._ @Benchmark def http4s: String = run(http4sService) diff --git a/bench/src/main/scala/routing/bench/PlayBenchmark.scala b/bench/src/main/scala/routing/bench/PlayBenchmark.scala index dd3bcdd..011a181 100644 --- a/bench/src/main/scala/routing/bench/PlayBenchmark.scala +++ b/bench/src/main/scala/routing/bench/PlayBenchmark.scala @@ -1,4 +1,4 @@ -// ++ 1.0.0-M41 +// ++ 1.0.0-M44 package routing package bench @@ -17,4 +17,4 @@ class PlayBenchmark { @Benchmark def routing: String = run(routingService) @Benchmark def routingManual: String = run(routingManualService) } -// -- 1.0.0-M41 +// -- 1.0.0-M44 diff --git a/build.sbt b/build.sbt index 91a46e9..1065a8d 100644 --- a/build.sbt +++ b/build.sbt @@ -7,6 +7,22 @@ Global / onChangedBuildSource := ReloadOnSourceChanges noPublishSettings +// GitHub Actions config +val javaVersions = Seq(11, 17, 21).map(v => JavaSpec.temurin(v.toString)) + +ThisBuild / githubWorkflowJavaVersions := javaVersions +ThisBuild / githubWorkflowArtifactUpload := false +ThisBuild / githubWorkflowBuildMatrixFailFast := Some(false) +ThisBuild / githubWorkflowTargetBranches := Seq("master") +ThisBuild / githubWorkflowPublishTargetBranches := Seq() + +def isJava(v: Int) = s"matrix.java == '${javaVersions.find(_.version == v.toString).get.render}'" + +ThisBuild / githubWorkflowBuild := Seq( + WorkflowStep.Run(List("sbt test"), name = Some("Build project")), + WorkflowStep.Run(List("sbt mdoc"), name = Some("Build docs"), cond = Some(isJava(21))), +) + lazy val core = simpleProj(projectMatrix.in(file("core")), "core", List( Platform.Jvm, Platform.Js, @@ -75,16 +91,6 @@ lazy val docs = http4sProj(projectMatrix.in(file("routing-docs")), "routing-docs "GITHUB_BLOB_URL" -> s"$githubRepoUrl/blob/master", "HTTP4S_SUFFIX" -> axis.suffix, "HTTP4S_VERSION_COMMENT" -> axis.comment, - "HTTP4S_PATH_CODE" -> (axis match { - case Http4sAxis.v0_22 | - Http4sAxis.v0_23 | - Http4sAxis.v1_0_0_M41 => - "Uri.Path.unsafeFromString(path)" - }), - "HTTP4S_UNSAFERUNSYNC_IMPORT" -> (axis match { - case Http4sAxis.v0_23 | Http4sAxis.v1_0_0_M41 => "import cats.effect.unsafe.implicits.global\n" - case _ => "" - }), "PLAY_LATEST_DEPENDENCY" -> playDepString(PlayAxis.v3_0), "PLAY_SUPPORTED_VERSIONS" -> PlayAxis.all.map(a => s"- ${a.version} -- `${playDepString(a)}`").mkString("\n"), ), @@ -139,8 +145,8 @@ lazy val example = http4sProj(projectMatrix.in(file("example")), "example", _ => libraryDependencies ++= Seq( http4sDep("circe", axis.version).value, http4sDep("blaze-server", axis match { - case Http4sAxis.v0_23 => s"${axis.suffix}.12" - case Http4sAxis.v1_0_0_M41 => s"${axis.suffix.dropRight(2)}38" + case Http4sAxis.v0_23 => s"${axis.suffix}.17" + case Http4sAxis.v1_0_0_M44 => s"${axis.suffix.dropRight(2)}41" case _ => axis.version }).value, ), diff --git a/docs/implementations/http4s.md b/docs/implementations/http4s.md index 8a999fe..4855afc 100644 --- a/docs/implementations/http4s.md +++ b/docs/implementations/http4s.md @@ -63,7 +63,8 @@ val service2: HttpRoutes[IO] = HttpRoutes.of { You can confirm that routes are matched correctly by passing some test requests to the service: ```scala mdoc -@HTTP4S_UNSAFERUNSYNC_IMPORT@import org.http4s.Request +import cats.effect.unsafe.implicits.global +import org.http4s.Request def testRoute(service: HttpRoutes[IO], call: Call): String = service @@ -88,7 +89,7 @@ You can also check that requests matching none of your routes are not handled by import org.http4s.{Method, Uri} def unhandled(method: Method, path: String) = - service1.run(Request[IO](method = method, uri = Uri(path = @HTTP4S_PATH_CODE@))).value.unsafeRunSync() + service1.run(Request[IO](method = method, uri = Uri(path = Uri.Path.unsafeFromString(path)))).value.unsafeRunSync() unhandled(GET, "/fake") diff --git a/docs/implementations/play.md b/docs/implementations/play.md index 3707fff..1698c32 100644 --- a/docs/implementations/play.md +++ b/docs/implementations/play.md @@ -14,8 +14,6 @@ The currently supported versions of Play are: @PLAY_SUPPORTED_VERSIONS@ -**Note:** Java 8 is only supported on Play 2.8.x - Play handlers will be of the shape `Params => play.api.mvc.Handler` First let's rebuild our example routes. diff --git a/example/src/main/scala/routing/example/App.scala b/example/src/main/scala/routing/example/App.scala index 0ef26cb..e14f16e 100644 --- a/example/src/main/scala/routing/example/App.scala +++ b/example/src/main/scala/routing/example/App.scala @@ -2,12 +2,17 @@ package routing.example import cats.effect.{ExitCode, IO, IOApp} import org.http4s.blaze.server.BlazeServerBuilder -import scala.concurrent.ExecutionContext // 0.22 object App extends IOApp { + // ++ 1.0.0-M44 + import org.typelevel.log4cats.LoggerFactory + import org.typelevel.log4cats.noop.NoOpFactory + + private implicit val loggerFactory: LoggerFactory[IO] = NoOpFactory[IO] + // -- 1.0.0-M44 + override def run(args: List[String]): IO[ExitCode] = - BlazeServerBuilder[IO](ExecutionContext.global) // 0.22 - BlazeServerBuilder[IO] // 0.23, 1.0.0-M41 + BlazeServerBuilder[IO] .bindHttp(8080, "localhost") .withHttpApp(Controller.actions.orNotFound) .serve diff --git a/http4s/src/main/scala/routing/http4s/Syntax.scala b/http4s/src/main/scala/routing/http4s/Syntax.scala index 3dac79a..23f6ca5 100644 --- a/http4s/src/main/scala/routing/http4s/Syntax.scala +++ b/http4s/src/main/scala/routing/http4s/Syntax.scala @@ -1,9 +1,7 @@ package routing package http4s -import cats.Applicative -import cats.Defer // 0.22 -import cats.Monad // 0.23, 1.0.0-M41 +import cats.{Applicative, Monad} import cats.data.OptionT import org.{http4s => h} import scala.annotation.tailrec @@ -45,10 +43,7 @@ object syntax { } implicit class Http4sRouteObjectOps(private val route: Route.type) extends AnyVal { - def httpRoutes[F[_]: Applicative: Defer]( // 0.22 - def httpRoutes[F[_]: Monad]( // 0.23, 1.0.0-M41 - handlers: Handled[h.Request[F] => F[h.Response[F]]]* - ): h.HttpRoutes[F] = + def httpRoutes[F[_]: Monad](handlers: Handled[h.Request[F] => F[h.Response[F]]]*): h.HttpRoutes[F] = h.HttpRoutes[F](tryRoutes(_, handlers.toList)) } } diff --git a/project/Build.scala b/project/Build.scala index 0932654..7ba6649 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -10,7 +10,7 @@ import sbtprojectmatrix.ProjectMatrixPlugin.autoImport._ import scala.sys.process._ object Build { - lazy val scalaVersions = Seq("2.13.13", "3.3.3") + lazy val scalaVersions = Seq("2.13.15", "3.3.4") lazy val latestScalaV = scalaVersions.find(_.startsWith("3.")).get lazy val kindProjector = compilerPlugin("org.typelevel" % "kind-projector" % "0.13.3" cross CrossVersion.full) @@ -32,8 +32,8 @@ object Build { output: List[String] ) - val startVersionBlock = "// ++ " - val endVersionBlock = "// -- " + val startVersionBlock = """^\s*// \+\+ (.*)$""".r + val endVersionBlock = """^\s*// -- (.*)$""".r val versionBlockComments = List(startVersionBlock, endVersionBlock) val singleLineVersionComment = """^([^/]+) // (.*)$""".r @@ -47,17 +47,15 @@ object Build { Nil ))((acc, line) => (acc.currVersions, line) match { // Start a version block with a line matching `// ++ $version` - case (Nil, l) if l.startsWith(startVersionBlock) => - acc.copy( - currVersions = splitVersion(l.stripPrefix(startVersionBlock)), - output = (acc.output :+ "")) + case (Nil, startVersionBlock(v)) => + acc.copy(currVersions = splitVersion(v), output = (acc.output :+ "")) // End a version block with a line matching `// -- $version` - case (vs @ (_ :: _), l) if vs.exists(v => l == endVersionBlock ++ v) => + case (vs @ (_ :: _), endVersionBlock(v)) if vs.exists(_ == v) => acc.copy(currVersions = Nil, output = (acc.output :+ "")) // Fail on attempts at nested version blocks - case (_ :: _, l) if versionBlockComments.exists(l.startsWith(_)) => + case (_ :: _, startVersionBlock(_) | endVersionBlock(_)) => sys.error("Cannot nest version blocks") // Decide whether to keep a version line within a version block by checking @@ -228,11 +226,10 @@ object Build { } val defaultHttp4sPlatforms = (_: Http4sAxis.Value) match { - case Http4sAxis.v0_22 => List(Platform.Jvm) - case Http4sAxis.v0_23 | Http4sAxis.v1_0_0_M41 => List(Platform.Jvm, Platform.Js) + case Http4sAxis.v0_23 | Http4sAxis.v1_0_0_M44 => List(Platform.Jvm, Platform.Js) } val defaultHttp4sScalaVersions = (axis: Http4sAxis.Value) => (_: Platform) => axis match { - case Http4sAxis.v0_22 | Http4sAxis.v0_23 | Http4sAxis.v1_0_0_M41 => identity[Seq[String]] _ + case Http4sAxis.v0_23 | Http4sAxis.v1_0_0_M44 => identity[Seq[String]] _ } lazy val http4sProj = LibAxesProj(Http4sAxis.all)( @@ -246,7 +243,7 @@ object Build { val defaultPlayPlatforms = (_: PlayAxis.Value) => List(Platform.Jvm) val defaultPlayScalaVersions = (axis: PlayAxis.Value) => (_: Platform) => axis match { - case PlayAxis.v2_8 | PlayAxis.v2_9 | PlayAxis.v3_0 => identity[Seq[String]] _ + case PlayAxis.v2_9 | PlayAxis.v3_0 => identity[Seq[String]] _ } lazy val playProj = LibAxesProj(PlayAxis.all)( @@ -258,7 +255,7 @@ object Build { defaultPlayScalaVersions, ) - val scalacheckVersion = "1.17.0" + val scalacheckVersion = "1.18.1" val scalacheckDep = Def.setting("org.scalacheck" %%% "scalacheck" % scalacheckVersion) val testSettings = Seq( @@ -269,8 +266,8 @@ object Build { .getOrElse(Seq()) ) - val catsCore = Def.setting("org.typelevel" %%% "cats-core" % "2.10.0") - val izumiReflect = Def.setting("dev.zio" %%% "izumi-reflect" % "2.3.8") + val catsCore = Def.setting("org.typelevel" %%% "cats-core" % "2.12.0") + val izumiReflect = Def.setting("dev.zio" %%% "izumi-reflect" % "2.3.10") val http4sV1Milestone = "1.0.0-M" object Http4sAxis extends Enumeration { @@ -286,9 +283,8 @@ object Build { implicit def valueToHAVal(v: Value): HAVal = v.asInstanceOf[HAVal] implicit def valueToVirtualAxis(v: Value): VirtualAxis.WeakAxis = v.axis - val v0_22 = HAVal("0.22", "0.22.15", "latest stable on cats effect 2") - val v0_23 = HAVal("0.23", "0.23.26", "latest stable on cats effect 3") - val v1_0_0_M41 = HAVal(s"${http4sV1Milestone}41", s"${http4sV1Milestone}41", "latest development on cats effect 3") + val v0_23 = HAVal("0.23", "0.23.30", "latest stable on cats effect 3") + val v1_0_0_M44 = HAVal(s"${http4sV1Milestone}44", s"${http4sV1Milestone}44", "latest development on cats effect 3") lazy val all = values.toList } @@ -308,9 +304,8 @@ object Build { implicit def valueToPAVal(v: Value): PAVal = v.asInstanceOf[PAVal] implicit def valueToVirtualAxis(v: Value): VirtualAxis.WeakAxis = v.axis - val v3_0 = PAVal("3.0", "3.0.0", proj => version => Def.setting("org.playframework" %%% proj % version)) - val v2_9 = PAVal("2.9", "2.9.0", proj => version => Def.setting("com.typesafe.play" %%% proj % version)) - val v2_8 = PAVal("2.8", "2.8.20", proj => version => Def.setting("com.typesafe.play" %%% proj % version cross CrossVersion.for3Use2_13)) + val v3_0 = PAVal("3.0", "3.0.6", proj => version => Def.setting("org.playframework" %%% proj % version)) + val v2_9 = PAVal("2.9", "2.9.6", proj => version => Def.setting("com.typesafe.play" %%% proj % version)) lazy val all = values.toList } diff --git a/project/build.properties b/project/build.properties index 04267b1..e88a0d8 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.6 diff --git a/project/plugins.sbt b/project/plugins.sbt index 9cac769..526c5f8 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,10 @@ -addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") -addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.0") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.1") +addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.24.0") addSbtPlugin("io.spray" % "sbt-revolver" % "0.10.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16") -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.2" ) +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.6") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1" ) +addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.2") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") resolvers += "bondlink-maven-repo" at "https://raw.githubusercontent.com/mblink/maven-repo/main" diff --git a/website/i18n/en.json b/website/i18n/en.json index 34216a9..c559278 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -14,9 +14,6 @@ "implementations": { "title": "Choosing an implementation" }, - "implementations/http4s-0.22": { - "title": "http4s 0.22 (latest stable on cats effect 2)" - }, "implementations/http4s-0.23": { "title": "http4s 0.23 (latest stable on cats effect 3)" }, diff --git a/website/sidebars.json b/website/sidebars.json index 3d1c7d1..8358124 100755 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -9,9 +9,8 @@ "label": "Implementations", "ids": [ "implementations", - "implementations/http4s-0.22", "implementations/http4s-0.23", - "implementations/http4s-1.0.0-M41", + "implementations/http4s-1.0.0-M44", "implementations/play" ] },