From bb2e3d43338e50afd6d1e3d351c618d22fbd1120 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Wed, 2 Jun 2021 13:06:07 +0200 Subject: [PATCH 1/5] Applied Scalafix rule(s) github:typelevel/cats-effect/v3_0_0?sha=series/3.x Update cats-effect to 3.1.1 Cats Effect 3 migration Bump circe, caliban-gitlab Update tapir, circe-generic-extras, set dependency scheme for circe --- build.sbt | 57 ++++++----- core/src/main/scala/io/pg/Prelude.scala | 16 --- .../scala/io/pg/background/background.scala | 6 +- .../scala/io/pg/messaging/messaging.scala | 9 +- .../src/main/scala/io/pg/gitlab/Gitlab.scala | 6 +- .../org/slf4j/impl/StaticLoggerBinder.java | 3 +- src/main/scala/io/pg/Application.scala | 99 +++++++++---------- src/main/scala/io/pg/Main.scala | 44 ++++++--- src/main/scala/io/pg/MergeRequests.scala | 4 +- src/main/scala/io/pg/OdinInterop.scala | 16 +-- src/main/scala/io/pg/actions.scala | 2 +- src/main/scala/io/pg/appconfig.scala | 19 ++-- .../scala/io/pg/config/ProjectConfig.scala | 2 +- src/main/scala/io/pg/resolver.scala | 2 +- src/main/scala/io/pg/webhook/webhook.scala | 3 +- .../scala/io/pg/WebhookProcessorTest.scala | 4 +- src/test/scala/io/pg/fakes/FakeUtils.scala | 2 +- .../io/pg/fakes/ProjectActionsStateFake.scala | 27 +++-- .../io/pg/fakes/ProjectConfigReaderFake.scala | 9 +- 19 files changed, 164 insertions(+), 166 deletions(-) diff --git a/build.sbt b/build.sbt index 9e647ff5..15e92298 100644 --- a/build.sbt +++ b/build.sbt @@ -56,6 +56,13 @@ ThisBuild / missinglinkExcludedDependencies += moduleFilter( name = "slf4j-api" ) +ThisBuild / libraryDependencySchemes ++= Seq( + "io.circe" %% "circe-core" % "early-semver", + "io.circe" %% "circe-generic-extras" % "early-semver", + "io.circe" %% "circe-literal" % "early-semver", + "io.circe" %% "circe-parser" % "early-semver" +) + def crossPlugin(x: sbt.librarymanagement.ModuleID) = compilerPlugin(x.cross(CrossVersion.full)) @@ -71,16 +78,16 @@ val commonSettings = List( scalacOptions += "-Ymacro-annotations", libraryDependencies ++= List( "org.typelevel" %% "cats-core" % "2.6.1", - "org.typelevel" %% "cats-effect" % "2.5.1", + "org.typelevel" %% "cats-effect" % "3.1.1", "org.typelevel" %% "cats-tagless-macros" % "0.14.0", - "co.fs2" %% "fs2-core" % "2.5.6", - "com.github.valskalla" %% "odin-core" % "0.11.0", - "io.circe" %% "circe-core" % "0.13.0", + "co.fs2" %% "fs2-core" % "3.0.4", + "com.github.valskalla" %% "odin-core" % "0.11.0+7-4b29a367-SNAPSHOT", + "io.circe" %% "circe-core" % "0.14.1", "com.github.julien-truffaut" %% "monocle-macro" % "2.1.0", - "com.disneystreaming" %% "weaver-framework" % "0.5.1" % Test, - "com.disneystreaming" %% "weaver-scalacheck" % "0.5.1" % Test + "com.disneystreaming" %% "weaver-cats" % "0.7.3" % Test, + "com.disneystreaming" %% "weaver-scalacheck" % "0.7.3" % Test ) ++ compilerPlugins, - testFrameworks += new TestFramework("weaver.framework.TestFramework"), + testFrameworks += new TestFramework("weaver.framework.CatsEffect"), publish / skip := true ) @@ -88,14 +95,14 @@ lazy val gitlab = project .settings( commonSettings, libraryDependencies ++= List( - "is.cir" %% "ciris" % "1.2.1", - "com.kubukoz" %% "caliban-gitlab" % "0.0.14", - "io.circe" %% "circe-generic-extras" % "0.13.0", - "io.circe" %% "circe-parser" % "0.13.0" % Test, - "io.circe" %% "circe-literal" % "0.13.0" % Test, - "com.softwaremill.sttp.tapir" %% "tapir-core" % "0.17.19", - "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.17.19", - "com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "0.17.19" + "is.cir" %% "ciris" % "2.0.1", + "com.kubukoz" %% "caliban-gitlab" % "0.1.0", + "io.circe" %% "circe-generic-extras" % "0.14.1", + "io.circe" %% "circe-parser" % "0.14.1" % Test, + "io.circe" %% "circe-literal" % "0.14.1" % Test, + "com.softwaremill.sttp.tapir" %% "tapir-core" % "0.18.0-M15", + "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % "0.18.0-M15", + "com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "0.18.0-M15" ) ) .dependsOn(core) @@ -181,19 +188,19 @@ lazy val pitgull = buildInfoPackage := "io.pg", buildInfoKeys := List(version, scalaVersion), libraryDependencies ++= List( - "com.softwaremill.sttp.client3" %% "http4s-ce2-backend" % "3.3.6", - "org.http4s" %% "http4s-dsl" % "0.21.24", - "org.http4s" %% "http4s-circe" % "0.21.24", - "org.http4s" %% "http4s-blaze-server" % "0.21.24", - "org.http4s" %% "http4s-blaze-client" % "0.21.24", - "is.cir" %% "ciris" % "1.2.1", - "io.circe" %% "circe-generic-extras" % "0.13.0", + "com.softwaremill.sttp.client3" %% "http4s-backend" % "3.3.6", + "org.http4s" %% "http4s-dsl" % "0.23.0-RC1", + "org.http4s" %% "http4s-circe" % "0.23.0-RC1", + "org.http4s" %% "http4s-blaze-server" % "0.23.0-RC1", + "org.http4s" %% "http4s-blaze-client" % "0.23.0-RC1", + "is.cir" %% "ciris" % "2.0.1", + "io.circe" %% "circe-generic-extras" % "0.14.0", "io.estatico" %% "newtype" % "0.4.4", "io.scalaland" %% "chimney" % "0.6.1", "io.chrisdavenport" %% "cats-time" % "0.3.4", - "com.github.valskalla" %% "odin-core" % "0.11.0", - "com.github.valskalla" %% "odin-slf4j" % "0.11.0", - "io.github.vigoo" %% "prox-fs2" % "0.7.1" + "com.github.valskalla" %% "odin-core" % "0.11.0+7-4b29a367-SNAPSHOT", + "com.github.valskalla" %% "odin-slf4j" % "0.11.0+7-4b29a367-SNAPSHOT", + "io.github.vigoo" %% "prox-fs2-3" % "0.7.1" ) ) .dependsOn(core, gitlab) diff --git a/core/src/main/scala/io/pg/Prelude.scala b/core/src/main/scala/io/pg/Prelude.scala index e9109f5a..439a31e4 100644 --- a/core/src/main/scala/io/pg/Prelude.scala +++ b/core/src/main/scala/io/pg/Prelude.scala @@ -1,22 +1,6 @@ package io.pg -import cats.effect.Resource -import cats.syntax.functor._ -import cats.Applicative -import cats.MonadError - object Prelude { - type MonadThrow[F[_]] = MonadError[F, Throwable] - - implicit class EffectToResourceLiftSyntax[F[_], A](private val fa: F[A]) extends AnyVal { - - def resource(implicit F: Applicative[F]): Resource[F, A] = - Resource.eval(fa) - - def resource_(implicit F: Applicative[F]): Resource[F, Unit] = - fa.void.resource - - } implicit class AnythingAnything[A](private val a: A) extends AnyVal { def ??? : Nothing = ??? diff --git a/core/src/main/scala/io/pg/background/background.scala b/core/src/main/scala/io/pg/background/background.scala index 8e569e61..d47125b0 100644 --- a/core/src/main/scala/io/pg/background/background.scala +++ b/core/src/main/scala/io/pg/background/background.scala @@ -15,13 +15,13 @@ object BackgroundProcess { final case class DrainStream[F[_], G[_]]( stream: fs2.Stream[F, Nothing], - C: fs2.Stream.Compiler[F, G] + C: fs2.Compiler[F, G] ) extends BackgroundProcess[G] def fromStream[F[_], G[_]]( stream: fs2.Stream[F, _] )( - implicit C: fs2.Stream.Compiler[F, G] + implicit C: fs2.Compiler[F, G] ): BackgroundProcess[G] = new DrainStream(stream.drain, C) @@ -30,7 +30,7 @@ object BackgroundProcess { )( processor: Processor[F, A] )( - implicit C: fs2.Stream.Compiler[F, F] + implicit C: fs2.Compiler[F, F] ): BackgroundProcess[F] = fromStream(channel.consume.through(processor.process)) diff --git a/core/src/main/scala/io/pg/messaging/messaging.scala b/core/src/main/scala/io/pg/messaging/messaging.scala index c15be1d0..5588d18e 100644 --- a/core/src/main/scala/io/pg/messaging/messaging.scala +++ b/core/src/main/scala/io/pg/messaging/messaging.scala @@ -1,11 +1,12 @@ package io.pg.messaging -import fs2.concurrent.Queue +import cats.effect.std.Queue import scala.reflect.ClassTag import cats.tagless.autoInvariant import cats.syntax.all._ import cats.ApplicativeError import io.odin.Logger +import cats.Functor trait Publisher[F[_], -A] { def publish(a: A): F[Unit] @@ -41,10 +42,10 @@ trait Channel[F[_], A] extends Publisher[F, A] { self => object Channel { - def fromQueue[F[_], A](q: Queue[F, A]): Channel[F, A] = + def fromQueue[F[_]: Functor, A](q: Queue[F, A]): Channel[F, A] = new Channel[F, A] { - def publish(a: A): F[Unit] = q.enqueue1(a) - val consume: fs2.Stream[F, A] = q.dequeue + def publish(a: A): F[Unit] = q.offer(a) + val consume: fs2.Stream[F, A] = fs2.Stream.fromQueueUnterminated(q) } implicit class ChannelOpticsSyntax[F[_], A](val ch: Channel[F, A]) extends AnyVal { diff --git a/gitlab/src/main/scala/io/pg/gitlab/Gitlab.scala b/gitlab/src/main/scala/io/pg/gitlab/Gitlab.scala index fa47e913..abc5bfad 100644 --- a/gitlab/src/main/scala/io/pg/gitlab/Gitlab.scala +++ b/gitlab/src/main/scala/io/pg/gitlab/Gitlab.scala @@ -21,7 +21,7 @@ import io.pg.gitlab.graphql.PipelineStatusEnum import io.pg.gitlab.graphql.Project import io.pg.gitlab.graphql.ProjectConnection import io.pg.gitlab.graphql.Query -import io.pg.gitlab.graphql.User +import io.pg.gitlab.graphql.UserCore import sttp.client3.Request import sttp.client3.SttpBackend import sttp.model.Uri @@ -80,7 +80,7 @@ object Gitlab { accessToken: Secret[String] )( implicit backend: SttpBackend[F, Any], - SC: fs2.Stream.Compiler[F, F] + SC: fs2.Compiler[F, F] ): Gitlab[F] = { def runRequest[O](request: Request[O, Any]): F[O] = @@ -139,7 +139,7 @@ object Gitlab { MergeRequest.iid.mapEither(_.toLongOption.toRight(DecodingError("MR IID wasn't a Long"))) ~ MergeRequest.headPipeline(Pipeline.status.map(convertPipelineStatus)) ~ MergeRequest - .author(User.username) + .author(UserCore.username) .mapEither(_.toRight(DecodingError("MR has no author"))) ~ MergeRequest.description ~ MergeRequest.shouldBeRebased ~ diff --git a/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/src/main/java/org/slf4j/impl/StaticLoggerBinder.java index 23dfd3e4..f30b38f5 100644 --- a/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ b/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -1,5 +1,5 @@ package org.slf4j.impl; - +/* public class StaticLoggerBinder extends io.pg.OdinInterop { public static String REQUESTED_API_VERSION = "1.7"; @@ -8,3 +8,4 @@ public static StaticLoggerBinder getSingleton() { return _instance; } } + */ diff --git a/src/main/scala/io/pg/Application.scala b/src/main/scala/io/pg/Application.scala index 82448784..40948d41 100644 --- a/src/main/scala/io/pg/Application.scala +++ b/src/main/scala/io/pg/Application.scala @@ -1,12 +1,11 @@ package io.pg import cats.data.NonEmptyList -import cats.effect.Blocker -import cats.effect.ConcurrentEffect -import cats.effect.ContextShift import cats.effect.Resource +import cats.effect.kernel.Async +import cats.effect.std.Queue +import cats.effect.implicits._ import cats.syntax.all._ -import fs2.concurrent.Queue import io.odin.Logger import io.pg.background.BackgroundProcess import io.pg.config.ProjectConfigReader @@ -15,7 +14,7 @@ import io.pg.gitlab.webhook.WebhookEvent import io.pg.messaging._ import io.pg.webhook._ import org.http4s.HttpApp -import org.http4s.client.blaze.BlazeClientBuilder +import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.implicits._ import sttp.capabilities.fs2.Fs2Streams import sttp.client3.SttpBackend @@ -23,8 +22,6 @@ import sttp.client3.http4s.Http4sBackend import scala.concurrent.ExecutionContext -import Prelude._ - sealed trait Event extends Product with Serializable object Event { @@ -38,56 +35,54 @@ final class Application[F[_]]( object Application { - def resource[F[_]: ConcurrentEffect: ContextShift: Logger]( + def resource[F[_]: Logger: Async]( config: AppConfig ): Resource[F, Application[F]] = { implicit val projectConfigReader = ProjectConfigReader.test[F] - Blocker[F].flatMap { blocker => - Queue - .bounded[F, Event](config.queues.maxSize) - .map(Channel.fromQueue) - .resource - .flatMap { eventChannel => - implicit val webhookChannel: Channel[F, WebhookEvent] = - eventChannel.only[Event.Webhook].imap(_.value)(Event.Webhook) - - BlazeClientBuilder[F](ExecutionContext.global) - .resource - .map( - org - .http4s - .client - .middleware - .Logger(logHeaders = true, logBody = false, redactHeadersWhen = config.middleware.sensitiveHeaders.contains) + Queue + .bounded[F, Event](config.queues.maxSize) + .map(Channel.fromQueue(_)) + .toResource + .flatMap { eventChannel => + implicit val webhookChannel: Channel[F, WebhookEvent] = + eventChannel.only[Event.Webhook].imap(_.value)(Event.Webhook) + + BlazeClientBuilder[F](ExecutionContext.global) + .resource + .map( + org + .http4s + .client + .middleware + .Logger(logHeaders = true, logBody = false, redactHeadersWhen = config.middleware.sensitiveHeaders.contains) + ) + .map { client => + implicit val backend: SttpBackend[F, Fs2Streams[F]] = + Http4sBackend.usingClient[F](client) + + implicit val gitlab: Gitlab[F] = + Gitlab.sttpInstance[F](config.git.apiUrl, config.git.apiToken) + + implicit val projectActions: ProjectActions[F] = + ProjectActions.instance[F] + + implicit val stateResolver: StateResolver[F] = + StateResolver.instance[F] + + implicit val mergeRequests: MergeRequests[F] = + MergeRequests.instance[F] + + val webhookProcess = BackgroundProcess.fromProcessor( + webhookChannel + )(Processor.simple(WebhookProcessor.instance[F])) + + new Application[F]( + routes = WebhookRouter.routes[F].orNotFound, + background = NonEmptyList.one(webhookProcess) ) - .map { client => - implicit val backend: SttpBackend[F, Fs2Streams[F]] = - Http4sBackend.usingClient[F](client, blocker) - - implicit val gitlab: Gitlab[F] = - Gitlab.sttpInstance[F](config.git.apiUrl, config.git.apiToken) - - implicit val projectActions: ProjectActions[F] = - ProjectActions.instance[F] - - implicit val stateResolver: StateResolver[F] = - StateResolver.instance[F] - - implicit val mergeRequests: MergeRequests[F] = - MergeRequests.instance[F] - - val webhookProcess = BackgroundProcess.fromProcessor( - webhookChannel - )(Processor.simple(WebhookProcessor.instance[F])) - - new Application[F]( - routes = WebhookRouter.routes[F].orNotFound, - background = NonEmptyList.one(webhookProcess) - ) - } - } - } + } + } } } diff --git a/src/main/scala/io/pg/Main.scala b/src/main/scala/io/pg/Main.scala index 476eacb4..edd98dfa 100644 --- a/src/main/scala/io/pg/Main.scala +++ b/src/main/scala/io/pg/Main.scala @@ -1,32 +1,29 @@ package io.pg -import scala.concurrent.ExecutionContext -import scala.concurrent.duration._ - -import cats.Parallel -import cats.effect.ConcurrentEffect -import cats.effect.ContextShift -import cats.effect.Effect import cats.effect.ExitCode import cats.effect.IO import cats.effect.IOApp import cats.effect.Resource import cats.effect.Sync -import cats.effect.Timer import cats.effect.implicits._ +import cats.effect.kernel.Async +import cats.effect.std.Dispatcher import cats.syntax.all._ +import cats.~> import io.chrisdavenport.cats.time.instances.all._ import io.odin.Level import io.odin.Logger import io.odin.formatter.Formatter -import io.pg.Prelude._ import org.http4s.HttpApp -import org.http4s.server.blaze.BlazeServerBuilder +import org.http4s.blaze.server.BlazeServerBuilder import org.http4s.server.middleware +import scala.concurrent.ExecutionContext +import scala.concurrent.duration._ + object Main extends IOApp { - def mkLogger[F[_]: ConcurrentEffect: Timer: ContextShift]: Resource[F, Logger[F]] = { + def mkLogger[F[_]: Async]: Resource[F, Logger[F]] = { val console = io.odin.consoleLogger[F](formatter = Formatter.colorful).withMinimalLevel(Level.Info).pure[Resource[F, *]] @@ -44,10 +41,25 @@ object Main extends IOApp { console |+| file } .evalTap { logger => - Sync[F].delay(OdinInterop.globalLogger.set(logger.mapK(Effect.toIOK).some)) + Dispatcher[F].use { dispatcher => + Sync[F].delay( + OdinInterop + .globalLogger + .set( + logger + .mapK(new (F ~> IO) { + def apply[A](fa: F[A]): IO[A] = IO.defer { + val (future, cancel) = dispatcher.unsafeToFutureCancelable(fa) + IO.fromFuture(IO(future)).onCancel(IO.fromFuture(IO(cancel()))) + } + }) + .some + ) + ) + } } - def mkServer[F[_]: Logger: ConcurrentEffect: Timer]( + def mkServer[F[_]: Logger: Async]( config: AppConfig, routes: HttpApp[F] ) = { @@ -72,14 +84,14 @@ object Main extends IOApp { def logStarted[F[_]: Logger](meta: MetaConfig) = Logger[F].info("Started application", Map("version" -> meta.version, "scalaVersion" -> meta.scalaVersion)) - def serve[F[_]: ConcurrentEffect: ContextShift: Timer: Parallel](config: AppConfig) = + def serve[F[_]: Async](config: AppConfig) = for { implicit0(logger: Logger[F]) <- mkLogger[F] - _ <- logStarting(config.meta).resource_ + _ <- logStarting(config.meta).toResource resources <- Application.resource[F](config) _ <- mkServer[F](config, resources.routes) _ <- resources.background.parTraverse_(_.run).background - _ <- logStarted(config.meta).resource_ + _ <- logStarted(config.meta).toResource } yield () def run(args: List[String]): IO[ExitCode] = diff --git a/src/main/scala/io/pg/MergeRequests.scala b/src/main/scala/io/pg/MergeRequests.scala index f2ddffb4..58d3250e 100644 --- a/src/main/scala/io/pg/MergeRequests.scala +++ b/src/main/scala/io/pg/MergeRequests.scala @@ -25,7 +25,7 @@ object MergeRequests { import scala.util.chaining._ def instance[F[_]: ProjectConfigReader: StateResolver: Monad: Logger]( - implicit SC: fs2.Stream.Compiler[F, F] + implicit SC: fs2.Compiler[F, F] ): MergeRequests[F] = project => for { config <- ProjectConfigReader[F].readConfig(project) @@ -39,7 +39,7 @@ object MergeRequests { )( states: List[A] )( - implicit SC: fs2.Stream.Compiler[F, F] + implicit SC: fs2.Compiler[F, F] ): F[List[B]] = { def tapLeftAndDrop[L, R](log: L => F[Unit]): Pipe[F, Either[L, R], R] = _.evalTap(_.leftTraverse(log)).map(_.toOption).unNone diff --git a/src/main/scala/io/pg/OdinInterop.scala b/src/main/scala/io/pg/OdinInterop.scala index 6824c3fc..7c01adca 100644 --- a/src/main/scala/io/pg/OdinInterop.scala +++ b/src/main/scala/io/pg/OdinInterop.scala @@ -1,16 +1,18 @@ package io.pg -import java.util.concurrent.atomic.AtomicReference - -import cats.effect.Clock -import cats.effect.Effect import cats.effect.IO +import cats.effect.kernel.Sync +import cats.effect.std.Dispatcher +import cats.effect.unsafe.implicits._ import io.odin.Logger import io.odin.slf4j.OdinLoggerBinder +import java.util.concurrent.atomic.AtomicReference + class OdinInterop extends OdinLoggerBinder[IO] { - implicit val F: Effect[IO] = IO.ioEffect - implicit val clock: Clock[IO] = Clock.create[IO] + implicit def F: Sync[IO] = IO.asyncForIO + + implicit def dispatcher: Dispatcher[IO] = Dispatcher[IO].allocated.unsafeRunSync()._1 val loggers: PartialFunction[String, Logger[IO]] = { val theLogger: String => Option[Logger[IO]] = _ => OdinInterop.globalLogger.get() @@ -21,5 +23,5 @@ class OdinInterop extends OdinLoggerBinder[IO] { } object OdinInterop { - val globalLogger = new AtomicReference[Option[Logger[IO]]](None) + val globalLogger: AtomicReference[Option[Logger[IO]]] = new AtomicReference[Option[Logger[IO]]](None) } diff --git a/src/main/scala/io/pg/actions.scala b/src/main/scala/io/pg/actions.scala index 3819bbc6..afd44f7e 100644 --- a/src/main/scala/io/pg/actions.scala +++ b/src/main/scala/io/pg/actions.scala @@ -8,7 +8,6 @@ import io.pg.config.Matcher import io.pg.config.ProjectConfig import io.pg.gitlab.Gitlab import io.odin.Logger -import io.pg.Prelude.MonadThrow import io.pg.gitlab.Gitlab.MergeRequestInfo import io.pg.config.TextMatcher import cats.MonoidK @@ -20,6 +19,7 @@ import io.pg.MergeRequestState.Mergeability.HasConflicts import cats.Applicative import cats.data.NonEmptyList import scala.util.matching.Regex +import cats.MonadThrow trait ProjectActions[F[_]] { type Action diff --git a/src/main/scala/io/pg/appconfig.scala b/src/main/scala/io/pg/appconfig.scala index 11d834ed..b866bfca 100644 --- a/src/main/scala/io/pg/appconfig.scala +++ b/src/main/scala/io/pg/appconfig.scala @@ -3,9 +3,8 @@ package io.pg import cats.syntax.all._ import ciris.Secret import org.http4s.Headers -import org.http4s.util.CaseInsensitiveString -import org.http4s.syntax.all._ import sttp.model.Uri +import org.typelevel.ci.CIString final case class AppConfig( http: HttpConfig, @@ -31,10 +30,10 @@ object AppConfig { import ciris._ - val httpConfig: ConfigValue[HttpConfig] = + val httpConfig: ConfigValue[ciris.Effect, HttpConfig] = env("HTTP_PORT").as[Int].default(8080).map(HttpConfig(_)) - val metaConfig: ConfigValue[MetaConfig] = + val metaConfig: ConfigValue[ciris.Effect, MetaConfig] = ( default(bannerString), default(BuildInfo.version), @@ -48,19 +47,19 @@ object AppConfig { .leftMap(e => ConfigError(s"Invalid URI ($value at $key), error: $e")) } - val gitConfig: ConfigValue[Git] = + val gitConfig: ConfigValue[ciris.Effect, Git] = ( default(Git.Host.Gitlab), env("GIT_API_URL").as[Uri], env("GIT_API_TOKEN").secret ).mapN(Git.apply) - private val queuesConfig: ConfigValue[Queues] = default(100).map(Queues) + private val queuesConfig: ConfigValue[ciris.Effect, Queues] = default(100).map(Queues) - private val middlewareConfig: ConfigValue[MiddlewareConfig] = - default(Headers.SensitiveHeaders + "Private-Token".ci).map(MiddlewareConfig) + private val middlewareConfig: ConfigValue[ciris.Effect, MiddlewareConfig] = + default(Headers.SensitiveHeaders + CIString("Private-Token")).map(MiddlewareConfig) - val appConfig: ConfigValue[AppConfig] = + val appConfig: ConfigValue[ciris.Effect, AppConfig] = (httpConfig, metaConfig, gitConfig, queuesConfig, middlewareConfig).parMapN(apply) } @@ -86,4 +85,4 @@ object Git { final case class Queues(maxSize: Int) -final case class MiddlewareConfig(sensitiveHeaders: Set[CaseInsensitiveString]) +final case class MiddlewareConfig(sensitiveHeaders: Set[CIString]) diff --git a/src/main/scala/io/pg/config/ProjectConfig.scala b/src/main/scala/io/pg/config/ProjectConfig.scala index 5e7a8c37..9cf54429 100644 --- a/src/main/scala/io/pg/config/ProjectConfig.scala +++ b/src/main/scala/io/pg/config/ProjectConfig.scala @@ -56,7 +56,7 @@ object ProjectConfigReader { def dhallJsonStringConfig[F[_]: ProxFS2: MonadThrow]: F[ProjectConfigReader[F]] = { val prox: ProxFS2[F] = implicitly - import prox.{contextShift => _, blocker => _, concurrent => _, _} + import prox._ val dhallCommand = "dhall-to-json" //todo: not reading a local file diff --git a/src/main/scala/io/pg/resolver.scala b/src/main/scala/io/pg/resolver.scala index 5c42abdf..023de848 100644 --- a/src/main/scala/io/pg/resolver.scala +++ b/src/main/scala/io/pg/resolver.scala @@ -20,7 +20,7 @@ trait StateResolver[F[_]] { object StateResolver { def instance[F[_]: Gitlab: Logger: MonadError[*[_], Throwable]]( - implicit SC: fs2.Stream.Compiler[F, F] + implicit SC: fs2.Compiler[F, F] ): StateResolver[F] = new StateResolver[F] { diff --git a/src/main/scala/io/pg/webhook/webhook.scala b/src/main/scala/io/pg/webhook/webhook.scala index f4366d28..5b8d613c 100644 --- a/src/main/scala/io/pg/webhook/webhook.scala +++ b/src/main/scala/io/pg/webhook/webhook.scala @@ -1,6 +1,5 @@ package io.pg.webhook -import cats.Defer import cats.Monad import cats.MonadError import cats.implicits._ @@ -19,7 +18,7 @@ import org.http4s.dsl.Http4sDsl object WebhookRouter { - def routes[F[_]: MergeRequests: JsonDecoder: Defer: Monad]( + def routes[F[_]: MergeRequests: JsonDecoder: Monad]( implicit eventPublisher: Publisher[F, WebhookEvent] ): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} diff --git a/src/test/scala/io/pg/WebhookProcessorTest.scala b/src/test/scala/io/pg/WebhookProcessorTest.scala index 1922726a..caabab7e 100644 --- a/src/test/scala/io/pg/WebhookProcessorTest.scala +++ b/src/test/scala/io/pg/WebhookProcessorTest.scala @@ -2,7 +2,7 @@ package io.pg import cats.effect.IO import cats.implicits._ -import io.pg.Prelude._ +import cats.effect.implicits._ import io.pg.config.ProjectConfig import io.pg.config.ProjectConfigReader import io.pg.fakes.ProjectActionsStateFake @@ -48,7 +48,7 @@ object WebhookProcessorTest extends SimpleIOSuite { ) } } - .resource + .toResource def testWithResources(name: String)(use: Resources[IO] => IO[Expectations]) = test(name)(mkResources.use(use)) diff --git a/src/test/scala/io/pg/fakes/FakeUtils.scala b/src/test/scala/io/pg/fakes/FakeUtils.scala index 4d91ca1b..dfa2b852 100644 --- a/src/test/scala/io/pg/fakes/FakeUtils.scala +++ b/src/test/scala/io/pg/fakes/FakeUtils.scala @@ -2,7 +2,7 @@ package io.pg.fakes import cats.Monad import cats.mtl.Stateful -import cats.effect.concurrent.Ref +import cats.effect.Ref object FakeUtils { diff --git a/src/test/scala/io/pg/fakes/ProjectActionsStateFake.scala b/src/test/scala/io/pg/fakes/ProjectActionsStateFake.scala index 53e0905e..7fc2a0fa 100644 --- a/src/test/scala/io/pg/fakes/ProjectActionsStateFake.scala +++ b/src/test/scala/io/pg/fakes/ProjectActionsStateFake.scala @@ -1,23 +1,22 @@ package io.pg.fakes +import cats.Monad +import cats.data.Chain +import cats.effect.Ref +import cats.implicits._ import cats.mtl.Stateful -import monocle.macros.Lenses +import io.odin.Logger +import io.pg.MergeRequestState +import io.pg.MergeRequestState.Mergeability +import io.pg.ProjectAction +import io.pg.ProjectAction.Merge +import io.pg.ProjectAction.Rebase import io.pg.ProjectActions import io.pg.StateResolver -import io.pg.ProjectAction -import io.pg.MergeRequestState +import io.pg.gitlab.Gitlab.MergeRequestInfo import io.pg.gitlab.webhook.Project -import io.pg.ProjectAction.Merge import io.scalaland.chimney.dsl._ -import cats.implicits._ -import cats.effect.Sync -import cats.effect.concurrent.Ref -import io.pg.gitlab.Gitlab.MergeRequestInfo -import io.pg.MergeRequestState.Mergeability -import io.pg.ProjectAction.Rebase -import cats.data.Chain -import cats.Monad -import io.odin.Logger +import monocle.macros.Lenses object ProjectActionsStateFake { sealed case class MergeRequestDescription(projectId: Long, mergeRequestIid: Long) @@ -82,7 +81,7 @@ object ProjectActionsStateFake { type Data[F[_]] = Stateful[F, State] def Data[F[_]: Data]: Data[F] = implicitly[Data[F]] - def refInstance[F[_]: Sync: Logger]: F[ProjectActions[F] with StateResolver[F] with State.Modifiers[F]] = + def refInstance[F[_]: Ref.Make: Logger: Monad]: F[ProjectActions[F] with StateResolver[F] with State.Modifiers[F]] = Ref[F].of(State.initial).map(FakeUtils.statefulRef(_)).map(implicit F => instance[F]) /** This instance has both the capabilities of ProjectActions and StateResolver, diff --git a/src/test/scala/io/pg/fakes/ProjectConfigReaderFake.scala b/src/test/scala/io/pg/fakes/ProjectConfigReaderFake.scala index 328bd96d..4597dd3b 100644 --- a/src/test/scala/io/pg/fakes/ProjectConfigReaderFake.scala +++ b/src/test/scala/io/pg/fakes/ProjectConfigReaderFake.scala @@ -1,14 +1,13 @@ package io.pg.fakes -import cats.MonadError +import cats.MonadThrow +import cats.effect.Ref import cats.implicits._ import cats.mtl.Stateful import io.pg.config.ProjectConfig import io.pg.config.ProjectConfigReader import io.pg.gitlab.webhook.Project import monocle.macros.Lenses -import cats.effect.Sync -import cats.effect.concurrent.Ref trait FakeState @@ -33,10 +32,10 @@ object ProjectConfigReaderFake { type Data[F[_]] = Stateful[F, State] def Data[F[_]: Data]: Data[F] = implicitly[Data[F]] - def refInstance[F[_]: Sync]: F[ProjectConfigReader[F] with State.Modifiers[F]] = + def refInstance[F[_]: Ref.Make: MonadThrow]: F[ProjectConfigReader[F] with State.Modifiers[F]] = Ref[F].of(State(Map.empty)).map(FakeUtils.statefulRef(_)).map(implicit F => instance[F]) - def instance[F[_]: Data: MonadError[*[_], Throwable]]: ProjectConfigReader[F] with State.Modifiers[F] = + def instance[F[_]: Data: MonadThrow]: ProjectConfigReader[F] with State.Modifiers[F] = new ProjectConfigReader[F] with State.Modifiers[F] { def readConfig(project: Project): F[ProjectConfig] = From 1db4a151e1f38f55feee87ff466da114d11577c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Jun 2021 17:14:30 +0200 Subject: [PATCH 2/5] Fix tests --- src/main/scala/io/pg/Main.scala | 34 +++++++++++--------------- src/main/scala/io/pg/OdinInterop.scala | 4 +-- src/test/scala/io/pg/MainTest.scala | 3 ++- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/main/scala/io/pg/Main.scala b/src/main/scala/io/pg/Main.scala index edd98dfa..a2069b91 100644 --- a/src/main/scala/io/pg/Main.scala +++ b/src/main/scala/io/pg/Main.scala @@ -20,10 +20,11 @@ import org.http4s.server.middleware import scala.concurrent.ExecutionContext import scala.concurrent.duration._ +import cats.arrow.FunctionK object Main extends IOApp { - def mkLogger[F[_]: Async]: Resource[F, Logger[F]] = { + def mkLogger[F[_]: Async](fToIO: F ~> IO): Resource[F, Logger[F]] = { val console = io.odin.consoleLogger[F](formatter = Formatter.colorful).withMinimalLevel(Level.Info).pure[Resource[F, *]] @@ -41,22 +42,15 @@ object Main extends IOApp { console |+| file } .evalTap { logger => - Dispatcher[F].use { dispatcher => - Sync[F].delay( - OdinInterop - .globalLogger - .set( - logger - .mapK(new (F ~> IO) { - def apply[A](fa: F[A]): IO[A] = IO.defer { - val (future, cancel) = dispatcher.unsafeToFutureCancelable(fa) - IO.fromFuture(IO(future)).onCancel(IO.fromFuture(IO(cancel()))) - } - }) - .some - ) - ) - } + Sync[F].delay( + OdinInterop + .globalLogger + .set( + logger + .mapK(fToIO) + .some + ) + ) } def mkServer[F[_]: Logger: Async]( @@ -84,9 +78,9 @@ object Main extends IOApp { def logStarted[F[_]: Logger](meta: MetaConfig) = Logger[F].info("Started application", Map("version" -> meta.version, "scalaVersion" -> meta.scalaVersion)) - def serve[F[_]: Async](config: AppConfig) = + def serve[F[_]: Async](fToIO: F ~> IO)(config: AppConfig) = for { - implicit0(logger: Logger[F]) <- mkLogger[F] + implicit0(logger: Logger[F]) <- mkLogger[F](fToIO) _ <- logStarting(config.meta).toResource resources <- Application.resource[F](config) _ <- mkServer[F](config, resources.routes) @@ -98,7 +92,7 @@ object Main extends IOApp { AppConfig .appConfig .resource[IO] - .flatMap(serve[IO]) + .flatMap(serve[IO](FunctionK.id)) .use(_ => IO.never) } diff --git a/src/main/scala/io/pg/OdinInterop.scala b/src/main/scala/io/pg/OdinInterop.scala index 7c01adca..c3d22e6c 100644 --- a/src/main/scala/io/pg/OdinInterop.scala +++ b/src/main/scala/io/pg/OdinInterop.scala @@ -10,9 +10,9 @@ import io.odin.slf4j.OdinLoggerBinder import java.util.concurrent.atomic.AtomicReference class OdinInterop extends OdinLoggerBinder[IO] { - implicit def F: Sync[IO] = IO.asyncForIO + implicit val F: Sync[IO] = IO.asyncForIO - implicit def dispatcher: Dispatcher[IO] = Dispatcher[IO].allocated.unsafeRunSync()._1 + implicit val dispatcher: Dispatcher[IO] = Dispatcher[IO].allocated.unsafeRunSync()._1 val loggers: PartialFunction[String, Logger[IO]] = { val theLogger: String => Option[Logger[IO]] = _ => OdinInterop.globalLogger.get() diff --git a/src/test/scala/io/pg/MainTest.scala b/src/test/scala/io/pg/MainTest.scala index 55618f58..3a2e531b 100644 --- a/src/test/scala/io/pg/MainTest.scala +++ b/src/test/scala/io/pg/MainTest.scala @@ -4,6 +4,7 @@ import cats.effect.IO import ciris.Secret import sttp.model.Uri._ import weaver.SimpleIOSuite +import cats.arrow.FunctionK object MainTest extends SimpleIOSuite { test("Application starts") { @@ -15,6 +16,6 @@ object MainTest extends SimpleIOSuite { middleware = MiddlewareConfig(Set()) ) - Main.serve(testConfig).use(IO.pure).as(success) + Main.serve(FunctionK.id)(testConfig).use(IO.pure).as(success) } } From eaf7a355d815411e9649a2d4318fc5791deabe92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Jun 2021 17:15:08 +0200 Subject: [PATCH 3/5] Cleanup `use` --- src/main/scala/io/pg/Main.scala | 2 +- src/test/scala/io/pg/MainTest.scala | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/scala/io/pg/Main.scala b/src/main/scala/io/pg/Main.scala index a2069b91..a77fa397 100644 --- a/src/main/scala/io/pg/Main.scala +++ b/src/main/scala/io/pg/Main.scala @@ -93,6 +93,6 @@ object Main extends IOApp { .appConfig .resource[IO] .flatMap(serve[IO](FunctionK.id)) - .use(_ => IO.never) + .useForever } diff --git a/src/test/scala/io/pg/MainTest.scala b/src/test/scala/io/pg/MainTest.scala index 3a2e531b..357951b2 100644 --- a/src/test/scala/io/pg/MainTest.scala +++ b/src/test/scala/io/pg/MainTest.scala @@ -1,10 +1,9 @@ package io.pg -import cats.effect.IO +import cats.arrow.FunctionK import ciris.Secret import sttp.model.Uri._ import weaver.SimpleIOSuite -import cats.arrow.FunctionK object MainTest extends SimpleIOSuite { test("Application starts") { @@ -16,6 +15,6 @@ object MainTest extends SimpleIOSuite { middleware = MiddlewareConfig(Set()) ) - Main.serve(FunctionK.id)(testConfig).use(IO.pure).as(success) + Main.serve(FunctionK.id)(testConfig).use_.as(success) } } From 92fa608dc0b8bad802bae2b41dfee7ecbc72db26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Jun 2021 17:27:24 +0200 Subject: [PATCH 4/5] Use homegrown handcrafted release of odin by @iRevive --- build.sbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 15e92298..d14e4dd6 100644 --- a/build.sbt +++ b/build.sbt @@ -81,7 +81,7 @@ val commonSettings = List( "org.typelevel" %% "cats-effect" % "3.1.1", "org.typelevel" %% "cats-tagless-macros" % "0.14.0", "co.fs2" %% "fs2-core" % "3.0.4", - "com.github.valskalla" %% "odin-core" % "0.11.0+7-4b29a367-SNAPSHOT", + "io.github.irevive" %% "odin-core" % "0.12.0-M3", "io.circe" %% "circe-core" % "0.14.1", "com.github.julien-truffaut" %% "monocle-macro" % "2.1.0", "com.disneystreaming" %% "weaver-cats" % "0.7.3" % Test, @@ -198,8 +198,8 @@ lazy val pitgull = "io.estatico" %% "newtype" % "0.4.4", "io.scalaland" %% "chimney" % "0.6.1", "io.chrisdavenport" %% "cats-time" % "0.3.4", - "com.github.valskalla" %% "odin-core" % "0.11.0+7-4b29a367-SNAPSHOT", - "com.github.valskalla" %% "odin-slf4j" % "0.11.0+7-4b29a367-SNAPSHOT", + "io.github.irevive" %% "odin-core" % "0.12.0-M3", + "io.github.irevive" %% "odin-slf4j" % "0.12.0-M3", "io.github.vigoo" %% "prox-fs2-3" % "0.7.1" ) ) From 74a52e9d6d6a16d987290901bc641c5a47d1cd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Sat, 12 Jun 2021 17:33:56 +0200 Subject: [PATCH 5/5] Uncomment java stuff --- src/main/java/org/slf4j/impl/StaticLoggerBinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/src/main/java/org/slf4j/impl/StaticLoggerBinder.java index f30b38f5..af701a45 100644 --- a/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ b/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -1,5 +1,5 @@ package org.slf4j.impl; -/* + public class StaticLoggerBinder extends io.pg.OdinInterop { public static String REQUESTED_API_VERSION = "1.7"; @@ -8,4 +8,4 @@ public static StaticLoggerBinder getSingleton() { return _instance; } } - */ +