Skip to content
Permalink
Browse files

update logic to read extra scalafix rules at startup

  • Loading branch information
manuelcueto committed Oct 23, 2019
1 parent 00bde12 commit bbb12e852080e647d69eba7c347f0755170b571f

This file was deleted.

@@ -37,6 +37,8 @@ The [`git-ask-pass` option](https://git-scm.com/docs/gitcredentials) must specif
- either the plain text password corresponding to the configured `${LOGIN}`,
- or (recommended) an authentication token corresponding to `${LOGIN}` (with appropriate permissions to watch the repositories; e.g. [Create a personal access token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line) for GitHub).

You can also provide a `--scalafix-migrations` option with the path to a file containing scalafix migrations.
More information can be found [here][migrations]
### Private repositories

If you run Scala Steward for your own private projects, the option `--do-not-fork` can be required, not to fork.
@@ -103,3 +105,4 @@ docker run -v $PWD:/opt/scala-steward \

`BITBUCKET_USERNAME=<myuser> BITBUCKET_PASSWORD=<mypass> ./run.sh`

[migrations]: https://github.com/fthomas/scala-steward/blob/master/docs/scalafix-migrations.md
@@ -27,10 +27,24 @@ accessible via the [github:][using-github] or [http:][using-http] schemes.
## Adding migration rules to Scala Steward

After you have written a new migration rule for a new version of your project,
Scala Steward needs to be made aware of it. To do so, your project needs to provide
a file `".scalafix-migrations.conf"` in the project's root, and it should contain
a list of json objects representing a migration like in [here][migrations-conf].
Providing an empty list or no file will result in no fixes applied to that specific Repo.
Scala Steward needs to be made aware of it. Creating a pull request that adds
the new rule to the list of [migrations][migrations] is enough for that. Once
that pull request is merged, Scala Steward will start using this migration.
When running Scala Steward you can specify a file containing extra migrations
that might not be present in the [default list][migrations].
You can also specify if you want the default list to be disabled.

The file should look like this:
```json
{
"disableDefaults": true,
"extraMigrations": [{
"groupId": "org.ice.cream",
"artifactIds": ["yumyum-.*"],
"newVersion": "1.0.0",
"rewriteRules": ["awesome rewrite rule"]}
]}
```

Pull requests that added migration rules can be found [here][scalafix-prs].

@@ -39,5 +53,4 @@ Pull requests that added migration rules can be found [here][scalafix-prs].
[scalafix-dev-guide]: https://scalacenter.github.io/scalafix/docs/developers/setup.html
[using-github]: https://scalacenter.github.io/scalafix/docs/developers/sharing-rules.html#using-github
[using-http]: https://scalacenter.github.io/scalafix/docs/developers/sharing-rules.html#using-http
[scalafix-prs]: https://github.com/fthomas/scala-steward/pulls?q=label%3Ascalafix-migration
[migrations-conf]: https://github.com/fthomas/scala-steward/blob/master/.scalafix-migrations.conf
[scalafix-prs]: https://github.com/fthomas/scala-steward/pulls?q=label%3Ascalafix-migration
@@ -51,7 +51,8 @@ object Cli {
ignoreOptsFiles: Boolean = false,
envVar: List[EnvVar] = Nil,
pruneRepos: Boolean = false,
processTimeout: FiniteDuration = 10.minutes
processTimeout: FiniteDuration = 10.minutes,
scalafixMigrations: Option[String] = None
)

final case class EnvVar(name: String, value: String)
@@ -62,7 +62,8 @@ final case class Config(
ignoreOptsFiles: Boolean,
envVars: List[EnvVar],
pruneRepos: Boolean,
processTimeout: FiniteDuration
processTimeout: FiniteDuration,
scalafixMigrations: Option[File]
) {
def vcsUser[F[_]](implicit F: Sync[F]): F[AuthenticatedUser] = {
val urlWithUser = util.uri.withUserInfo.set(UserInfo(vcsLogin, None))(vcsApiHost).renderString
@@ -93,7 +94,8 @@ object Config {
ignoreOptsFiles = args.ignoreOptsFiles,
envVars = args.envVar,
pruneRepos = args.pruneRepos,
processTimeout = args.processTimeout
processTimeout = args.processTimeout,
scalafixMigrations = args.scalafixMigrations.map(_.toFile)
)
}
}
@@ -21,6 +21,7 @@ import cats.Traverse
import cats.implicits._
import fs2.Stream
import io.chrisdavenport.log4cats.Logger
import org.scalasteward.core.application.Config
import org.scalasteward.core.data.Update
import org.scalasteward.core.io.{isFileSpecificTo, isSourceFile, FileAlg, WorkspaceAlg}
import org.scalasteward.core.sbt.SbtAlg
@@ -32,6 +33,7 @@ import org.scalasteward.core.scalafix.MigrationAlg
final class EditAlg[F[_]](
implicit
fileAlg: FileAlg[F],
config: Config,
logger: Logger[F],
sbtAlg: SbtAlg[F],
streamCompiler: Stream.Compiler[F, F],
@@ -61,7 +63,7 @@ final class EditAlg[F[_]](
}

def applyScalafixMigrations(repo: Repo, update: Update): F[Unit] =
migrationAlg.loadMigrations(repo) >>= { migrations =>
migrationAlg.loadMigrations(config.scalafixMigrations) >>= { migrations =>
Nel.fromList(scalafix.findMigrations(migrations, update)).fold(F.unit) { migrations =>
logger.info(s"Applying migrations: $migrations") >> sbtAlg.runMigrations(repo, migrations)
}
@@ -150,7 +150,7 @@ final class NurtureAlg[F[_]](
.get(data.update.artifactId)
.flatTraverse(vcsExtraAlg.getReleaseNoteUrl(_, data.update))
branchName = vcs.createBranch(config.vcsType, data.fork, data.update)
migrations <- migrationAlg.loadMigrations(data.repo)
migrations <- migrationAlg.loadMigrations(config.scalafixMigrations)
requestData = NewPullRequestData.from(
data,
branchName,
@@ -39,16 +39,19 @@ final case class Migration(

override def hashCode: Int = Hash[Migration].hash(this)

@SuppressWarnings(Array("org.wartremover.warts.Equals"))
override def equals(x: Any): Boolean = {

implicit val regexEq: Eq[Regex] = Eq.by(_.regex)
lazy val other: Migration = x.asInstanceOf[Migration]

this.hashCode() === x.hashCode() &&
x.isInstanceOf[Migration] &&
other.artifactIds === this.artifactIds &&
other.groupId === this.groupId &&
other.newVersion === this.newVersion &&
other.rewriteRules === this.rewriteRules
this.ne(null) &&
other.ne(null) &&
this.hashCode() === other.hashCode() &&
this.artifactIds === other.artifactIds &&
this.groupId === other.groupId &&
this.newVersion === other.newVersion &&
this.rewriteRules === other.rewriteRules
}
}

@@ -16,35 +16,35 @@

package org.scalasteward.core.scalafix

import org.scalasteward.core.io.{FileAlg, WorkspaceAlg}
import org.scalasteward.core.vcs.data.Repo
import better.files.File
import org.scalasteward.core.io.FileAlg
import cats.implicits._
import cats.Monad
import io.circe.parser._
import io.chrisdavenport.log4cats.Logger

trait MigrationAlg[F[_]] {
def loadMigrations(repo: Repo): F[List[Migration]]
def loadMigrations(migrationsFile: Option[File]): F[List[Migration]]
}

object MigrationAlg {

def create[F[_]](
implicit fileAlg: FileAlg[F],
workspaceAlg: WorkspaceAlg[F],
logger: Logger[F],
F: Monad[F]
): MigrationAlg[F] = new MigrationAlg[F] {
override def loadMigrations(repo: Repo): F[List[Migration]] =
override def loadMigrations(migrationsFile: Option[File]): F[List[Migration]] =
for {
repoDir <- workspaceAlg.repoDir(repo)
migrationsFile <- fileAlg.readFile(repoDir / ".scalafix-migrations.conf")
migrations <- migrationsFile
.traverse(parse(_).flatMap(_.as[List[Migration]]))
fileContents <- migrationsFile.flatTraverse(fileAlg.readFile)
defaultMigrations = migrations
allMigrations <- fileContents
.traverse(parse(_).flatMap(_.as[ScalafixMigrations]))
.fold(
_ => logger.warn("Failed to parse migrations file") >> F.pure(List.empty[Migration]),
x => F.pure(x.combineAll)
_ => logger.warn("Failed to parse migrations file") >> defaultMigrations.pure[F],
_.fold(defaultMigrations)(_.migrations(defaultMigrations)).pure[F]
)
} yield migrations

} yield allMigrations
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2018-2019 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.scalafix

import io.circe.Decoder
import io.circe.generic.semiauto._

final case class ScalafixMigrations(
disableDefaults: Boolean = false,
extraMigrations: List[Migration] = List.empty
) {
def migrations(defaultMigrations: List[Migration]) =
if (disableDefaults) extraMigrations else defaultMigrations ++ extraMigrations
}

object ScalafixMigrations {
implicit val scalafixMigrationsDecoder: Decoder[ScalafixMigrations] = deriveDecoder
}
@@ -17,9 +17,60 @@
package org.scalasteward.core

import cats.implicits._
import org.scalasteward.core.data.{Update, Version}
import org.scalasteward.core.util._
import org.scalasteward.core.data.{GroupId, Update, Version}

package object scalafix {

val migrations: List[Migration] =
List(
Migration(
GroupId("co.fs2"),
Nel.of("fs2-.*".r),
Version("1.0.0"),
Nel.of("github:functional-streams-for-scala/fs2/v1?sha=v1.0.5")
),
Migration(
GroupId("com.spotify"),
Nel.of("scio-.*".r),
Version("0.7.0"),
Nel.of(
"github:spotify/scio/FixAvroIO?sha=v0.7.4",
"github:spotify/scio/AddMissingImports?sha=v0.7.4",
"github:spotify/scio/RewriteSysProp?sha=v0.7.4",
"github:spotify/scio/BQClientRefactoring?sha=v0.7.4"
)
),
Migration(
GroupId("org.http4s"),
Nel.of("http4s-.*".r),
Version("0.20.0"),
Nel.of("github:http4s/http4s/v0_20?sha=v0.20.11")
),
Migration(
GroupId("org.typelevel"),
Nel.of("cats-core".r),
Version("1.0.0"),
Nel.of(
"https://raw.githubusercontent.com/typelevel/cats/master/scalafix/rules/src/main/scala/fix/Cats_v1_0_0.scala"
)
),
Migration(
GroupId("org.scalatest"),
Nel.of("scalatest".r),
Version("3.1.0"),
Nel.of(
"https://raw.githubusercontent.com/scalatest/autofix/e4de53fa40fac423bd64d165ff36bde38ce52388/3.0.x/rules/src/main/scala/org/scalatest/autofix/v3_0_x/RenameDeprecatedPackage.scala",
"https://raw.githubusercontent.com/scalatest/autofix/e4de53fa40fac423bd64d165ff36bde38ce52388/3.1.x/rules/src/main/scala/org/scalatest/autofix/v3_1_x/RewriteDeprecatedNames.scala"
)
),
Migration(
GroupId("org.scalacheck"),
Nel.of("scalacheck".r),
Version("1.14.1"),
Nel.of("github:typelevel/scalacheck/v1_14_1?sha=3fc537dde9d8fdf951503a8d8b027a568d52d055")
)
)
def findMigrations(givenMigrations: List[Migration], update: Update): List[Migration] =
givenMigrations.filter { migration =>
update.groupId === migration.groupId &&
@@ -15,7 +15,6 @@ class EditAlgTest extends AnyFunSuite with Matchers {
val update = Update.Single(GroupId("org.typelevel"), "cats-core", "1.2.0", Nel.of("1.3.0"))
val file1 = File.temp / "ws/fthomas/scala-steward/build.sbt"
val file2 = File.temp / "ws/fthomas/scala-steward/project/Dependencies.scala"
val file3 = File.temp / "ws/fthomas/scala-steward/.scalafix-migrations.conf"

val state = editAlg
.applyUpdate(repo, update)
@@ -24,7 +23,6 @@ class EditAlgTest extends AnyFunSuite with Matchers {

state shouldBe MockState.empty.copy(
commands = Vector(
List("read", file3.pathAsString),
List("read", file1.pathAsString),
List("read", file2.pathAsString),
List("read", file1.pathAsString),
@@ -46,7 +44,6 @@ class EditAlgTest extends AnyFunSuite with Matchers {
val update = Update.Single(GroupId("org.scalameta"), "scalafmt-core", "2.0.0", Nel.of("2.1.0"))
val scalafmtFile = File.temp / "ws/fthomas/scala-steward/.scalafmt.conf"
val file1 = File.temp / "ws/fthomas/scala-steward/build.sbt"
val file2 = File.temp / "ws/fthomas/scala-steward/.scalafix-migrations.conf"

val state = editAlg
.applyUpdate(repo, update)
@@ -65,7 +62,6 @@ class EditAlgTest extends AnyFunSuite with Matchers {

state shouldBe MockState.empty.copy(
commands = Vector(
List("read", file2.pathAsString),
List("read", scalafmtFile.pathAsString),
List("read", scalafmtFile.pathAsString),
List("read", scalafmtFile.pathAsString),
@@ -41,7 +41,8 @@ object MockContext {
EnvVar("ANOTHER_TEST_VAR", "ALSO_GREAT")
),
pruneRepos = false,
processTimeout = 10.minutes
processTimeout = 10.minutes,
None
)

implicit val mockEffBracketThrowable: BracketThrowable[MockEff] = Sync[MockEff]

0 comments on commit bbb12e8

Please sign in to comment.
You can’t perform that action at this time.