Repository: scala-steward-org/scala-steward
Pull request #3466 (2024-11-05 19:12:15)

Add JSON metadata in PRs
This solves #3465. This ended up being quite straightforward as I didn't realize there was already a well-defined JSON serialization for updates. I think the only downside of this is a slightly larger payload that gets sent to GitHub each time a PR is created, but it does enable some interesting use cases of taking automated actions on the resulting PRs.


michaelmior: I'm not sure what's going on with the CI failure since at first glance, it appears unrelated to this change.
exoego: It appears that other test cases needs changes (JSON metadata in PR description) too, just my 2 cents from quick look.
michaelmior: @exoego The remaining failing tests seem to be unrelated to my changes.
mzuehlke: The failing test are caused by your changes. 
the first is about white space
the other tests are trying to read the last line of the PR which in the past had the labels
michaelmior: @mzuehlke Thanks. I seemed to have missed that. I fixed those failing tests.

Patch:
diff --git a/build.sbt b/build.sbt
index ac69f479..887ee170 100644
--- a/build.sbt
+++ b/build.sbt
@@ -356,14 +356,10 @@ lazy val dockerSettings = Def.settings(
       s"tar -xf $sbtTgz",
       s"rm -f $sbtTgz"
     ).mkString(" && ")
-    val millVer = Dependencies.millScriptVersion
+    val millVer = Dependencies.millwScriptVersion
     val millBin = s"$binDir/mill"
-    val releasePageVersion = millVer.split("-") match {
-      case Array(v, m, _*) if m.startsWith("M") => s"${v}-${m}"
-      case Array(v, _*)                         => v
-    }
     val installMill = Seq(
-      s"$curl $millBin https://github.com/lihaoyi/mill/releases/download/${releasePageVersion}/$millVer",
+      s"$curl $millBin https://github.com/lefou/millw/releases/download/${millVer}/millw-${millVer}",
       s"chmod +x $millBin"
     ).mkString(" && ")
     val csBin = s"$binDir/cs"
diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala
index e058b333..dfe15946 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala
@@ -46,7 +46,8 @@ final class MillAlg[F[_]](defaultResolver: Resolver)(implicit
       )
 
   private def runMill(buildRootDir: File) = {
-    val command = Nel("mill", List("-i", "--import", cliPluginCoordinate, "show", extractDeps))
+    val command =
+      Nel("mill", List("--no-server", "--import", cliPluginCoordinate, "show", extractDeps))
     processAlg.execSandboxed(command, buildRootDir)
   }
   private def runMillUnder011(buildRootDir: File, millBuildVersion: Option[Version]) = {
diff --git a/modules/core/src/main/scala/org/scalasteward/core/forge/data/NewPullRequestData.scala b/modules/core/src/main/scala/org/scalasteward/core/forge/data/NewPullRequestData.scala
index b5b186a5..4e659ac8 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/forge/data/NewPullRequestData.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/forge/data/NewPullRequestData.scala
@@ -17,6 +17,8 @@
 package org.scalasteward.core.forge.data
 
 import cats.syntax.all._
+import io.circe.Json
+import io.circe.syntax._
 import org.http4s.Uri
 import org.scalasteward.core.data._
 import org.scalasteward.core.edit.EditAttempt
@@ -112,9 +114,18 @@ object NewPullRequestData {
         |<sup>
         |${labels.mkString("labels: ", ", ", "")}
         |</sup>
-        |""".stripMargin.trim
+        |
+        |<!-- scala-steward = ${metadataJson(update, labels)} -->""".stripMargin.trim
   }
 
+  def metadataJson(update: Update, labels: List[String]): String =
+    Json
+      .obj(
+        "Update" -> update.asJson,
+        "Labels" -> Json.fromValues(labels.map(_.asJson))
+      )
+      .toString
+
   def renderUpdateInfoUrls(updateInfoUrls: List[UpdateInfoUrl]): Option[String] =
     Option.when(updateInfoUrls.nonEmpty) {
       updateInfoUrls
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 680c858f..6c2d89c5 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -14,7 +14,7 @@ object Dependencies {
   val circeParser = "io.circe" %% "circe-parser" % circeGeneric.revision
   val circeRefined = "io.circe" %% "circe-refined" % "0.15.1"
   val commonsIo = "commons-io" % "commons-io" % "2.17.0"
-  val coursierCore = "io.get-coursier" %% "coursier" % "2.1.15"
+  val coursierCore = "io.get-coursier" %% "coursier" % "2.1.16"
   val coursierSbtMaven =
     "io.get-coursier" %% "coursier-sbt-maven-repository" % coursierCore.revision
   val cron4sCore = "com.github.alonsodomin.cron4s" %% "cron4s-core" % "0.7.0"
@@ -33,7 +33,7 @@ object Dependencies {
   val jjwtApi = "io.jsonwebtoken" % "jjwt-api" % "0.12.6"
   val jjwtImpl = "io.jsonwebtoken" % "jjwt-impl" % jjwtApi.revision
   val jjwtJackson = "io.jsonwebtoken" % "jjwt-jackson" % jjwtApi.revision
-  val millScriptVersion = "0.11.0-M10"
+  val millwScriptVersion = "0.4.12"
   val monocleCore = "dev.optics" %% "monocle-core" % "3.3.0"
   val munit = "org.scalameta" %% "munit" % "1.0.2"
   val munitCatsEffect = "org.typelevel" %% "munit-cats-effect" % "2.0.0"


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/buildtool/mill/MillAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/buildtool/mill/MillAlgTest.scala
index c2969b4b..65ce9025 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/buildtool/mill/MillAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/buildtool/mill/MillAlgTest.scala
@@ -39,7 +39,7 @@ class MillAlgTest extends FunSuite {
     val millCmd = Cmd.execSandboxed(
       repoDir,
       "mill",
-      "-i",
+      "--no-server",
       "--import",
       "ivy:org.scala-steward::scala-steward-mill-plugin::0.18.0",
       "show",
diff --git a/modules/core/src/test/scala/org/scalasteward/core/forge/data/NewPullRequestDataTest.scala b/modules/core/src/test/scala/org/scalasteward/core/forge/data/NewPullRequestDataTest.scala
index ca72e2cb..3ae0a0f5 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/forge/data/NewPullRequestDataTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/forge/data/NewPullRequestDataTest.scala
@@ -60,7 +60,35 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "ch.qos.logback",
+          |          "artifactId" : {
+          |            "name" : "logback-classic",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "1.2.0",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "1.2.3"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
@@ -127,7 +155,35 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "ch.qos.logback",
+          |          "artifactId" : {
+          |            "name" : "logback-classic",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "1.2.0",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "1.2.3"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
@@ -191,7 +247,65 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "Grouped" : {
+          |      "name" : "my-group",
+          |      "title" : "The PR title",
+          |      "updates" : [
+          |        {
+          |          "ForArtifactId" : {
+          |            "crossDependency" : [
+          |              {
+          |                "groupId" : "ch.qos.logback",
+          |                "artifactId" : {
+          |                  "name" : "logback-classic",
+          |                  "maybeCrossName" : null
+          |                },
+          |                "version" : "1.2.0",
+          |                "sbtVersion" : null,
+          |                "scalaVersion" : null,
+          |                "configurations" : null
+          |              }
+          |            ],
+          |            "newerVersions" : [
+          |              "1.2.3"
+          |            ],
+          |            "newerGroupId" : null,
+          |            "newerArtifactId" : null
+          |          }
+          |        },
+          |        {
+          |          "ForArtifactId" : {
+          |            "crossDependency" : [
+          |              {
+          |                "groupId" : "com.example",
+          |                "artifactId" : {
+          |                  "name" : "foo",
+          |                  "maybeCrossName" : null
+          |                },
+          |                "version" : "1.0.0",
+          |                "sbtVersion" : null,
+          |                "scalaVersion" : null,
+          |                "configurations" : null
+          |              }
+          |            ],
+          |            "newerVersions" : [
+          |              "2.0.0"
+          |            ],
+          |            "newerGroupId" : null,
+          |            "newerArtifactId" : null
+          |          }
+          |        }
+          |      ]
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
@@ -248,7 +362,35 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "ch.qos.logback",
+          |          "artifactId" : {
+          |            "name" : "logback-classic",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "1.2.0",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "1.2.3"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
@@ -642,7 +784,38 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update, early-semver-patch, semver-spec-patch, commit-count:0
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "ch.qos.logback",
+          |          "artifactId" : {
+          |            "name" : "logback-classic",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "1.2.0",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "1.2.3"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update",
+          |    "early-semver-patch",
+          |    "semver-spec-patch",
+          |    "commit-count:0"
+          |  ]
+          |} -->""".stripMargin
 
     val expected = NewPullRequestData(
       title = "Update logback-classic to 1.2.3",
@@ -731,7 +904,70 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update, early-semver-patch, semver-spec-patch, early-semver-major, semver-spec-major, commit-count:0
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "Grouped" : {
+          |      "name" : "my-group",
+          |      "title" : null,
+          |      "updates" : [
+          |        {
+          |          "ForArtifactId" : {
+          |            "crossDependency" : [
+          |              {
+          |                "groupId" : "ch.qos.logback",
+          |                "artifactId" : {
+          |                  "name" : "logback-classic",
+          |                  "maybeCrossName" : null
+          |                },
+          |                "version" : "1.2.0",
+          |                "sbtVersion" : null,
+          |                "scalaVersion" : null,
+          |                "configurations" : null
+          |              }
+          |            ],
+          |            "newerVersions" : [
+          |              "1.2.3"
+          |            ],
+          |            "newerGroupId" : null,
+          |            "newerArtifactId" : null
+          |          }
+          |        },
+          |        {
+          |          "ForArtifactId" : {
+          |            "crossDependency" : [
+          |              {
+          |                "groupId" : "com.example",
+          |                "artifactId" : {
+          |                  "name" : "foo",
+          |                  "maybeCrossName" : null
+          |                },
+          |                "version" : "1.0.0",
+          |                "sbtVersion" : null,
+          |                "scalaVersion" : null,
+          |                "configurations" : null
+          |              }
+          |            ],
+          |            "newerVersions" : [
+          |              "2.0.0"
+          |            ],
+          |            "newerGroupId" : null,
+          |            "newerArtifactId" : null
+          |          }
+          |        }
+          |      ]
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update",
+          |    "early-semver-patch",
+          |    "semver-spec-patch",
+          |    "early-semver-major",
+          |    "semver-spec-major",
+          |    "commit-count:0"
+          |  ]
+          |} -->""".stripMargin
 
     val expected = NewPullRequestData(
       title = "Update for group my-group",
@@ -803,7 +1039,38 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update, early-semver-major, semver-spec-minor, commit-count:1
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "org.typelevel",
+          |          "artifactId" : {
+          |            "name" : "cats-effect",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "2.5.5",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "3.4.2"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update",
+          |    "early-semver-major",
+          |    "semver-spec-minor",
+          |    "commit-count:1"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
@@ -857,7 +1124,38 @@ class NewPullRequestDataTest extends FunSuite {
           |
           |<sup>
           |labels: library-update, early-semver-major, semver-spec-minor, commit-count:1
-          |</sup>""".stripMargin
+          |</sup>
+          |
+          |<!-- scala-steward = {
+          |  "Update" : {
+          |    "ForArtifactId" : {
+          |      "crossDependency" : [
+          |        {
+          |          "groupId" : "com.lihaoyi",
+          |          "artifactId" : {
+          |            "name" : "os-lib",
+          |            "maybeCrossName" : null
+          |          },
+          |          "version" : "0.7.8",
+          |          "sbtVersion" : null,
+          |          "scalaVersion" : null,
+          |          "configurations" : null
+          |        }
+          |      ],
+          |      "newerVersions" : [
+          |        "0.9.1"
+          |      ],
+          |      "newerGroupId" : null,
+          |      "newerArtifactId" : null
+          |    }
+          |  },
+          |  "Labels" : [
+          |    "library-update",
+          |    "early-semver-major",
+          |    "semver-spec-minor",
+          |    "commit-count:1"
+          |  ]
+          |} -->""".stripMargin
 
     assertEquals(body, expected)
   }
diff --git a/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala
index c0532143..f03182c7 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/nurture/NurtureAlgTest.scala
@@ -73,7 +73,39 @@ class NurtureAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] {
              |
              |<sup>
              |labels: library-update, early-semver-minor, semver-spec-minor, version-scheme:early-semver, commit-count:1
-             |</sup>""".stripMargin.trim,
+             |</sup>
+             |
+             |<!-- scala-steward = {
+             |  "Update" : {
+             |    "ForArtifactId" : {
+             |      "crossDependency" : [
+             |        {
+             |          "groupId" : "org.typelevel",
+             |          "artifactId" : {
+             |            "name" : "cats-effect",
+             |            "maybeCrossName" : "cats-effect_2.13"
+             |          },
+             |          "version" : "3.3.0",
+             |          "sbtVersion" : null,
+             |          "scalaVersion" : null,
+             |          "configurations" : null
+             |        }
+             |      ],
+             |      "newerVersions" : [
+             |        "3.4.0"
+             |      ],
+             |      "newerGroupId" : null,
+             |      "newerArtifactId" : null
+             |    }
+             |  },
+             |  "Labels" : [
+             |    "library-update",
+             |    "early-semver-minor",
+             |    "semver-spec-minor",
+             |    "version-scheme:early-semver",
+             |    "commit-count:1"
+             |  ]
+             |} -->""".stripMargin.trim,
       head = "scala-steward:update/cats-effect-3.4.0",
       base = baseBranch,
       labels = List(
@@ -90,7 +122,8 @@ class NurtureAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] {
   }
 
   test("preparePullRequest should not set labels if ForgeConfig.addLabels = false") {
-    def nextToLast(L: Array[String]) = L(L.size - 2)
+    def findLabelLine(L: Array[String]) =
+      L.zipWithIndex.find(_._1.contains("labels: ")).map(x => L(x._2)).getOrElse("")
 
     val config =
       MockConfig.config.copy(forgeCfg = MockConfig.config.forgeCfg.copy(addLabels = false))
@@ -116,9 +149,9 @@ class NurtureAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] {
     })
     nurtureAlg.preparePullRequest(updateData, edits).runA(state).map { obtained =>
       assert(obtained.labels.isEmpty)
-      val nextToLastLine = nextToLast(obtained.body.split("\n"))
+      val labelLine = findLabelLine(obtained.body.split("\n"))
       assertEquals(
-        nextToLastLine,
+        labelLine,
         "labels: library-update, early-semver-minor, semver-spec-minor, version-scheme:early-semver, commit-count:1"
       )
     }
@@ -127,7 +160,8 @@ class NurtureAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] {
   test(
     "preparePullRequest should set custom labels if PullRequestsConfig.customLabels is provided"
   ) {
-    def nextToLast(L: Array[String]) = L(L.size - 2)
+    def findLabelLine(L: Array[String]) =
+      L.zipWithIndex.find(_._1.contains("labels: ")).map(x => L(x._2)).getOrElse("")
 
     val nurtureAlg = context.nurtureAlg
     val dependency = "org.typelevel".g % ("cats-effect", "cats-effect_2.13").a % "3.3.0"
@@ -165,10 +199,10 @@ class NurtureAlgTest extends CatsEffectSuite with Http4sDsl[MockEff] {
         )
       )
 
-      val nextToLastLine = nextToLast(obtained.body.split("\n"))
+      val labelLine = findLabelLine(obtained.body.split("\n"))
 
       assertEquals(
-        nextToLastLine,
+        labelLine,
         "labels: custom-label-1, custom-label-2, library-update, early-semver-minor, semver-spec-minor, commit-count:1"
       )
     }