diff --git a/README.md b/README.md index 9c23b122d8..3ad33357fa 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) [![Docker Pulls](https://img.shields.io/docker/pulls/fthomas/scala-steward.svg?style=flat&color=blue)](https://hub.docker.com/r/fthomas/scala-steward/) -Scala Steward is a bot that helps you keep your library dependencies, sbt plugins, and Scala and sbt versions up-to-date. +Scala Steward is a bot that helps you keep your library dependencies and build plugins up-to-date. +It works with [Maven](https://maven.apache.org/), [Mill](https://mill-build.com/), [sbt](https://www.scala-sbt.org/), and [Scala CLI](https://scala-cli.virtuslab.org/). See also the announcement blog post: [*Keep your projects up-to-date with Scala Steward*](https://www.scala-lang.org/blog/2019/07/10/announcing-scala-steward.html) diff --git a/build.sbt b/build.sbt index 475f793c78..15d9b9dc0b 100644 --- a/build.sbt +++ b/build.sbt @@ -428,17 +428,22 @@ lazy val moduleRootPkg = settingKey[String]("").withRank(KeyRanks.Invisible) moduleRootPkg := rootPkg // Run Scala Steward from sbt for development and testing. -// Do not do this in production. +// Members of the @scala-steward-org/core team can request an access token +// of @scala-steward-dev for local development from @fthomas. lazy val runSteward = taskKey[Unit]("") runSteward := Def.taskDyn { val home = System.getenv("HOME") val projectDir = (LocalRootProject / baseDirectory).value + val gitHubLogin = projectName + "-dev" + // val gitHubAppDir = projectDir.getParentFile / "gh-app" val args = Seq( Seq("--workspace", s"$projectDir/workspace"), Seq("--repos-file", s"$projectDir/repos.md"), - Seq("--git-author-email", s"me@$projectName.org"), - Seq("--forge-login", projectName), - Seq("--git-ask-pass", s"$home/.github/askpass/$projectName.sh"), + Seq("--git-author-email", s"dev@$projectName.org"), + Seq("--forge-login", gitHubLogin), + Seq("--git-ask-pass", s"$home/.github/askpass/$gitHubLogin.sh"), + // Seq("--github-app-id", IO.read(gitHubAppDir / "scala-steward.app-id.txt").trim), + // Seq("--github-app-key-file", s"$gitHubAppDir/scala-steward.private-key.pem"), Seq("--whitelist", s"$home/.cache/coursier"), Seq("--whitelist", s"$home/.cache/JNA"), Seq("--whitelist", s"$home/.cache/mill"), @@ -450,6 +455,15 @@ runSteward := Def.taskDyn { (core.jvm / Compile / run).toTask(args) }.value +lazy val runValidateRepoConfig = taskKey[Unit]("") +runValidateRepoConfig := Def.taskDyn { + val projectDir = (LocalRootProject / baseDirectory).value + val args = Seq( + Seq("validate-repo-config", s"$projectDir/.scala-steward.conf") + ).flatten.mkString(" ", " ", "") + (core.jvm / Compile / run).toTask(args) +}.value + /// commands def addCommandsAlias(name: String, cmds: Seq[String]) = diff --git a/docs/faq.md b/docs/faq.md index 3e4bc694f3..a9bec186b2 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -45,6 +45,12 @@ Updates for `sbt` and `scalafmt` can be controlled by using the following `group { groupId = "org.scalameta", artifactId = "scalafmt-core" } ``` +Updates for the Scala 2 or Scala 3 version can be controlled by using the respective `groupId` and `artifactId`: +```properties +{ groupId = "org.scala-lang", artifactId = "scala-library" } +{ groupId = "org.scala-lang", artifactId = "scala3-library" } +``` + ## Can Scala Steward update multiple branches in a repository? Yes! You can update multiple branches of a repository by adding it several times to the "repos.md" file diff --git a/docs/help.md b/docs/help.md index 2c05cb3bf1..b114ff4f33 100644 --- a/docs/help.md +++ b/docs/help.md @@ -4,8 +4,8 @@ All command line arguments for the `scala-steward` application. ``` Usage: - scala-steward validate-repo-config scala-steward --workspace --repos-file [--repos-file ]... [--git-author-name ] --git-author-email [--git-author-signing-key ] --git-ask-pass [--sign-commits] [--forge-type ] [--forge-api-host ] --forge-login [--do-not-fork] [--add-labels] [--ignore-opts-files] [--env-var ]... [--process-timeout ] [--whitelist ]... [--read-only ]... [--enable-sandbox | --disable-sandbox] [--max-buffer-size ] [--repo-config ]... [--disable-default-repo-config] [--scalafix-migrations ]... [--disable-default-scalafix-migrations] [--artifact-migrations ]... [--disable-default-artifact-migrations] [--cache-ttl ] [--bitbucket-use-default-reviewers] [--bitbucket-server-use-default-reviewers] [--gitlab-merge-when-pipeline-succeeds] [--gitlab-required-reviewers ] [--gitlab-remove-source-branch] [--azure-repos-organization ] [--github-app-id --github-app-key-file ] [--url-checker-test-url ]... [--default-maven-repo ] [--refresh-backoff-period ] + scala-steward validate-repo-config diff --git a/modules/core/src/main/resources/artifact-migrations.v2.conf b/modules/core/src/main/resources/artifact-migrations.v2.conf index 74fdc0f64a..085a8ff8aa 100644 --- a/modules/core/src/main/resources/artifact-migrations.v2.conf +++ b/modules/core/src/main/resources/artifact-migrations.v2.conf @@ -727,6 +727,31 @@ changes = [ groupIdAfter = com.typesafe.play artifactIdAfter = play-grpc-testkit }, + { + groupIdBefore = com.typesafe.play + groupIdAfter = org.playframework + artifactIdAfter = play-grpc-generators + }, + { + groupIdBefore = com.typesafe.play + groupIdAfter = org.playframework + artifactIdAfter = play-grpc-runtime + }, + { + groupIdBefore = com.typesafe.play + groupIdAfter = org.playframework + artifactIdAfter = play-grpc-scalatest + }, + { + groupIdBefore = com.typesafe.play + groupIdAfter = org.playframework + artifactIdAfter = play-grpc-specs2 + }, + { + groupIdBefore = com.typesafe.play + groupIdAfter = org.playframework + artifactIdAfter = play-grpc-testkit + }, { groupIdBefore = com.lightbend.sbt groupIdAfter = com.github.sbt @@ -1074,4 +1099,24 @@ changes = [ groupIdAfter = org.playframework.silhouette artifactIdAfter = play-silhouette }, + { + groupIdAfter = org.typelevel + artifactIdBefore = otel4s-java + artifactIdAfter = otel4s-oteljava + }, + { + groupIdAfter = org.typelevel + artifactIdBefore = otel4s-java-common + artifactIdAfter = otel4s-oteljava-common + }, + { + groupIdAfter = org.typelevel + artifactIdBefore = otel4s-java-metrics + artifactIdAfter = otel4s-oteljava-metrics + }, + { + groupIdAfter = org.typelevel + artifactIdBefore = otel4s-java-trace + artifactIdAfter = otel4s-oteljava-trace + }, ] diff --git a/modules/core/src/main/resources/default.scala-steward.conf b/modules/core/src/main/resources/default.scala-steward.conf index 6467fbd9da..8ca3c128b4 100644 --- a/modules/core/src/main/resources/default.scala-steward.conf +++ b/modules/core/src/main/resources/default.scala-steward.conf @@ -48,64 +48,68 @@ updates.ignore = [ { groupId = "org.scala-lang", artifactId = "scalap", version = { exact = "2.12.19" } }, // Ignore the next Scala.js patch version until it is announced. - { groupId = "org.scala-js", artifactId = "sbt-scalajs", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-ir", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-ir_sjs1", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker-interface", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker-interface_sjs1", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker_sjs1", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-sbt-test-adapter", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-compiler", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-javalib", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-javalib-intf", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.11", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.12", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.13", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.11", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.12", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.13", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.11", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.12", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.13", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-plugin", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.11", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.12", version = { exact = "1.14.1" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.13", version = { exact = "1.14.1" } }, + { groupId = "org.scala-js", artifactId = "sbt-scalajs", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-ir", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-ir_sjs1", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker-interface", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker-interface_sjs1", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker_sjs1", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-sbt-test-adapter", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-compiler", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-javalib", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-javalib-intf", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.11", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.12", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.13", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-scalalib_2.12", version = { suffix = "+1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-scalalib_2.13", version = { suffix = "+1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.11", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.12", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.13", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.11", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.12", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.13", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-plugin", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.11", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.12", version = { exact = "1.15.1" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.13", version = { exact = "1.15.1" } }, // Ignore the next Scala.js minor version until it is announced. - { groupId = "org.scala-js", artifactId = "sbt-scalajs", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-ir", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-ir_sjs1", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker-interface", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker-interface_sjs1", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-linker_sjs1", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-sbt-test-adapter", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-compiler", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-javalib", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-javalib-intf", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.11", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.12", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-library_2.13", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.11", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.12", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.13", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.11", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.12", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.13", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-plugin", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.11", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.12", version = { exact = "1.15.0" } }, - { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.13", version = { exact = "1.15.0" } }, + { groupId = "org.scala-js", artifactId = "sbt-scalajs", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-ir", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-ir_sjs1", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker-interface", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker-interface_sjs1", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-linker_sjs1", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-sbt-test-adapter", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-compiler", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-javalib", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-javalib-intf", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.11", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.12", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-library_2.13", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-scalalib_2.12", version = { suffix = "+1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-scalalib_2.13", version = { suffix = "+1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.11", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.12", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-interface_2.13", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.11", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.12", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-test-bridge_2.13", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-plugin", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.11", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.12", version = { exact = "1.16.0" } }, + { groupId = "org.scala-js", artifactId = "scalajs-junit-test-runtime_2.13", version = { exact = "1.16.0" } }, // Artifacts below are ignored because they are broken or their versioning is broken. @@ -194,4 +198,7 @@ updates.ignore = [ // https://github.com/scalameta/scalafmt/issues/3689 { groupId = "org.scalameta", artifactId = "scalafmt-core", version = "3.7.16" }, + + // https://github.com/circe/circe-yaml/issues/402 + { groupId = "io.circe", artifactId="circe-yaml", version="1.15.0" }, ] diff --git a/modules/core/src/main/scala/org/scalasteward/core/Main.scala b/modules/core/src/main/scala/org/scalasteward/core/Main.scala index 3a6be45be5..17c5d9d2b6 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/Main.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/Main.scala @@ -18,13 +18,16 @@ package org.scalasteward.core import cats.effect.std.Console import cats.effect.{ExitCode, IO, IOApp} -import org.scalasteward.core.application.{Cli, Context} +import org.scalasteward.core.application.{Cli, Context, ValidateRepoConfigContext} object Main extends IOApp { override def run(args: List[String]): IO[ExitCode] = Cli.parseArgs(args) match { - case Cli.ParseResult.Success(config) => Context.step0[IO](config).use(_.runF) - case Cli.ParseResult.Help(help) => Console[IO].println(help).as(ExitCode.Success) - case Cli.ParseResult.Error(error) => Console[IO].errorln(error).as(ExitCode.Error) + case Cli.ParseResult.Success(Cli.Usage.Regular(config)) => + Context.step0[IO](config).use(_.stewardAlg.runF) + case Cli.ParseResult.Success(Cli.Usage.ValidateRepoConfig(file)) => + ValidateRepoConfigContext.step0[IO].flatMap(_.validateRepoConfigAlg.validateAndReport(file)) + case Cli.ParseResult.Help(help) => Console[IO].println(help).as(ExitCode.Success) + case Cli.ParseResult.Error(error) => Console[IO].errorln(error).as(ExitCode.Error) } } diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Cli.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Cli.scala index 2191e4160f..8b53045a9b 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Cli.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Cli.scala @@ -337,16 +337,7 @@ object Cli { .withDefault(default) } - private val configFile: Opts[File] = - Opts.argument[File]() - - private val validateConfigFile: Opts[File] = - Opts.subcommand( - name = "validate-repo-config", - help = "Validate the repo config file and exit; report errors if any" - )(configFile) - - private val configOpts: Opts[Config] = ( + private val regular: Opts[Usage] = ( workspace, reposFiles, gitCfg, @@ -365,29 +356,36 @@ object Cli { urlCheckerTestUrls, defaultMavenRepo, refreshBackoffPeriod - ).mapN(Config.apply) - - val command: Command[StewardUsage] = - Command("scala-steward", "")( - validateConfigFile - .map(StewardUsage.ValidateRepoConfig) - .orElse( - configOpts - .map(StewardUsage.Regular) - ) - ) + ).mapN(Config.apply).map(Usage.Regular.apply) + + private val validateRepoConfig: Opts[Usage] = + Opts + .subcommand( + name = "validate-repo-config", + help = "Validate the repo config file and exit; report errors if any" + )(Opts.argument[File]()) + .map(Usage.ValidateRepoConfig.apply) + + val command: Command[Usage] = + Command("scala-steward", "")(regular.orElse(validateRepoConfig)) sealed trait ParseResult extends Product with Serializable object ParseResult { - final case class Success(config: StewardUsage) extends ParseResult + final case class Success(usage: Usage) extends ParseResult final case class Help(help: String) extends ParseResult final case class Error(error: String) extends ParseResult } + sealed trait Usage extends Product with Serializable + object Usage { + final case class Regular(config: Config) extends Usage + final case class ValidateRepoConfig(file: File) extends Usage + } + def parseArgs(args: List[String]): ParseResult = command.parse(args) match { case Left(help) if help.errors.isEmpty => ParseResult.Help(help.toString) case Left(help) => ParseResult.Error(help.toString) - case Right(config) => ParseResult.Success(config) + case Right(usage) => ParseResult.Success(usage) } } diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Config.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Config.scala index 398ba32f16..3385af7203 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Config.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Config.scala @@ -17,19 +17,13 @@ package org.scalasteward.core.application import better.files.File -import cats.Monad -import cats.syntax.all._ import org.http4s.Uri -import org.http4s.Uri.UserInfo import org.scalasteward.core.application.Cli.EnvVar import org.scalasteward.core.application.Config._ import org.scalasteward.core.data.Resolver import org.scalasteward.core.forge.ForgeType -import org.scalasteward.core.forge.data.AuthenticatedUser import org.scalasteward.core.forge.github.GitHubApp import org.scalasteward.core.git.Author -import org.scalasteward.core.io.{ProcessAlg, WorkspaceAlg} -import org.scalasteward.core.util import org.scalasteward.core.util.Nel import scala.concurrent.duration.FiniteDuration @@ -71,20 +65,6 @@ final case class Config( defaultResolver: Resolver, refreshBackoffPeriod: FiniteDuration ) { - def forgeUser[F[_]](implicit - processAlg: ProcessAlg[F], - workspaceAlg: WorkspaceAlg[F], - F: Monad[F] - ): F[AuthenticatedUser] = - for { - rootDir <- workspaceAlg.rootDir - urlWithUser = util.uri.withUserInfo - .replace(UserInfo(forgeCfg.login, None))(forgeCfg.apiHost) - .renderString - prompt = s"Password for '$urlWithUser': " - password <- processAlg.exec(Nel.of(gitCfg.gitAskPass.pathAsString, prompt), rootDir) - } yield AuthenticatedUser(forgeCfg.login, password.mkString.trim) - def forgeSpecificCfg: ForgeSpecificCfg = forgeCfg.tpe match { case ForgeType.AzureRepos => azureReposCfg @@ -164,10 +144,4 @@ object Config { final case class GiteaCfg( ) extends ForgeSpecificCfg - - sealed trait StewardUsage - object StewardUsage { - final case class Regular(config: Config) extends StewardUsage - final case class ValidateRepoConfig(file: File) extends StewardUsage - } } diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index 1281fa2903..1011d75fb2 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -16,8 +16,6 @@ package org.scalasteward.core.application -import better.files.File -import cats.MonadThrow import cats.effect._ import cats.effect.implicits._ import cats.syntax.all._ @@ -25,7 +23,7 @@ import eu.timepit.refined.auto._ import org.http4s.Uri import org.http4s.client.Client import org.http4s.headers.`User-Agent` -import org.scalasteward.core.application.Config.{ForgeCfg, StewardUsage} +import org.scalasteward.core.application.Config.ForgeCfg import org.scalasteward.core.buildtool.BuildToolDispatcher import org.scalasteward.core.buildtool.bazel.BazelAlg import org.scalasteward.core.buildtool.bleep.BleepAlg @@ -43,13 +41,13 @@ import org.scalasteward.core.edit.hooks.HookExecutor import org.scalasteward.core.edit.scalafix._ import org.scalasteward.core.edit.update.ScannerAlg import org.scalasteward.core.forge.github.{GitHubAppApiAlg, GitHubAuthAlg} -import org.scalasteward.core.forge.{ForgeApiAlg, ForgeRepoAlg, ForgeSelection} +import org.scalasteward.core.forge.{ForgeApiAlg, ForgeAuthAlg, ForgeRepoAlg, ForgeSelection} import org.scalasteward.core.git.{GenGitAlg, GitAlg} import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg} import org.scalasteward.core.nurture.{NurtureAlg, PullRequestRepository, UpdateInfoUrlFinder} import org.scalasteward.core.persistence.{CachingKeyValueStore, JsonKeyValueStore} import org.scalasteward.core.repocache._ -import org.scalasteward.core.repoconfig.{RepoConfigAlg, RepoConfigLoader, ValidateRepoConfigAlg} +import org.scalasteward.core.repoconfig.{RepoConfigAlg, RepoConfigLoader} import org.scalasteward.core.scalafmt.ScalafmtAlg import org.scalasteward.core.update.artifact.{ArtifactMigrationsFinder, ArtifactMigrationsLoader} import org.scalasteward.core.update.{FilterAlg, PruningAlg, UpdateAlg} @@ -93,29 +91,10 @@ final class Context[F[_]](implicit ) object Context { - - sealed trait StewardContext[F[_]] { - def runF: F[ExitCode] - } - object StewardContext { - final case class Regular[F[_]](context: Context[F]) extends StewardContext[F] { - override def runF: F[ExitCode] = context.stewardAlg.runF - } - - final case class ValidateRepoConfig[F[_]](file: File)(implicit - val validateRepoConfigAlg: ValidateRepoConfigAlg[F], - val logger: Logger[F] - ) extends StewardContext[F] { - override def runF: F[ExitCode] = validateRepoConfigAlg.validateAndReport(file) - } - } - - def step0[F[_]]( - usage: Config.StewardUsage - )(implicit F: Async[F]): Resource[F, StewardContext[F]] = + def step0[F[_]](config: Config)(implicit F: Async[F]): Resource[F, Context[F]] = for { - logger0 <- Resource.eval(Slf4jLogger.fromName[F]("org.scalasteward.core")) - _ <- Resource.eval(logger0.info(banner)) + logger <- Resource.eval(Slf4jLogger.fromName[F]("org.scalasteward.core")) + _ <- Resource.eval(logger.info(banner)) _ <- Resource.eval(F.delay(System.setProperty("http.agent", userAgentString))) userAgent <- Resource.eval(F.fromEither(`User-Agent`.parse(1)(userAgentString))) middleware = ClientConfiguration @@ -129,46 +108,22 @@ object Context { ClientConfiguration.disableFollowRedirect, middleware ) - fileAlg0 = FileAlg.create(logger0, F) - context <- usage match { - case StewardUsage.Regular(config) => - initRegular(config)( - defaultClient, - UrlCheckerClient(urlCheckerClient), - fileAlg0, - logger0, - F - ).map(StewardContext.Regular(_)) - - case StewardUsage.ValidateRepoConfig(file) => - implicit val fileAlg: FileAlg[F] = fileAlg0 - implicit val logger: Logger[F] = logger0 - Resource.pure[F, StewardContext[F]](initValidateRepoConfig(file)) + fileAlg = FileAlg.create(logger, F) + processAlg = ProcessAlg.create(config.processCfg)(logger, F) + workspaceAlg = WorkspaceAlg.create(config)(fileAlg, logger, F) + context <- Resource.eval { + step1(config)( + defaultClient, + UrlCheckerClient(urlCheckerClient), + fileAlg, + logger, + processAlg, + workspaceAlg, + F + ) } - } yield context - def initRegular[F[_]](config: Config)(implicit - client: Client[F], - urlCheckerClient: UrlCheckerClient[F], - fileAlg: FileAlg[F], - logger: Logger[F], - F: Async[F] - ): Resource[F, Context[F]] = { - implicit val processAlg = ProcessAlg.create(config.processCfg) - implicit val workspaceAlg = WorkspaceAlg.create(config) - Resource.eval(step1(config)) - } - - def initValidateRepoConfig[F[_]](file: File)(implicit - fileAlg: FileAlg[F], - logger: Logger[F], - F: MonadThrow[F] - ): StewardContext.ValidateRepoConfig[F] = { - implicit val validateRepoConfigAlg = new ValidateRepoConfigAlg[F]() - StewardContext.ValidateRepoConfig[F](file) - } - def step1[F[_]](config: Config)(implicit client: Client[F], urlCheckerClient: UrlCheckerClient[F], @@ -179,7 +134,8 @@ object Context { F: Async[F] ): F[Context[F]] = for { - forgeUser <- config.forgeUser[F] + _ <- F.unit + forgeUser = new ForgeAuthAlg[F](config.gitCfg, config.forgeCfg).forgeUser artifactMigrationsLoader0 = new ArtifactMigrationsLoader[F] artifactMigrationsFinder0 <- artifactMigrationsLoader0.createFinder(config.artifactCfg) scalafixMigrationsLoader0 = new ScalafixMigrationsLoader[F] @@ -244,7 +200,7 @@ object Context { implicit val editAlg: EditAlg[F] = new EditAlg[F] implicit val nurtureAlg: NurtureAlg[F] = new NurtureAlg[F](config.forgeCfg) implicit val pruningAlg: PruningAlg[F] = new PruningAlg[F] - implicit val reposFilesLoader: ReposFilesLoader[F] = new ReposFilesLoader[F]() + implicit val reposFilesLoader: ReposFilesLoader[F] = new ReposFilesLoader[F] implicit val gitHubAppApiAlg: GitHubAppApiAlg[F] = new GitHubAppApiAlg[F](config.forgeCfg.apiHost) implicit val stewardAlg: StewardAlg[F] = new StewardAlg[F](config) diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/ValidateRepoConfigContext.scala b/modules/core/src/main/scala/org/scalasteward/core/application/ValidateRepoConfigContext.scala new file mode 100644 index 0000000000..3439427e94 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/application/ValidateRepoConfigContext.scala @@ -0,0 +1,46 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.application + +import cats.effect.Sync +import cats.syntax.all._ +import org.scalasteward.core.io.FileAlg +import org.scalasteward.core.repoconfig.ValidateRepoConfigAlg +import org.typelevel.log4cats.Logger +import org.typelevel.log4cats.slf4j.Slf4jLogger + +final class ValidateRepoConfigContext[F[_]](implicit + val validateRepoConfigAlg: ValidateRepoConfigAlg[F] +) + +object ValidateRepoConfigContext { + def step0[F[_]](implicit F: Sync[F]): F[ValidateRepoConfigContext[F]] = + for { + logger <- Slf4jLogger.fromName[F]("org.scalasteward.core") + fileAlg = FileAlg.create(logger, F) + context = step1(fileAlg, logger, F) + } yield context + + def step1[F[_]](implicit + fileAlg: FileAlg[F], + logger: Logger[F], + F: Sync[F] + ): ValidateRepoConfigContext[F] = { + implicit val validateRepoConfigAlg: ValidateRepoConfigAlg[F] = new ValidateRepoConfigAlg + new ValidateRepoConfigContext[F] + } +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/data/Version.scala b/modules/core/src/main/scala/org/scalasteward/core/data/Version.scala index 9bb595ae14..22c005d6a0 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/data/Version.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/data/Version.scala @@ -190,7 +190,7 @@ object Version { val numeric = Numbers.digits.map(s => List(Numeric(s))) val alpha = Parser.charsWhile(c => !digits(c) && !separators(c)).map(s => List(Alpha(s))) val separator = Parser.charIn(separators).map(c => List(Separator(c))) - val hash = (Parser.charIn('-', '+') ~ + val hash = (Parser.charIn(separators) ~ Parser.char('g').string.? ~ Rfc5234.hexdig.rep(6).string.filterNot(startsWithDate)).backtrack .map { case ((s, g), h) => List(Separator(s), Hash(g.getOrElse("") + h)) } diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeAuthAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeAuthAlg.scala new file mode 100644 index 0000000000..65debaa02e --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeAuthAlg.scala @@ -0,0 +1,42 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.forge + +import cats.Monad +import cats.syntax.all._ +import org.http4s.Uri.UserInfo +import org.scalasteward.core.application.Config.{ForgeCfg, GitCfg} +import org.scalasteward.core.forge.data.AuthenticatedUser +import org.scalasteward.core.io.{ProcessAlg, WorkspaceAlg} +import org.scalasteward.core.util +import org.scalasteward.core.util.Nel + +final class ForgeAuthAlg[F[_]](gitCfg: GitCfg, forgeCfg: ForgeCfg)(implicit + processAlg: ProcessAlg[F], + workspaceAlg: WorkspaceAlg[F], + F: Monad[F] +) { + def forgeUser: F[AuthenticatedUser] = + for { + rootDir <- workspaceAlg.rootDir + userInfo = UserInfo(forgeCfg.login, None) + urlWithUser = util.uri.withUserInfo.replace(userInfo)(forgeCfg.apiHost).renderString + prompt = s"Password for '$urlWithUser': " + output <- processAlg.exec(Nel.of(gitCfg.gitAskPass.pathAsString, prompt), rootDir) + password = output.mkString.trim + } yield AuthenticatedUser(forgeCfg.login, password) +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeSelection.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeSelection.scala index e53fe350b2..9fb7c0ca1f 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeSelection.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/ForgeSelection.scala @@ -18,7 +18,7 @@ package org.scalasteward.core.forge import cats.effect.Temporal import cats.syntax.all._ -import cats.{Applicative, Parallel} +import cats.{Applicative, Functor, Parallel} import org.http4s.headers.Authorization import org.http4s.{BasicCredentials, Header, Request} import org.scalasteward.core.application.Config @@ -39,13 +39,13 @@ object ForgeSelection { def forgeApiAlg[F[_]: Parallel]( forgeCfg: ForgeCfg, forgeSpecificCfg: ForgeSpecificCfg, - user: AuthenticatedUser + user: F[AuthenticatedUser] )(implicit httpJsonClient: HttpJsonClient[F], logger: Logger[F], F: Temporal[F] ): ForgeApiAlg[F] = { - val auth = (_: Any) => authenticate(forgeCfg.tpe, user) + val auth = authenticate(forgeCfg.tpe, user) forgeSpecificCfg match { case specificCfg: Config.AzureReposCfg => new AzureReposApiAlg(forgeCfg.apiHost, specificCfg, auth) @@ -64,27 +64,30 @@ object ForgeSelection { def authenticate[F[_]]( forgeType: ForgeType, - user: AuthenticatedUser - )(implicit F: Applicative[F]): Request[F] => F[Request[F]] = + user: F[AuthenticatedUser] + )(implicit F: Functor[F]): Request[F] => F[Request[F]] = forgeType match { - case AzureRepos => _.putHeaders(basicAuth(user)).pure[F] - case Bitbucket => _.putHeaders(basicAuth(user)).pure[F] - case BitbucketServer => _.putHeaders(basicAuth(user), xAtlassianToken).pure[F] - case GitHub => _.putHeaders(basicAuth(user)).pure[F] - case GitLab => _.putHeaders(Header.Raw(ci"Private-Token", user.accessToken)).pure[F] - case Gitea => _.putHeaders(basicAuth(user)).pure[F] + case AzureRepos => req => user.map(u => req.putHeaders(basicAuth(u))) + case Bitbucket => req => user.map(u => req.putHeaders(basicAuth(u))) + case BitbucketServer => req => user.map(u => req.putHeaders(basicAuth(u), xAtlassianToken)) + case GitHub => req => user.map(u => req.putHeaders(basicAuth(u))) + case GitLab => req => user.map(u => req.putHeaders(privateToken(u))) + case Gitea => req => user.map(u => req.putHeaders(basicAuth(u))) } private def basicAuth(user: AuthenticatedUser): Authorization = Authorization(BasicCredentials(user.login, user.accessToken)) + private def privateToken(user: AuthenticatedUser): Header.Raw = + Header.Raw(ci"Private-Token", user.accessToken) + // Bypass the server-side XSRF check, see // https://github.com/scala-steward-org/scala-steward/pull/1863#issuecomment-754538364 private val xAtlassianToken = Header.Raw(ci"X-Atlassian-Token", "no-check") def authenticateIfApiHost[F[_]]( forgeCfg: ForgeCfg, - user: AuthenticatedUser + user: F[AuthenticatedUser] )(implicit F: Applicative[F]): Request[F] => F[Request[F]] = req => { val sameScheme = req.uri.scheme === forgeCfg.apiHost.scheme diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlg.scala index c399094026..4dd5698910 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlg.scala @@ -31,7 +31,7 @@ import org.typelevel.log4cats.Logger final class AzureReposApiAlg[F[_]]( azureAPiHost: Uri, config: AzureReposCfg, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F], logger: Logger[F], F: MonadThrow[F]) extends ForgeApiAlg[F] { @@ -45,7 +45,7 @@ final class AzureReposApiAlg[F[_]]( val create = client.postWithBody[PullRequestOut, PullRequestPayload]( url.pullRequests(repo), PullRequestPayload.from(data), - modify(repo) + modify ) for { _ <- F.whenA(data.assignees.nonEmpty)(warnIfAssigneesAreUsed) @@ -66,21 +66,21 @@ final class AzureReposApiAlg[F[_]]( client.patchWithBody[PullRequestOut, ClosePullRequestPayload]( url.closePullRequest(repo, number), ClosePullRequestPayload("abandoned"), - modify(repo) + modify ) // https://docs.microsoft.com/en-us/rest/api/azure/devops/git/stats/get?view=azure-devops-rest-7.1 override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = - client.get[BranchOut](url.getBranch(repo, branch), modify(repo)) + client.get[BranchOut](url.getBranch(repo, branch), modify) // https://docs.microsoft.com/en-us/rest/api/azure/devops/git/repositories/get-repository-with-parent?view=azure-devops-rest-7.1 override def getRepo(repo: Repo): F[RepoOut] = - client.get[RepoOut](url.getRepo(repo), modify(repo)) + client.get[RepoOut](url.getRepo(repo), modify) // https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.1 override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = client - .get[Paginated[PullRequestOut]](url.listPullRequests(repo, head, base), modify(repo)) + .get[Paginated[PullRequestOut]](url.listPullRequests(repo, head, base), modify) .map(_.value) // https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-threads/create?view=azure-devops-rest-7.1 @@ -92,7 +92,7 @@ final class AzureReposApiAlg[F[_]]( client.postWithBody[Comment, PullRequestCommentPayload]( url.commentPullRequest(repo, number), PullRequestCommentPayload.createComment(comment), - modify(repo) + modify ) private def warnIfAssigneesAreUsed = diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlg.scala index fe81d9fb07..65af40706e 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlg.scala @@ -32,7 +32,7 @@ import org.typelevel.log4cats.Logger class BitbucketApiAlg[F[_]]( config: ForgeCfg, bitbucketCfg: BitbucketCfg, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F], logger: Logger[F], @@ -42,13 +42,13 @@ class BitbucketApiAlg[F[_]]( override def createFork(repo: Repo): F[RepoOut] = for { - fork <- client.post[RepositoryResponse](url.forks(repo), modify(repo)).recoverWith { + fork <- client.post[RepositoryResponse](url.forks(repo), modify).recoverWith { case UnexpectedResponse(_, _, _, Status.BadRequest, _) => - client.get(url.repo(repo.copy(owner = config.login)), modify(repo)) + client.get(url.repo(repo.copy(owner = config.login)), modify) } maybeParent <- fork.parent - .map(n => client.get[RepositoryResponse](url.repo(n), modify(n))) + .map(n => client.get[RepositoryResponse](url.repo(n), modify)) .sequence[F, RepositoryResponse] } yield mapToRepoOut(fork, maybeParent) @@ -65,7 +65,7 @@ class BitbucketApiAlg[F[_]]( ) private def getDefaultReviewers(repo: Repo): F[List[Reviewer]] = - client.get[DefaultReviewers](url.defaultReviewers(repo), modify(repo)).map(_.values) + client.get[DefaultReviewers](url.defaultReviewers(repo), modify).map(_.values) override def createPullRequest(repo: Repo, data: NewPullRequestData): F[PullRequestOut] = { val sourceBranchOwner = if (config.doNotFork) repo.owner else config.login @@ -86,7 +86,7 @@ class BitbucketApiAlg[F[_]]( ) ) .flatMap { payload => - client.postWithBody(url.pullRequests(repo), payload, modify(repo)) + client.postWithBody(url.pullRequests(repo), payload, modify) } for { @@ -105,26 +105,26 @@ class BitbucketApiAlg[F[_]]( logger.warn("Updating PRs is not yet supported for Bitbucket") override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = - client.get(url.branch(repo, branch), modify(repo)) + client.get(url.branch(repo, branch), modify) override def getRepo(repo: Repo): F[RepoOut] = for { - repo <- client.get[RepositoryResponse](url.repo(repo), modify(repo)) + repo <- client.get[RepositoryResponse](url.repo(repo), modify) maybeParent <- repo.parent - .map(n => client.get[RepositoryResponse](url.repo(n), modify(n))) + .map(n => client.get[RepositoryResponse](url.repo(n), modify)) .sequence[F, RepositoryResponse] } yield mapToRepoOut(repo, maybeParent) override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = client - .get[Page[PullRequestOut]](url.listPullRequests(repo, head), modify(repo)) + .get[Page[PullRequestOut]](url.listPullRequests(repo, head), modify) .map(_.values) override def closePullRequest(repo: Repo, number: PullRequestNumber): F[PullRequestOut] = client.post[PullRequestOut]( url.decline(repo, number), - modify(repo) + modify ) override def commentPullRequest( @@ -136,7 +136,7 @@ class BitbucketApiAlg[F[_]]( .postWithBody[CreateComment, CreateComment]( url.comments(repo, number), CreateComment(comment), - modify(repo) + modify ) .map((cc: CreateComment) => Comment(cc.content.raw)) diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlg.scala index 8120a16e6e..71cac81c6c 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlg.scala @@ -33,7 +33,7 @@ import org.typelevel.log4cats.Logger final class BitbucketServerApiAlg[F[_]]( bitbucketApiHost: Uri, config: BitbucketServerCfg, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F], logger: Logger[F], F: MonadThrow[F]) extends ForgeApiAlg[F] { private val url = new Url(bitbucketApiHost) @@ -53,7 +53,7 @@ final class BitbucketServerApiAlg[F[_]]( .postWithBody[Json.Comment, Json.Comment]( url.comments(repo, number), Json.Comment(comment), - modify(repo) + modify ) .map(comment => Comment(comment.text)) @@ -82,7 +82,7 @@ final class BitbucketServerApiAlg[F[_]]( locked = false, reviewers = reviewers ) - pr <- client.postWithBody[Json.PR, Json.NewPR](url.pullRequests(repo), req, modify(repo)) + pr <- client.postWithBody[Json.PR, Json.NewPR](url.pullRequests(repo), req, modify) } yield pr.toPullRequestOut } @@ -97,10 +97,10 @@ final class BitbucketServerApiAlg[F[_]]( if (config.useDefaultReviewers) getDefaultReviewers(repo) else F.pure(List.empty[Reviewer]) private def declinePullRequest(repo: Repo, number: PullRequestNumber, version: Int): F[Unit] = - client.post_(url.declinePullRequest(repo, number, version), modify(repo)) + client.post_(url.declinePullRequest(repo, number, version), modify) private def getDefaultReviewers(repo: Repo): F[List[Reviewer]] = - client.get[List[Json.Condition]](url.reviewers(repo), modify(repo)).map { conditions => + client.get[List[Json.Condition]](url.reviewers(repo), modify).map { conditions => conditions.flatMap { condition => condition.reviewers.map(reviewer => Reviewer(User(reviewer.name))) } @@ -108,25 +108,25 @@ final class BitbucketServerApiAlg[F[_]]( override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = client - .get[Json.Branches](url.listBranch(repo, branch), modify(repo)) + .get[Json.Branches](url.listBranch(repo, branch), modify) .map(_.values.head.toBranchOut) private def getDefaultBranch(repo: Repo): F[Json.Branch] = - client.get[Json.Branch](url.defaultBranch(repo), modify(repo)) + client.get[Json.Branch](url.defaultBranch(repo), modify) private def getPullRequest(repo: Repo, number: PullRequestNumber): F[PR] = - client.get[Json.PR](url.pullRequest(repo, number), modify(repo)) + client.get[Json.PR](url.pullRequest(repo, number), modify) override def getRepo(repo: Repo): F[RepoOut] = for { - jRepo <- client.get[Json.Repo](url.repos(repo), modify(repo)) + jRepo <- client.get[Json.Repo](url.repos(repo), modify) cloneUrl <- jRepo.cloneUrlOrRaise[F] defaultBranch <- getDefaultBranch(repo) } yield RepoOut(jRepo.slug, UserOut(repo.owner), None, cloneUrl, defaultBranch.displayId) override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = client - .get[Json.Page[Json.PR]](url.listPullRequests(repo, s"refs/heads/$head"), modify(repo)) + .get[Json.Page[Json.PR]](url.listPullRequests(repo, s"refs/heads/$head"), modify) .map(_.values.map(_.toPullRequestOut)) private def warnIfLabelsAreUsed = diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/data/RepoOut.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/data/RepoOut.scala index c833905145..be9abd615d 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/data/RepoOut.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/data/RepoOut.scala @@ -34,7 +34,7 @@ final case class RepoOut( archived: Boolean = false ) { def parentOrRaise[F[_]](implicit F: ApplicativeThrow[F]): F[RepoOut] = - parent.fold(F.raiseError[RepoOut](new Throwable(s"repo $name has no parent")))(F.pure) + parent.fold(F.raiseError[RepoOut](new Throwable(s"repo ${repo.show} has no parent")))(F.pure) def repo: Repo = Repo(owner.login, name) diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/gitea/GiteaApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/gitea/GiteaApiAlg.scala index 36faa27948..2364afd069 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/gitea/GiteaApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/gitea/GiteaApiAlg.scala @@ -18,14 +18,17 @@ package org.scalasteward.core.forge.gitea import cats._ import cats.implicits._ -import org.scalasteward.core.git.Branch -import org.scalasteward.core.git.Sha1 -import org.scalasteward.core.util.HttpJsonClient +import io.circe._ +import io.circe.generic.semiauto.{deriveCodec, deriveEncoder} import org.http4s.{Request, Uri} import org.scalasteward.core.application.Config.ForgeCfg import org.scalasteward.core.data.Repo import org.scalasteward.core.forge.ForgeApiAlg import org.scalasteward.core.forge.data._ +import org.scalasteward.core.forge.gitea.GiteaApiAlg._ +import org.scalasteward.core.git.{Branch, Sha1} +import org.scalasteward.core.util.uri._ +import org.scalasteward.core.util.{intellijThisImportIsUsed, HttpJsonClient} import org.typelevel.log4cats.Logger // docs @@ -33,10 +36,6 @@ import org.typelevel.log4cats.Logger // - https://try.gitea.io/api/swagger // - https://codeberg.org/api/swagger object GiteaApiAlg { - import io.circe._ - import io.circe.generic.semiauto.deriveCodec - import org.scalasteward.core.util.uri._ - implicit val uriEncoder: Encoder[Uri] = Encoder[String].contramap[Uri](_.renderString) val DefaultLabelColor = "#e01060" @@ -44,7 +43,7 @@ object GiteaApiAlg { name: Option[String], // name of the forked repository organization: Option[String] // organization name, if forking into an organization ) - implicit val createForkOptionCodec: Encoder[CreateForkOption] = deriveCodec + implicit val createForkOptionEncoder: Encoder[CreateForkOption] = deriveEncoder case class User( login: String, @@ -116,14 +115,15 @@ object GiteaApiAlg { case class AttachLabelReq(labels: Vector[Int]) implicit val attachLabelReqCodec: Codec[AttachLabelReq] = deriveCodec + + intellijThisImportIsUsed(uriEncoder) } final class GiteaApiAlg[F[_]: HttpJsonClient]( vcs: ForgeCfg, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit logger: Logger[F], F: MonadThrow[F]) extends ForgeApiAlg[F] { - import GiteaApiAlg._ def client: HttpJsonClient[F] = implicitly val url = new Url(vcs.apiHost) @@ -158,7 +158,7 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( .postWithBody[Repository, CreateForkOption]( url.forks(repo), CreateForkOption(name = none, organization = none), - modify(repo) + modify ) .map(repoOut(_)) @@ -182,7 +182,7 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( .postWithBody[PullRequestResp, CreatePullRequestOption]( url.pulls(repo), create, - modify(repo) + modify ) } yield pullRequestOut(resp) @@ -199,21 +199,21 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( .patchWithBody[PullRequestResp, EditPullRequestOption]( url.pull(repo, number), edit, - modify(repo) + modify ) .map(pullRequestOut(_)) } override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = client - .get[BranchResp](url.repoBranch(repo, branch), modify(repo)) + .get[BranchResp](url.repoBranch(repo, branch), modify) .map { b => BranchOut(branch, CommitOut(b.commit.id)) } override def getRepo(repo: Repo): F[RepoOut] = client - .get[Repository](url.repos(repo), modify(repo)) + .get[Repository](url.repos(repo), modify) .map(repoOut(_)) override def listPullRequests( @@ -228,7 +228,7 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( .pulls(repo) .withQueryParam("page", page) .withQueryParam("limit", PULL_REQUEST_PAGE_SIZE), - modify(repo) + modify ) // basically unfoldEval @@ -255,7 +255,7 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( .postWithBody[CommentResp, CreateIssueCommentOption]( url.comments(repo, number), create, - modify(repo) + modify ) .map { x => Comment(x.body) @@ -277,14 +277,14 @@ final class GiteaApiAlg[F[_]: HttpJsonClient]( client.postWithBody[Label, CreateLabelReq]( url.labels(repo), CreateLabelReq(name, DefaultLabelColor), - modify(repo) + modify ) def listLabels(repo: Repo): F[Vector[Label]] = { def paging(page: Int) = client.get[Vector[Label]]( url.labels(repo).withQueryParam("page", page), - modify(repo) + modify ) def go(page: Int, accu: Vector[Label]): F[Vector[Label]] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubApiAlg.scala index 41bc2a2921..18c4a1f7c6 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubApiAlg.scala @@ -18,6 +18,7 @@ package org.scalasteward.core.forge.github import cats.MonadThrow import cats.syntax.all._ +import io.circe.Json import org.http4s.{Request, Uri} import org.scalasteward.core.data.Repo import org.scalasteward.core.forge.ForgeApiAlg @@ -26,11 +27,10 @@ import org.scalasteward.core.forge.github.GitHubException._ import org.scalasteward.core.git.Branch import org.scalasteward.core.util.HttpJsonClient import org.typelevel.log4cats.Logger -import io.circe.Json final class GitHubApiAlg[F[_]]( gitHubApiHost: Uri, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F], logger: Logger[F], @@ -40,7 +40,7 @@ final class GitHubApiAlg[F[_]]( /** https://docs.github.com/en/rest/repos/forks?apiVersion=2022-11-28#create-a-fork */ override def createFork(repo: Repo): F[RepoOut] = - client.post[RepoOut](url.forks(repo), modify(repo)).flatTap { repoOut => + client.post[RepoOut](url.forks(repo), modify).flatTap { repoOut => F.raiseWhen(repoOut.parent.exists(_.archived))(RepositoryArchived(repo)) } @@ -51,7 +51,7 @@ final class GitHubApiAlg[F[_]]( .postWithBody[PullRequestOut, CreatePullRequestPayload]( uri = url.pulls(repo), body = payload, - modify = modify(repo) + modify = modify ) .adaptErr(SecondaryRateLimitExceeded.fromThrowable) @@ -77,7 +77,7 @@ final class GitHubApiAlg[F[_]]( .patchWithBody[PullRequestOut, UpdatePullRequestPayload]( uri = url.pull(repo, number), body = payload, - modify = modify(repo) + modify = modify ) .adaptErr(SecondaryRateLimitExceeded.fromThrowable) @@ -91,24 +91,24 @@ final class GitHubApiAlg[F[_]]( /** https://docs.github.com/en/rest/repos/branches?apiVersion=2022-11-28#get-branch */ override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = - client.get(url.branches(repo, branch), modify(repo)) + client.get(url.branches(repo, branch), modify) /** https://docs.github.com/en/rest/repos?apiVersion=2022-11-28#get */ override def getRepo(repo: Repo): F[RepoOut] = - client.get[RepoOut](url.repos(repo), modify(repo)).flatTap { repoOut => + client.get[RepoOut](url.repos(repo), modify).flatTap { repoOut => F.raiseWhen(repoOut.archived)(RepositoryArchived(repo)) } /** https://docs.github.com/en/rest/pulls?apiVersion=2022-11-28#list-pull-requests */ override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = - client.get(url.listPullRequests(repo, head, base), modify(repo)) + client.get(url.listPullRequests(repo, head, base), modify) /** https://docs.github.com/en/rest/pulls?apiVersion=2022-11-28#update-a-pull-request */ override def closePullRequest(repo: Repo, number: PullRequestNumber): F[PullRequestOut] = client.patchWithBody[PullRequestOut, UpdateState]( url.pull(repo, number), UpdateState(PullRequestState.Closed), - modify(repo) + modify ) /** https://docs.github.com/en/rest/issues?apiVersion=2022-11-28#create-an-issue-comment */ @@ -118,7 +118,7 @@ final class GitHubApiAlg[F[_]]( comment: String ): F[Comment] = client - .postWithBody(url.comments(repo, number), Comment(comment), modify(repo)) + .postWithBody(url.comments(repo, number), Comment(comment), modify) /** https://docs.github.com/en/rest/reference/issues?apiVersion=2022-11-28#add-labels-to-an-issue */ @@ -131,7 +131,7 @@ final class GitHubApiAlg[F[_]]( .postWithBody[io.circe.Json, GitHubLabels]( url.issueLabels(repo, number), GitHubLabels(labels), - modify(repo) + modify ) .adaptErr(SecondaryRateLimitExceeded.fromThrowable) .void @@ -145,7 +145,7 @@ final class GitHubApiAlg[F[_]]( .postWithBody[Json, GitHubAssignees]( url.assignees(repo, number), GitHubAssignees(assignees), - modify(repo) + modify ) .void .handleErrorWith { error => @@ -161,7 +161,7 @@ final class GitHubApiAlg[F[_]]( .postWithBody[Json, GitHubReviewers]( url.reviewers(repo, number), GitHubReviewers(reviewers), - modify(repo) + modify ) .void .handleErrorWith { error => diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubAuthAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubAuthAlg.scala index 0dcc6d3b7c..8ffbb10f37 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubAuthAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/github/GitHubAuthAlg.scala @@ -19,14 +19,13 @@ package org.scalasteward.core.forge.github import better.files.File import cats.effect.Sync import cats.implicits._ -import io.jsonwebtoken.{Jwts, SignatureAlgorithm} -import org.bouncycastle.jce.provider.BouncyCastleProvider -import org.bouncycastle.util.io.pem.PemReader - +import io.jsonwebtoken.Jwts import java.io.FileReader import java.security.spec.PKCS8EncodedKeySpec import java.security.{KeyFactory, PrivateKey, Security} import java.util.Date +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.util.io.pem.PemReader import scala.concurrent.duration.FiniteDuration import scala.util.Using @@ -72,13 +71,13 @@ object GitHubAuthAlg { val signingKey = readPrivateKey(app.keyFile) val builder = Jwts .builder() - .setIssuedAt(now) - .setIssuer(app.id.toString) - .signWith(signingKey, SignatureAlgorithm.RS256) + .issuedAt(now) + .issuer(app.id.toString) + .signWith(signingKey, Jwts.SIG.RS256) if (ttlMillis > 0) { val expMillis = nowMillis + ttlMillis val exp = new Date(expMillis) - builder.setExpiration(exp) + builder.expiration(exp) } builder.compact() } diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlg.scala index 53e543a5ed..63da7a5427 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlg.scala @@ -16,8 +16,8 @@ package org.scalasteward.core.forge.gitlab -import cats.effect.Temporal import cats.Parallel +import cats.effect.Temporal import cats.syntax.all._ import io.circe._ import io.circe.generic.semiauto._ @@ -31,7 +31,6 @@ import org.scalasteward.core.git.{Branch, Sha1} import org.scalasteward.core.util.uri.uriDecoder import org.scalasteward.core.util.{intellijThisImportIsUsed, HttpJsonClient, UnexpectedResponse} import org.typelevel.log4cats.Logger - import scala.concurrent.duration.{Duration, DurationInt} final private[gitlab] case class ForkPayload(id: String, namespace: String) @@ -161,7 +160,7 @@ private[gitlab] object GitLabJsonCodec { final class GitLabApiAlg[F[_]: Parallel]( forgeCfg: ForgeCfg, gitLabCfg: GitLabCfg, - modify: Repo => Request[F] => F[Request[F]] + modify: Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F], logger: Logger[F], @@ -172,13 +171,13 @@ final class GitLabApiAlg[F[_]: Parallel]( private val url = new Url(forgeCfg.apiHost) override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = - client.get(url.listMergeRequests(repo, head, base.name), modify(repo)) + client.get(url.listMergeRequests(repo, head, base.name), modify) override def createFork(repo: Repo): F[RepoOut] = { val userOwnedRepo = repo.copy(owner = forgeCfg.login) val data = ForkPayload(url.encodedProjectId(userOwnedRepo), forgeCfg.login) client - .postWithBody[RepoOut, ForkPayload](url.createFork(repo), data, modify(repo)) + .postWithBody[RepoOut, ForkPayload](url.createFork(repo), data, modify) .recoverWith { case UnexpectedResponse(_, _, _, Status.Conflict, _) => getRepo(userOwnedRepo) // workaround for https://gitlab.com/gitlab-org/gitlab-ce/issues/65275 @@ -190,8 +189,8 @@ final class GitLabApiAlg[F[_]: Parallel]( override def createPullRequest(repo: Repo, data: NewPullRequestData): F[PullRequestOut] = { val targetRepo = if (forgeCfg.doNotFork) repo else repo.copy(owner = forgeCfg.login) val mergeRequest = for { - projectId <- client.get[ProjectId](url.repos(repo), modify(repo)) - usernameMapping <- getUsernameToUserIdsMapping(repo, (data.assignees ++ data.reviewers).toSet) + projectId <- client.get[ProjectId](url.repos(repo), modify) + usernameMapping <- getUsernameToUserIdsMapping((data.assignees ++ data.reviewers).toSet) payload = MergeRequestPayload( id = url.encodedProjectId(targetRepo), projectId = projectId.id, @@ -202,7 +201,7 @@ final class GitLabApiAlg[F[_]: Parallel]( res <- client.postWithBody[MergeRequestOut, MergeRequestPayload]( uri = url.mergeRequest(targetRepo), body = payload, - modify = modify(repo) + modify = modify ) } yield res @@ -213,7 +212,7 @@ final class GitLabApiAlg[F[_]: Parallel]( backoffMultiplier: Double = 2.0 ): F[MergeRequestOut] = client - .get[MergeRequestOut](url.existingMergeRequest(repo, number), modify(repo)) + .get[MergeRequestOut](url.existingMergeRequest(repo, number), modify) .flatMap { case mr if mr.mergeStatus =!= GitLabMergeStatus.Checking => F.pure(mr) case mr if retries > 0 => @@ -264,7 +263,7 @@ final class GitLabApiAlg[F[_]: Parallel]( client .put[MergeRequestOut]( url.mergeWhenPiplineSucceeds(repo, mr.iid), - modify(repo) + modify ) // it's possible that our status changed from can be merged already, // so just handle it gracefully and proceed without setting auto merge. @@ -289,7 +288,7 @@ final class GitLabApiAlg[F[_]: Parallel]( client .put[MergeRequestApprovalsOut]( url.requiredApprovals(repo, mrOut.iid, requiredReviewers), - modify(repo) + modify ) .map(_ => ()) .recoverWith { case UnexpectedResponse(_, _, _, status, body) => @@ -301,18 +300,18 @@ final class GitLabApiAlg[F[_]: Parallel]( case None => F.pure(mrOut) } - private def getUsernameToUserIdsMapping(repo: Repo, usernames: Set[String]): F[Map[String, Int]] = + private def getUsernameToUserIdsMapping(usernames: Set[String]): F[Map[String, Int]] = usernames.toList .parTraverse { username => - getUserIdForUsername(repo, username).map { userIdOpt => + getUserIdForUsername(username).map { userIdOpt => userIdOpt.map(userId => (username, userId)) } } .map(_.flatten.toMap) - private def getUserIdForUsername(repo: Repo, username: String): F[Option[Int]] = { + private def getUserIdForUsername(username: String): F[Option[Int]] = { val userIdOrError: F[Decoder.Result[Int]] = client - .get[Json](url.users.withQueryParam("username", username), modify(repo)) + .get[Json](url.users.withQueryParam("username", username), modify) .flatMap { usersReponse => usersReponse.hcursor.values match { case Some(users) => @@ -341,15 +340,15 @@ final class GitLabApiAlg[F[_]: Parallel]( .putWithBody[MergeRequestOut, UpdateState]( url.existingMergeRequest(repo, number), UpdateState(PullRequestState.Closed), - modify(repo) + modify ) .map(_.pullRequestOut) override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = - client.get(url.getBranch(repo, branch), modify(repo)) + client.get(url.getBranch(repo, branch), modify) override def getRepo(repo: Repo): F[RepoOut] = - client.get(url.repos(repo), modify(repo)) + client.get(url.repos(repo), modify) override def referencePullRequest(number: PullRequestNumber): String = s"!${number.value}" @@ -360,6 +359,6 @@ final class GitLabApiAlg[F[_]: Parallel]( number: PullRequestNumber, comment: String ): F[Comment] = - client.postWithBody(url.comments(repo, number), Comment(comment), modify(repo)) + client.postWithBody(url.comments(repo, number), Comment(comment), modify) } diff --git a/modules/core/src/main/scala/org/scalasteward/core/util/uri.scala b/modules/core/src/main/scala/org/scalasteward/core/util/uri.scala index 614d8b88d3..b21dfd3865 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/util/uri.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/util/uri.scala @@ -17,7 +17,7 @@ package org.scalasteward.core.util import cats.syntax.all._ -import io.circe.{Decoder, KeyDecoder, KeyEncoder} +import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder} import monocle.Optional import org.http4s.Uri import org.http4s.Uri.{Authority, Scheme, UserInfo} @@ -26,16 +26,19 @@ object uri { implicit val uriDecoder: Decoder[Uri] = Decoder[String].emap(s => Uri.fromString(s).leftMap(_.getMessage)) + implicit val uriEncoder: Encoder[Uri] = + Encoder[String].contramap[Uri](_.renderString) + implicit val uriKeyDecoder: KeyDecoder[Uri] = KeyDecoder.instance(Uri.fromString(_).toOption) implicit val uriKeyEncoder: KeyEncoder[Uri] = KeyEncoder.instance(_.renderString) - val withAuthority: Optional[Uri, Authority] = + private val withAuthority: Optional[Uri, Authority] = Optional[Uri, Authority](_.authority)(authority => _.copy(authority = Some(authority))) - val authorityWithUserInfo: Optional[Authority, UserInfo] = + private val authorityWithUserInfo: Optional[Authority, UserInfo] = Optional[Authority, UserInfo](_.userInfo)(userInfo => _.copy(userInfo = Some(userInfo))) val withUserInfo: Optional[Uri, UserInfo] = diff --git a/modules/core/src/test/scala/org/scalasteward/core/application/CliTest.scala b/modules/core/src/test/scala/org/scalasteward/core/application/CliTest.scala index 0ec3a3ddc9..ffbe23d46f 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/application/CliTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/application/CliTest.scala @@ -4,9 +4,8 @@ import better.files.File import cats.data.Validated.Valid import munit.FunSuite import org.http4s.syntax.literals._ -import org.scalasteward.core.application.Cli.EnvVar import org.scalasteward.core.application.Cli.ParseResult._ -import org.scalasteward.core.application.Config.StewardUsage +import org.scalasteward.core.application.Cli.{EnvVar, Usage} import org.scalasteward.core.forge.ForgeType import org.scalasteward.core.forge.github.GitHubApp import org.scalasteward.core.util.Nel @@ -14,7 +13,7 @@ import scala.concurrent.duration._ class CliTest extends FunSuite { test("parseArgs: example") { - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs( + val Success(Usage.Regular(obtained)) = Cli.parseArgs( List( List("--workspace", "a"), List("--repos-file", "b"), @@ -69,7 +68,7 @@ class CliTest extends FunSuite { assert(!obtained.bitbucketServerCfg.useDefaultReviewers) } - val minimumRequiredParams = List( + private val minimumRequiredParams = List( List("--workspace", "a"), List("--repos-file", "b"), List("--git-author-email", "d"), @@ -79,7 +78,7 @@ class CliTest extends FunSuite { ) test("parseArgs: minimal example") { - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs( + val Success(Usage.Regular(obtained)) = Cli.parseArgs( minimumRequiredParams.flatten ) @@ -92,7 +91,7 @@ class CliTest extends FunSuite { } test("parseArgs: enable sandbox") { - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs( + val Success(Usage.Regular(obtained)) = Cli.parseArgs( List( List("--workspace", "a"), List("--repos-file", "b"), @@ -123,7 +122,7 @@ class CliTest extends FunSuite { } test("parseArgs: disable sandbox") { - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs( + val Success(Usage.Regular(obtained)) = Cli.parseArgs( List( List("--workspace", "a"), List("--repos-file", "b"), @@ -157,7 +156,7 @@ class CliTest extends FunSuite { List("--gitlab-merge-when-pipeline-succeeds"), List("--gitlab-required-reviewers", "5") ) - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs(params.flatten) + val Success(Usage.Regular(obtained)) = Cli.parseArgs(params.flatten) assert(obtained.gitLabCfg.mergeWhenPipelineSucceeds) assertEquals(obtained.gitLabCfg.requiredReviewers, Some(5)) @@ -174,7 +173,7 @@ class CliTest extends FunSuite { } test("parseArgs: validate-repo-config") { - val Success(StewardUsage.ValidateRepoConfig(file)) = Cli.parseArgs( + val Success(Usage.ValidateRepoConfig(file)) = Cli.parseArgs( List( List("validate-repo-config", "file.conf") ).flatten @@ -188,7 +187,7 @@ class CliTest extends FunSuite { List("--forge-type", "azure-repos"), List("--do-not-fork") ) - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs(params.flatten) + val Success(Usage.Regular(obtained)) = Cli.parseArgs(params.flatten) assert(obtained.forgeCfg.doNotFork) } @@ -204,7 +203,7 @@ class CliTest extends FunSuite { val params = minimumRequiredParams ++ List( List("--forge-type", "bitbucket") ) - val Success(StewardUsage.Regular(obtained)) = Cli.parseArgs(params.flatten) + val Success(Usage.Regular(obtained)) = Cli.parseArgs(params.flatten) assert(!obtained.forgeCfg.addLabels) } diff --git a/modules/core/src/test/scala/org/scalasteward/core/data/VersionTest.scala b/modules/core/src/test/scala/org/scalasteward/core/data/VersionTest.scala index 505bbdd330..72dbec4832 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/data/VersionTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/data/VersionTest.scala @@ -222,6 +222,7 @@ class VersionTest extends DisciplineSuite { ("10000000", List("20000000"), Some("20000000")), ("1032048a", List("2032048a4c2"), Some("2032048a4c2")), ("0.1.1-3dfde9d7", List("0.2.1-485fdf3b"), None), + ("1.0.0+1319.ae77058", List("1.0.0+1320.38b57aa"), Some("1.0.0+1320.38b57aa")), ("0.1.1", List("0.2.1-485fdf3b"), None), ("0.1.1-ALPHA", List("0.2.1-485fdf3b"), None), ("0.1.1-ALPHA", List("0.2.1-BETA"), None), diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlgTest.scala index 70b11e548b..c30d67fde7 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/azurerepos/AzureReposApiAlgTest.scala @@ -18,6 +18,7 @@ import org.scalasteward.core.mock.{MockEff, MockState} class AzureReposApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) private val repo = Repo("scala-steward-org", "scala-steward") private val apiHost = uri"https://dev.azure.com" @@ -175,7 +176,7 @@ class AzureReposApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val forgeCfg = config.forgeCfg.copy(apiHost = apiHost, tpe = ForgeType.AzureRepos) private val azureReposCfg = AzureReposCfg(organization = Some("azure-org")) - private val azureReposApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, azureReposCfg, user) + private val azureReposApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, azureReposCfg, userM) test("getRepo") { val obtained = azureReposApiAlg.getRepo(repo).runA(state) diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlgTest.scala index be66fa5d47..4c06bc6824 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucket/BitbucketApiAlgTest.scala @@ -21,6 +21,7 @@ import org.scalasteward.core.mock.{MockEff, MockState} class BitbucketApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) private val basicAuth = Authorization(BasicCredentials(user.login, user.accessToken)) private val auth = HttpApp[MockEff] { request => @@ -209,7 +210,7 @@ class BitbucketApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val forgeCfg = config.forgeCfg.copy(tpe = ForgeType.Bitbucket) private val bitbucketCfg = BitbucketCfg(useDefaultReviewers = true) - private val bitbucketApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, bitbucketCfg, user) + private val bitbucketApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, bitbucketCfg, userM) private val prUrl = uri"https://bitbucket.org/fthomas/base.g8/pullrequests/2" private val repo = Repo("fthomas", "base.g8") diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlgTest.scala index 5b33d287a8..a8a6bf691e 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/bitbucketserver/BitbucketServerApiAlgTest.scala @@ -22,6 +22,7 @@ class BitbucketServerApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] private val repo = Repo("scala-steward-org", "scala-steward") private val main = Branch("main") private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) private val basicAuth = Authorization(BasicCredentials(user.login, user.accessToken)) private val auth = HttpApp[MockEff] { request => @@ -112,7 +113,7 @@ class BitbucketServerApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] private val forgeCfg = config.forgeCfg.copy(tpe = ForgeType.BitbucketServer) private val bitbucketServerApiAlg = ForgeSelection - .forgeApiAlg[MockEff](forgeCfg, BitbucketServerCfg(useDefaultReviewers = false), user) + .forgeApiAlg[MockEff](forgeCfg, BitbucketServerCfg(useDefaultReviewers = false), userM) test("createPullRequest") { val data = NewPullRequestData( @@ -146,7 +147,7 @@ class BitbucketServerApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] reviewers = Nil ) val apiAlg = ForgeSelection - .forgeApiAlg[MockEff](forgeCfg, BitbucketServerCfg(useDefaultReviewers = true), user) + .forgeApiAlg[MockEff](forgeCfg, BitbucketServerCfg(useDefaultReviewers = true), userM) val pr = apiAlg.createPullRequest(repo, data).runA(state) val expected = PullRequestOut( diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/gitea/GiteaApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/gitea/GiteaApiAlgTest.scala index 80f066bd0c..dcfa749814 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/gitea/GiteaApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/gitea/GiteaApiAlgTest.scala @@ -20,6 +20,7 @@ import org.scalasteward.core.mock.{MockEff, MockState} class GiteaApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) private val repo = Repo("foo", "baz") private val basicAuth = Authorization(BasicCredentials(user.login, user.accessToken)) @@ -61,7 +62,7 @@ class GiteaApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { tpe = ForgeType.Gitea, apiHost = config.forgeCfg.apiHost / "api" / "v1" ) - private val giteaAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, GiteaCfg(), user) + private val giteaAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, GiteaCfg(), userM) test("getRepo") { giteaAlg diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/github/GitHubApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/github/GitHubApiAlgTest.scala index e6f15da442..0c48e4a334 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/github/GitHubApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/github/GitHubApiAlgTest.scala @@ -23,6 +23,7 @@ import org.scalasteward.core.mock.{MockEff, MockState} class GitHubApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) private val basicAuth = Authorization(BasicCredentials(user.login, user.accessToken)) private val auth = HttpApp[MockEff] { request => @@ -198,7 +199,7 @@ class GitHubApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val state = MockState.empty.copy(clientResponses = auth <+> httpApp) private val forgeCfg = config.forgeCfg.copy(tpe = ForgeType.GitHub) - private val gitHubApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, GitHubCfg(), user) + private val gitHubApiAlg = ForgeSelection.forgeApiAlg[MockEff](forgeCfg, GitHubCfg(), userM) private val repo = Repo("fthomas", "base.g8") diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlgTest.scala index b6a224456a..20c9fa128b 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/forge/gitlab/GitLabApiAlgTest.scala @@ -27,6 +27,7 @@ import org.typelevel.ci.CIStringSyntax class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { private val user = AuthenticatedUser("user", "pass") + private val userM = MockEff.pure(user) object MergeWhenPipelineSucceedsMatcher extends QueryParamDecoderMatcher[Boolean]("merge_when_pipeline_succeeds") @@ -127,7 +128,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = None, removeSourceBranch = false ), - user + userM ) private val gitlabApiAlgNoFork = ForgeSelection.forgeApiAlg[MockEff]( @@ -137,7 +138,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = None, removeSourceBranch = false ), - user + userM ) private val gitlabApiAlgAutoMerge = ForgeSelection.forgeApiAlg[MockEff]( @@ -147,7 +148,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = None, removeSourceBranch = false ), - user + userM ) private val gitlabApiAlgRemoveSourceBranch = ForgeSelection.forgeApiAlg[MockEff]( @@ -157,7 +158,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = None, removeSourceBranch = true ), - user + userM ) private val gitlabApiAlgLessReviewersRequired = ForgeSelection.forgeApiAlg[MockEff]( @@ -167,7 +168,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = Some(0), removeSourceBranch = false ), - user + userM ) private val gitlabApiAlgWithAssigneeAndReviewers = ForgeSelection.forgeApiAlg[MockEff]( @@ -177,7 +178,7 @@ class GitLabApiAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] { requiredReviewers = Some(0), removeSourceBranch = false ), - user + userM ) private val data = UpdateData( diff --git a/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala b/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala index 4e5b7f800a..955eb6641c 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala @@ -2,8 +2,8 @@ package org.scalasteward.core.mock import better.files.File import org.http4s.Uri +import org.scalasteward.core.application.Cli import org.scalasteward.core.application.Cli.ParseResult.Success -import org.scalasteward.core.application.{Cli, Config} object MockConfig { val mockRoot: File = File.temp / "scala-steward" @@ -24,5 +24,5 @@ object MockConfig { "--add-labels", "--refresh-backoff-period=1hour" ) - val Success(Config.StewardUsage.Regular(config)) = Cli.parseArgs(args) + val Success(Cli.Usage.Regular(config)) = Cli.parseArgs(args) } diff --git a/modules/core/src/test/scala/org/scalasteward/core/mock/MockContext.scala b/modules/core/src/test/scala/org/scalasteward/core/mock/MockContext.scala index b0733d82d3..1bfcb8a466 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/mock/MockContext.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/mock/MockContext.scala @@ -1,12 +1,10 @@ package org.scalasteward.core.mock -import better.files.File import cats.data.Kleisli import cats.effect.kernel.Resource import cats.effect.unsafe.implicits.global import org.http4s.client.Client -import org.scalasteward.core.application.Context -import org.scalasteward.core.application.Context.StewardContext +import org.scalasteward.core.application.{Config, Context, ValidateRepoConfigContext} import org.scalasteward.core.edit.scalafix.ScalafixMigrationsLoader import org.scalasteward.core.io.FileAlgTest.ioFileAlg import org.scalasteward.core.io._ @@ -15,7 +13,6 @@ import org.scalasteward.core.repoconfig.RepoConfigLoader import org.scalasteward.core.update.artifact.ArtifactMigrationsLoader import org.scalasteward.core.util.UrlCheckerClient import org.typelevel.log4cats.Logger -import org.scalasteward.core.application.Config object MockContext { implicit private val client: Client[MockEff] = @@ -47,6 +44,7 @@ object MockContext { val context: Context[MockEff] = context(config) def context(stewardConfig: Config): Context[MockEff] = mockState.toRef.flatMap(Context.step1(stewardConfig).run).unsafeRunSync() - def validateRepoConfigContext(file: File): StewardContext.ValidateRepoConfig[MockEff] = - Context.initValidateRepoConfig(file) + + val validateRepoConfigContext: ValidateRepoConfigContext[MockEff] = + ValidateRepoConfigContext.step1 } diff --git a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/ValidateRepoConfigAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/ValidateRepoConfigAlgTest.scala index e4def2e154..b2a1c498d6 100644 --- a/modules/core/src/test/scala/org/scalasteward/core/repoconfig/ValidateRepoConfigAlgTest.scala +++ b/modules/core/src/test/scala/org/scalasteward/core/repoconfig/ValidateRepoConfigAlgTest.scala @@ -1,76 +1,47 @@ package org.scalasteward.core.repoconfig -import better.files.File import cats.effect.ExitCode -import cats.effect.unsafe.implicits.global -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import org.scalasteward.core.mock.{MockContext, MockState} - -class ValidateRepoConfigAlgTest extends munit.FunSuite { - - def configFile(content: String) = FunFixture[(File, ExitCode)]( - setup = { _ => - val tmpFile = - File( - Files.write( - Files.createTempFile(".scala-steward", ".conf"), - content.getBytes(StandardCharsets.UTF_8) - ) - ) - - val obtained = MockContext - .validateRepoConfigContext(tmpFile) - .runF - .runA(MockState.empty) - .unsafeRunSync() - - (tmpFile, obtained) - }, - teardown = { case (file, _) => - file.delete() - } - ) - - configFile( - """|updates.pin = [ - | { groupId = "org.scala-lang", artifactId="scala3-library", version = "3.1." }, - | { groupId = "org.scala-lang", artifactId="scala3-library_sjs1", version = "3.1." }, - | { groupId = "org.scala-js", artifactId="sbt-scalajs", version = "1.10." } - |]""".stripMargin - ) - .test("accepts valid config") { case (_, obtained) => - assertEquals(obtained, ExitCode.Success) - } +import munit.CatsEffectSuite +import org.scalasteward.core.mock.MockConfig.mockRoot +import org.scalasteward.core.mock.MockContext.validateRepoConfigContext.validateRepoConfigAlg +import org.scalasteward.core.mock.MockState + +class ValidateRepoConfigAlgTest extends CatsEffectSuite { + test("accepts valid config") { + val file = mockRoot / ".scala-steward.conf" + val content = + """|updates.pin = [ + | { groupId = "org.scala-lang", artifactId="scala3-library", version = "3.1." }, + | { groupId = "org.scala-js", artifactId="sbt-scalajs", version = "1.10." } + |]""".stripMargin + val state = MockState.empty.addFiles(file -> content) + val obtained = state.flatMap(validateRepoConfigAlg.validateAndReport(file).runA) + assertIO(obtained, ExitCode.Success) + } - configFile( - """|updates.pin =? [ - | { groupId = "org.scala-lang", artifactId="scala3-library", version = "3.1." }, - | { groupId = "org.scala-lang", artifactId="scala3-library_sjs1", version = "3.1." }, - | { groupId = "org.scala-js", artifactId="sbt-scalajs", version = "1.10." }, - |]""".stripMargin - ) - .test("rejects config with a parsing failure") { case (_, obtained) => - assertEquals(obtained, ExitCode.Error) - } + test("rejects config with a parsing failure") { + val file = mockRoot / ".scala-steward.conf" + val content = + """|updates.pin =? [ + | { groupId = "org.scala-lang", artifactId="scala3-library", version = "3.1." }, + | { groupId = "org.scala-js", artifactId="sbt-scalajs", version = "1.10." }, + |]""".stripMargin + val state = MockState.empty.addFiles(file -> content) + val obtained = state.flatMap(validateRepoConfigAlg.validateAndReport(file).runA) + assertIO(obtained, ExitCode.Error) + } - configFile( - """|updatePullRequests = 123 - | - |""".stripMargin - ) - .test("rejects config with a decoding failure") { case (_, obtained) => - assertEquals(obtained, ExitCode.Error) - } + test("rejects config with a decoding failure") { + val file = mockRoot / ".scala-steward.conf" + val content = """updatePullRequests = 123""".stripMargin + val state = MockState.empty.addFiles(file -> content) + val obtained = state.flatMap(validateRepoConfigAlg.validateAndReport(file).runA) + assertIO(obtained, ExitCode.Error) + } test("rejects non-existent config file") { - val nonExistentFile = File("/", "scripts", "script") - val obtained = MockContext - .validateRepoConfigContext(nonExistentFile) - .runF - .runA(MockState.empty) - .unsafeRunSync() - - assertEquals(obtained, ExitCode.Error) + val file = mockRoot / ".scala-steward.conf" + val obtained = validateRepoConfigAlg.validateAndReport(file).runA(MockState.empty) + assertIO(obtained, ExitCode.Error) } } diff --git a/modules/docs/mdoc/faq.md b/modules/docs/mdoc/faq.md index d242c086e8..4b0ec7f177 100644 --- a/modules/docs/mdoc/faq.md +++ b/modules/docs/mdoc/faq.md @@ -45,6 +45,12 @@ Updates for `sbt` and `scalafmt` can be controlled by using the following `group { groupId = "org.scalameta", artifactId = "scalafmt-core" } ``` +Updates for the Scala 2 or Scala 3 version can be controlled by using the respective `groupId` and `artifactId`: +```properties +{ groupId = "org.scala-lang", artifactId = "scala-library" } +{ groupId = "org.scala-lang", artifactId = "scala3-library" } +``` + ## Can Scala Steward update multiple branches in a repository? Yes! You can update multiple branches of a repository by adding it several times to the "repos.md" file diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e6a99e46c1..218df8bee3 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -32,7 +32,7 @@ object Dependencies { val http4sJdkhttpClient = "org.http4s" %% "http4s-jdk-http-client" % "1.0.0-M9" val log4catsSlf4j = "org.typelevel" %% "log4cats-slf4j" % "2.6.0" val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.4.14" - val jjwtApi = "io.jsonwebtoken" % "jjwt-api" % "0.11.5" + val jjwtApi = "io.jsonwebtoken" % "jjwt-api" % "0.12.3" val jjwtImpl = "io.jsonwebtoken" % "jjwt-impl" % jjwtApi.revision val jjwtJackson = "io.jsonwebtoken" % "jjwt-jackson" % jjwtApi.revision val millScriptVersion = "0.11.0-M10" diff --git a/project/build.properties b/project/build.properties index e8a1e246e8..abbbce5da4 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.7 +sbt.version=1.9.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index 4f37ede861..81dc31e691 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -11,4 +11,4 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.1") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9") -addSbtPlugin("org.typelevel" % "sbt-typelevel-mergify" % "0.6.3") +addSbtPlugin("org.typelevel" % "sbt-typelevel-mergify" % "0.6.4")