Repository: scala-steward-org/scala-steward (scala)

Issue:
Link to documenation of Scalafix migrations
See https://github.com/ucb-bar/chisel-testers2/pull/128#issuecomment-589519442.

If a Scalafix has accompanying documentation it would be nice if Scala Steward would link to it in the PR. To support this, `scalafix.Migration` would need a new field of type `Option[Uri]` which would be used when creating the PR body.

Pull request description:
If a Scalafix has accompanying documentation Scala Steward will link to it in the PR.

fixes: #1348

Patch:
diff --git a/modules/core/src/main/resources/scalafix-migrations.conf b/modules/core/src/main/resources/scalafix-migrations.conf
index 2e719dcbc8..8ad130152e 100644
--- a/modules/core/src/main/resources/scalafix-migrations.conf
+++ b/modules/core/src/main/resources/scalafix-migrations.conf
@@ -3,9 +3,7 @@ migrations = [
     groupId: "org.scalaz",
     artifactIds: ["scalaz-.*"],
     newVersion: "7.3.0",
-    rewriteRules: [
-      "https://raw.githubusercontent.com/scalaz/scalazfix/master/rules/src/main/scala/scalaz/ScalazFix.scala"
-    ]
+    rewriteRules: ["https://raw.githubusercontent.com/scalaz/scalazfix/master/rules/src/main/scala/scalaz/ScalazFix.scala"]
   },
   {
     groupId: "co.fs2",
@@ -22,7 +20,8 @@ migrations = [
       "github:spotify/scio/AddMissingImports?sha=v0.7.4",
       "github:spotify/scio/RewriteSysProp?sha=v0.7.4",
       "github:spotify/scio/BQClientRefactoring?sha=v0.7.4"
-    ]
+    ],
+    doc: "https://github.com/spotify/scio/blob/v0.7.0/site/src/paradox/migrations/v0.7.0-Migration-Guide.md#automated-migration"
   },
   {
     groupId: "com.spotify",
@@ -34,28 +33,35 @@ migrations = [
     groupId: "dev.zio",
     artifactIds: ["zio-test"],
     newVersion: "1.0.0-RC18",
-    rewriteRules: ["github:zio/zio/CurriedAssert"]
+      rewriteRules: ["github:zio/zio/CurriedAssert"]
   },
   {
     groupId: "org.http4s",
     artifactIds: ["http4s-.*"],
     newVersion: "0.20.0",
-    rewriteRules: ["github:http4s/http4s/v0_20?sha=v0.20.11"]
+    rewriteRules: ["github:http4s/http4s/v0_20?sha=v0.20.11"],
+    doc: "https://github.com/http4s/http4s/blob/v0.20.0/docs/src/main/tut/upgrading.md"
   },
   {
     groupId: "org.typelevel",
     artifactIds: ["cats-core"],
     newVersion: "1.0.0",
-    rewriteRules: ["https://raw.githubusercontent.com/typelevel/cats/master/scalafix/rules/src/main/scala/fix/Cats_v1_0_0.scala"]
+    rewriteRules: ["https://raw.githubusercontent.com/typelevel/cats/master/scalafix/rules/src/main/scala/fix/Cats_v1_0_0.scala"],
+    doc: "https://github.com/typelevel/cats/blob/v1.0.0/scalafix/README.md"
+  },
+  {
+    groupId: "org.scalatest",
+    artifactIds: ["scalatest"],
+    newVersion: "3.0.8",
+    rewriteRules: ["https://raw.githubusercontent.com/scalatest/autofix/e4de53fa40fac423bd64d165ff36bde38ce52388/3.0.x/rules/src/main/scala/org/scalatest/autofix/v3_0_x/RenameDeprecatedPackage.scala"],
+    doc: "https://github.com/scalatest/autofix/blob/master/3.0.x/README.md"
   },
   {
     groupId: "org.scalatest",
     artifactIds: ["scalatest"],
     newVersion: "3.1.0",
-    rewriteRules: [
-      "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"
-    ]
+    rewriteRules: ["https://raw.githubusercontent.com/scalatest/autofix/e4de53fa40fac423bd64d165ff36bde38ce52388/3.1.x/rules/src/main/scala/org/scalatest/autofix/v3_1_x/RewriteDeprecatedNames.scala"],
+    doc: "https://github.com/scalatest/autofix/blob/master/3.1.x/README.md"
   },
   {
     groupId: "org.scalacheck",
@@ -67,6 +73,7 @@ migrations = [
     groupId: "org.manatki",
     artifactIds: ["derevo-.*"],
     newVersion: "0.11.0",
-    rewriteRules: ["https://gist.githubusercontent.com/REDNBLACK/9bc56ad71e4b01a63001339fa61b4cfd/raw/5f1cd32713c3235a83fe2d1d182bd71a001ef464/derevo-v0.11.0.scala"]
+    rewriteRules: ["https://gist.githubusercontent.com/REDNBLACK/9bc56ad71e4b01a63001339fa61b4cfd/raw/5f1cd32713c3235a83fe2d1d182bd71a001ef464/derevo-v0.11.0.scala"],
+    doc: "https://github.com/manatki/derevo/blob/0.11.3/README.md#breaking-changes-in-011"
   }
 ]
diff --git a/modules/core/src/main/scala/org/scalasteward/core/scalafix/Migration.scala b/modules/core/src/main/scala/org/scalasteward/core/scalafix/Migration.scala
index 6151da653e..d396bf26f1 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/scalafix/Migration.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/scalafix/Migration.scala
@@ -25,7 +25,8 @@ final case class Migration(
     groupId: GroupId,
     artifactIds: Nel[String],
     newVersion: Version,
-    rewriteRules: Nel[String]
+    rewriteRules: Nel[String],
+    doc: Option[String]
 )
 
 object Migration {
diff --git a/modules/core/src/main/scala/org/scalasteward/core/vcs/data/NewPullRequestData.scala b/modules/core/src/main/scala/org/scalasteward/core/vcs/data/NewPullRequestData.scala
index 1ae270ac0b..5f650fd3c3 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/vcs/data/NewPullRequestData.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/vcs/data/NewPullRequestData.scala
@@ -135,16 +135,25 @@ object NewPullRequestData {
 
   def migrationNote(migrations: List[Migration]): (Option[String], Option[Details]) =
     if (migrations.isEmpty) (None, None)
-    else
+    else {
+      val ruleList =
+        migrations.flatMap(_.rewriteRules.toList).map(rule => s"* $rule").mkString("\n")
+      val docList = migrations.flatMap(_.doc).map(uri => s"* $uri").mkString("\n")
+      val docSection =
+        if (docList.isEmpty)
+          ""
+        else
+          s"\n\nDocumentation:\n\n$docList"
       (
         Some("scalafix-migrations"),
         Some(
           Details(
             "Applied Migrations",
-            migrations.flatMap(_.rewriteRules.toList).map(rule => s"* $rule").mkString("\n")
+            s"$ruleList$docSection"
           )
         )
       )
+    }
 
   def semVerLabel(update: Update): Option[String] =
     for {


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/buildsystem/sbt/SbtAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/buildsystem/sbt/SbtAlgTest.scala
index 76c0e96db5..e718ade2e5 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/buildsystem/sbt/SbtAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/buildsystem/sbt/SbtAlgTest.scala
@@ -67,7 +67,8 @@ class SbtAlgTest extends AnyFunSuite with Matchers {
         GroupId("co.fs2"),
         Nel.of("fs2-core"),
         Version("1.0.0"),
-        Nel.of("github:functional-streams-for-scala/fs2/v1?sha=v1.0.5")
+        Nel.of("github:functional-streams-for-scala/fs2/v1?sha=v1.0.5"),
+        None
       )
     )
     val state = sbtAlg.runMigrations(repo, migrations).runS(MockState.empty).unsafeRunSync()
diff --git a/modules/core/src/test/scala/org/scalasteward/core/scalafix/MigrationAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/scalafix/MigrationAlgTest.scala
index 37a6526b1e..36d139b648 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/scalafix/MigrationAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/scalafix/MigrationAlgTest.scala
@@ -18,7 +18,8 @@ class MigrationAlgTest extends AnyFunSuite with Matchers {
          |    groupId: "org.ice.cream",
          |    artifactIds: ["yumyum-.*"],
          |    newVersion: "1.0.0",
-         |    rewriteRules: ["awesome rewrite rule"]
+         |    rewriteRules: ["awesome rewrite rule"],
+         |    doc: "https://scalacenter.github.io/scalafix/"
          |  }
          |]""".stripMargin
     val initialState = MockState.empty.add(extraFile, content)
@@ -26,12 +27,15 @@ class MigrationAlgTest extends AnyFunSuite with Matchers {
       MigrationAlg.loadMigrations[MockEff](Some(extraFile)).runA(initialState).unsafeRunSync
 
     migrations.size should be > 1
-    migrations should contain oneElementOf List(
-      Migration(
-        GroupId("org.ice.cream"),
-        Nel.of("yumyum-.*"),
-        Version("1.0.0"),
-        Nel.of("awesome rewrite rule")
+    (migrations should contain).oneElementOf(
+      List(
+        Migration(
+          GroupId("org.ice.cream"),
+          Nel.of("yumyum-.*"),
+          Version("1.0.0"),
+          Nel.of("awesome rewrite rule"),
+          Some("https://scalacenter.github.io/scalafix/")
+        )
       )
     )
   }
@@ -56,7 +60,8 @@ class MigrationAlgTest extends AnyFunSuite with Matchers {
         GroupId("org.ice.cream"),
         Nel.of("yumyum-.*"),
         Version("1.0.0"),
-        Nel.of("awesome rewrite rule")
+        Nel.of("awesome rewrite rule"),
+        None
       )
     )
   }
@@ -78,6 +83,6 @@ class MigrationAlgTest extends AnyFunSuite with Matchers {
   test("loadMigrations without extra file") {
     val migrations =
       MigrationAlg.loadMigrations[MockEff](None).runA(MockState.empty).unsafeRunSync()
-    migrations.size shouldBe 10
+    migrations.size shouldBe 11
   }
 }
diff --git a/modules/core/src/test/scala/org/scalasteward/core/vcs/data/NewPullRequestDataTest.scala b/modules/core/src/test/scala/org/scalasteward/core/vcs/data/NewPullRequestDataTest.scala
index 5d4e63b3a4..00b2744430 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/vcs/data/NewPullRequestDataTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/vcs/data/NewPullRequestDataTest.scala
@@ -99,7 +99,8 @@ class NewPullRequestDataTest extends AnyFunSuite with Matchers {
       update.groupId,
       Nel.of(update.artifactId.name),
       Version("0.7.0"),
-      Nel.of("I am a rewrite rule")
+      Nel.of("I am a rewrite rule"),
+      None
     )
     val (label, appliedMigrations) = NewPullRequestData.migrationNote(List(migration))
 
@@ -113,6 +114,31 @@ class NewPullRequestDataTest extends AnyFunSuite with Matchers {
       """.stripMargin.trim
   }
 
+  test("migrationNote: when artifact has migrations with docs") {
+    val update = Update.Single("com.spotify" % "scio-core" % "0.6.0", Nel.of("0.7.0"))
+    val migration = Migration(
+      update.groupId,
+      Nel.of(update.artifactId.name),
+      Version("0.7.0"),
+      Nel.of("I am a rewrite rule"),
+      Some("https://scalacenter.github.io/scalafix/")
+    )
+    val (label, appliedMigrations) = NewPullRequestData.migrationNote(List(migration))
+
+    label shouldBe Some("scalafix-migrations")
+    appliedMigrations.fold("")(_.toHtml) shouldBe
+      """<details>
+        |<summary>Applied Migrations</summary>
+        |
+        |* I am a rewrite rule
+        |
+        |Documentation:
+        |
+        |* https://scalacenter.github.io/scalafix/
+        |</details>
+      """.stripMargin.trim
+  }
+
   test("updateType") {
     val dependency = "com.example" % "foo" % "0.1"
     val single = Update.Single(dependency, Nel.of("0.2"))