Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Scala Steward: Reformat with scalafmt 3.7.2
554a8897231b562317c80e615ce41fef92bfef2f
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
runner.dialect=scala3

version = "3.5.9"
version = "3.7.2"
maxColumn = 140
align.preset = some
align.tokens."+" = [
Expand Down
9 changes: 7 additions & 2 deletions bootstrap/src/main/scala/org/polyvariant/Args.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ object Args {
private val switch = "-(\\w+)".r
private val option = "--(\\w+)".r

private def parseNext(pendingArguments: List[String], previousResult: Map[String, String]): Map[String, String] =
private def parseNext(
pendingArguments: List[String],
previousResult: Map[String, String]
): Map[String, String] =
pendingArguments match {
case Nil => previousResult
case option(opt) :: value :: tail => parseNext(tail, previousResult ++ Map(opt -> value))
Expand All @@ -14,7 +17,9 @@ object Args {
}

// TODO: Consider switching to https://ben.kirw.in/decline/ after https://github.com/bkirwi/decline/pull/293
def parse(args: List[String]): Map[String, String] =
def parse(
args: List[String]
): Map[String, String] =
parseNext(args.toList, Map())

}
4 changes: 3 additions & 1 deletion bootstrap/src/main/scala/org/polyvariant/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ final case class Config(

object Config {

def fromArgs[F[_]: MonadThrow](args: Map[String, String]): F[Config] =
def fromArgs[F[_]: MonadThrow](
args: Map[String, String]
): F[Config] =
MonadThrow[F]
.catchNonFatal {
Config(
Expand Down
61 changes: 48 additions & 13 deletions bootstrap/src/main/scala/org/polyvariant/Gitlab.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,57 @@ import cats.MonadThrow
import io.circe.*

trait Gitlab[F[_]] {
def mergeRequests(projectId: Long): F[List[Gitlab.MergeRequestInfo]]
def deleteMergeRequest(projectId: Long, mergeRequestId: Long): F[Unit]
def createWebhook(projectId: Long, pitgullUrl: Uri): F[Unit]
def listWebhooks(projectId: Long): F[List[Gitlab.Webhook]]

def mergeRequests(
projectId: Long
): F[List[Gitlab.MergeRequestInfo]]

def deleteMergeRequest(
projectId: Long,
mergeRequestId: Long
): F[Unit]

def createWebhook(
projectId: Long,
pitgullUrl: Uri
): F[Unit]

def listWebhooks(
projectId: Long
): F[List[Gitlab.Webhook]]

}

object Gitlab {

def apply[F[_]](using ev: Gitlab[F]): Gitlab[F] = ev
def apply[F[_]](
using ev: Gitlab[F]
): Gitlab[F] = ev

def sttpInstance[F[_]: Logger: MonadThrow](
baseUri: Uri,
accessToken: String
)(
using backend: SttpBackend[Identity, Any] // FIXME: https://github.com/polyvariant/pitgull/issues/265
): Gitlab[F] = {
def runRequest[O](request: Request[O, Any]): F[O] =
def runRequest[O](
request: Request[O, Any]
): F[O] =
request
.header("Private-Token", accessToken)
.send(backend)
.pure[F]
.map(_.body) // FIXME - change in https://github.com/polyvariant/pitgull/issues/265

def runGraphQLQuery[A: IsOperation, B](a: SelectionBuilder[A, B]): F[B] =
def runGraphQLQuery[A: IsOperation, B](
a: SelectionBuilder[A, B]
): F[B] =
runRequest(a.toRequest(baseUri.addPath("api", "graphql"))).rethrow

new Gitlab[F] {
def mergeRequests(projectId: Long): F[List[MergeRequestInfo]] =
def mergeRequests(
projectId: Long
): F[List[MergeRequestInfo]] =
Logger[F].info(s"Looking up merge requests for project: $projectId") *>
mergeRequestsQuery(projectId)
.mapEither(_.toRight(DecodingError("Project not found")))
Expand All @@ -60,7 +83,10 @@ object Gitlab {
Logger[F].info(s"Found merge requests. Size: ${result.size}")
}

def deleteMergeRequest(projectId: Long, mergeRequestId: Long): F[Unit] = for {
def deleteMergeRequest(
projectId: Long,
mergeRequestId: Long
): F[Unit] = for {
_ <- Logger[F].debug(s"Request to remove $mergeRequestId")
result <- runRequest(
basicRequest.delete(
Expand All @@ -79,7 +105,10 @@ object Gitlab {
)
} yield ()

def createWebhook(projectId: Long, pitgullUrl: Uri): F[Unit] = for {
def createWebhook(
projectId: Long,
pitgullUrl: Uri
): F[Unit] = for {
_ <- Logger[F].debug(s"Creating webhook to $pitgullUrl")
result <- runRequest(
basicRequest
Expand All @@ -100,7 +129,9 @@ object Gitlab {
)
} yield ()

def listWebhooks(projectId: Long): F[List[Webhook]] = for {
def listWebhooks(
projectId: Long
): F[List[Webhook]] = for {
_ <- Logger[F].debug(s"Listing webhooks for $projectId")
response <- runRequest(
basicRequest
Expand Down Expand Up @@ -141,7 +172,9 @@ object Gitlab {
private def flattenTheEarth[A]: Option[List[Option[Option[Option[List[Option[A]]]]]]] => List[A] =
_.toList.flatten.flatten.flatten.flatten.flatten.flatten

private def mergeRequestInfoSelection(projectId: Long): SelectionBuilder[MergeRequest, MergeRequestInfo] = (
private def mergeRequestInfoSelection(
projectId: Long
): SelectionBuilder[MergeRequest, MergeRequestInfo] = (
MergeRequest.iid.mapEither(_.toLongOption.toRight(DecodingError("MR IID wasn't a Long"))) ~
MergeRequest
.author(UserCore.username)
Expand All @@ -168,7 +201,9 @@ object Gitlab {
hasConflicts = hasConflicts
)

private def mergeRequestsQuery(projectId: Long) =
private def mergeRequestsQuery(
projectId: Long
) =
Query
.projects(ids = List(show"gid://gitlab/Project/$projectId").some)(
ProjectConnection
Expand Down
63 changes: 51 additions & 12 deletions bootstrap/src/main/scala/org/polyvariant/Logger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,64 @@ import cats.effect.kernel.Sync
import scala.io.AnsiColor._

trait Logger[F[_]] {
def debug(msg: String): F[Unit]
def success(msg: String): F[Unit]
def info(msg: String): F[Unit]
def warn(msg: String): F[Unit]
def error(msg: String): F[Unit]

def debug(
msg: String
): F[Unit]

def success(
msg: String
): F[Unit]

def info(
msg: String
): F[Unit]

def warn(
msg: String
): F[Unit]

def error(
msg: String
): F[Unit]

}

object Logger {
def apply[F[_]](using ev: Logger[F]): Logger[F] = ev

def apply[F[_]](
using ev: Logger[F]
): Logger[F] = ev

def wrappedPrint[F[_]: Sync] = new Logger[F] {
private def colorPrinter(color: String)(msg: String): F[Unit] =

private def colorPrinter(
color: String
)(
msg: String
): F[Unit] =
Sync[F].delay(println(s"$color$msg$RESET"))

override def debug(msg: String): F[Unit] = colorPrinter(CYAN)(msg)
override def success(msg: String): F[Unit] = colorPrinter(GREEN)(msg)
override def info(msg: String): F[Unit] = colorPrinter(WHITE)(msg)
override def warn(msg: String): F[Unit] = colorPrinter(YELLOW)(msg)
override def error(msg: String): F[Unit] = colorPrinter(RED)(msg)
override def debug(
msg: String
): F[Unit] = colorPrinter(CYAN)(msg)

override def success(
msg: String
): F[Unit] = colorPrinter(GREEN)(msg)

override def info(
msg: String
): F[Unit] = colorPrinter(WHITE)(msg)

override def warn(
msg: String
): F[Unit] = colorPrinter(YELLOW)(msg)

override def error(
msg: String
): F[Unit] = colorPrinter(RED)(msg)

}

}
32 changes: 25 additions & 7 deletions bootstrap/src/main/scala/org/polyvariant/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import cats.Monad

object Main extends IOApp {

private def printMergeRequests[F[_]: Logger: Applicative](mergeRequests: List[MergeRequestInfo]): F[Unit] =
private def printMergeRequests[F[_]: Logger: Applicative](
mergeRequests: List[MergeRequestInfo]
): F[Unit] =
mergeRequests.traverse { mr =>
Logger[F].info(s"ID: ${mr.mergeRequestIid} by: ${mr.authorUsername}")
}.void
Expand All @@ -28,18 +30,30 @@ object Main extends IOApp {
ifFalse = MonadThrow[F].raiseError(new Exception("User rejected deletion"))
)

private def qualifyMergeRequestsForDeletion(botUserName: String, mergeRequests: List[MergeRequestInfo]): List[MergeRequestInfo] =
private def qualifyMergeRequestsForDeletion(
botUserName: String,
mergeRequests: List[MergeRequestInfo]
): List[MergeRequestInfo] =
mergeRequests.filter(_.authorUsername == botUserName)

private def deleteMergeRequests[F[_]: Gitlab: Logger: Applicative](project: Long, mergeRequests: List[MergeRequestInfo]): F[Unit] =
private def deleteMergeRequests[F[_]: Gitlab: Logger: Applicative](
project: Long,
mergeRequests: List[MergeRequestInfo]
): F[Unit] =
mergeRequests.traverse(mr => Gitlab[F].deleteMergeRequest(project, mr.mergeRequestIid)).void

private def createWebhook[F[_]: Gitlab: Logger: Applicative](project: Long, webhook: Uri): F[Unit] =
private def createWebhook[F[_]: Gitlab: Logger: Applicative](
project: Long,
webhook: Uri
): F[Unit] =
Logger[F].info("Creating webhook") *>
Gitlab[F].createWebhook(project, webhook) *>
Logger[F].info("Webhook created")

private def configureWebhooks[F[_]: Gitlab: Logger: Monad](project: Long, webhook: Uri): F[Unit] = for {
private def configureWebhooks[F[_]: Gitlab: Logger: Monad](
project: Long,
webhook: Uri
): F[Unit] = for {
hooks <- Gitlab[F].listWebhooks(project).map(_.filter(_.url == webhook.toString))
_ <- Monad[F]
.ifM(hooks.nonEmpty.pure[F])(
Expand All @@ -48,7 +62,9 @@ object Main extends IOApp {
)
} yield ()

private def program[F[_]: Logger: Console: Async](args: List[String]): F[Unit] = {
private def program[F[_]: Logger: Console: Async](
args: List[String]
): F[Unit] = {
given SttpBackend[Identity, Any] = HttpURLConnectionBackend()
val parsedArgs = Args.parse(args)
for {
Expand All @@ -70,7 +86,9 @@ object Main extends IOApp {
} yield ()
}

override def run(args: List[String]): IO[ExitCode] = {
override def run(
args: List[String]
): IO[ExitCode] = {
given Logger[IO] = Logger.wrappedPrint[IO]
program[IO](args).recoverWith {
case Config.ArgumentsParsingException =>
Expand Down
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ ThisBuild / libraryDependencySchemes ++= Seq(
"io.circe" %% "circe-parser" % "early-semver"
)

def crossPlugin(x: sbt.librarymanagement.ModuleID) =
def crossPlugin(
x: sbt.librarymanagement.ModuleID
) =
compilerPlugin(x.cross(CrossVersion.full))

val compilerPlugins = List(
Expand Down
10 changes: 8 additions & 2 deletions core/src/main/scala/io/pg/TextUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ package io.pg

object TextUtils {

def trim(maxChars: Int)(s: String): String = {
def trim(
maxChars: Int
)(
s: String
): String = {
val ellipsis = "." * 3
if (s.lengthIs > maxChars) s.take(maxChars - ellipsis.length) ++ ellipsis
else s
}

def inline(s: String): String =
def inline(
s: String
): String =
s.replaceAll("\n", " ")

}
Loading