Repository: scala-steward-org/scala-steward
Pull request #2636 (2022-06-16 20:39:21)

Add regex filtering for PR labels
Filter labels Scala Steward adds to PRs it creates by a regex to avoid visual clutter or conflicts with existing label conventions. Follows up on discussion that happened in #2523.


Patch:
diff --git a/docs/repo-specific-configuration.md b/docs/repo-specific-configuration.md
index 9485cd22..8ab35cc1 100644
--- a/docs/repo-specific-configuration.md
+++ b/docs/repo-specific-configuration.md
@@ -32,6 +32,11 @@ You can add `<YOUR_REPO>/.scala-steward.conf` to configure how Scala Steward upd
 #pullRequests.frequency = "0 0 ? * 3" # every thursday on midnight
 pullRequests.frequency = "7 days"
 
+# pullRequests.includeMatchedLabels allows to control which labels are added to PRs 
+# via a regex check each label is checked against.
+# Defaults to no regex (all labels are added) which is equivalent to ".*".
+pullRequests.includeMatchedLabels = "(.*semver.*)|(commit-count:n:.*)"
+
 # Only these dependencies which match the given patterns are updated.
 #
 # Each pattern must have `groupId`, and may have `artifactId` and `version`.
diff --git a/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala
index 5307c742..9db26051 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala
@@ -193,7 +193,8 @@ final class NurtureAlg[F[_]](config: VCSCfg)(implicit
         edits,
         existingArtifactUrlsMap,
         releaseRelatedUrls.getOrElse(List.empty),
-        filesWithOldVersion
+        filesWithOldVersion,
+        data.repoData.config.pullRequests.includeMatchedLabels
       )
       pr <- vcsApiAlg.createPullRequest(data.repo, requestData)
       _ <- vcsApiAlg
diff --git a/modules/core/src/main/scala/org/scalasteward/core/repoconfig/PullRequestsConfig.scala b/modules/core/src/main/scala/org/scalasteward/core/repoconfig/PullRequestsConfig.scala
index dd576113..8de1bf4e 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/repoconfig/PullRequestsConfig.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/repoconfig/PullRequestsConfig.scala
@@ -17,12 +17,17 @@
 package org.scalasteward.core.repoconfig
 
 import cats.{Eq, Monoid}
+import cats.implicits._
 import io.circe.Codec
 import io.circe.generic.extras.Configuration
 import io.circe.generic.extras.semiauto.deriveConfiguredCodec
 
+import java.util.regex.PatternSyntaxException
+import scala.util.matching.Regex
+
 final case class PullRequestsConfig(
-    frequency: Option[PullRequestFrequency] = None
+    frequency: Option[PullRequestFrequency] = None,
+    includeMatchedLabels: Option[Regex] = None
 ) {
   def frequencyOrDefault: PullRequestFrequency =
     frequency.getOrElse(PullRequestsConfig.defaultFrequency)
@@ -37,12 +42,21 @@ object PullRequestsConfig {
   implicit val pullRequestsConfigConfiguration: Configuration =
     Configuration.default.withDefaults
 
+  implicit val regexCodec: Codec[Regex] =
+    Codec
+      .from[String](implicitly, implicitly)
+      .iemap(s => Either.catchOnly[PatternSyntaxException](s.r).leftMap(_.getMessage))(_.regex)
+
   implicit val pullRequestsConfigCodec: Codec[PullRequestsConfig] =
     deriveConfiguredCodec
 
   implicit val pullRequestsConfigMonoid: Monoid[PullRequestsConfig] =
     Monoid.instance(
       PullRequestsConfig(),
-      (x, y) => PullRequestsConfig(frequency = x.frequency.orElse(y.frequency))
+      (x, y) =>
+        PullRequestsConfig(
+          frequency = x.frequency.orElse(y.frequency),
+          includeMatchedLabels = x.includeMatchedLabels.orElse(y.includeMatchedLabels)
+        )
     )
 }
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 9bb5edc6..793bd372 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
@@ -27,6 +27,8 @@ import org.scalasteward.core.git.{Branch, CommitMsg}
 import org.scalasteward.core.repoconfig.{GroupRepoConfig, RepoConfigAlg}
 import org.scalasteward.core.util.{Details, Nel}
 
+import scala.util.matching.Regex
+
 final case class NewPullRequestData(
     title: String,
     body: String,
@@ -181,9 +183,10 @@ object NewPullRequestData {
       edits: List[EditAttempt] = List.empty,
       artifactIdToUrl: Map[String, Uri] = Map.empty,
       releaseRelatedUrls: List[ReleaseRelatedUrl] = List.empty,
-      filesWithOldVersion: List[String] = List.empty
+      filesWithOldVersion: List[String] = List.empty,
+      includeMatchedLabels: Option[Regex] = None
   ): NewPullRequestData = {
-    val labels = labelsFor(data.update, edits, filesWithOldVersion)
+    val labels = labelsFor(data.update, edits, filesWithOldVersion, includeMatchedLabels)
     NewPullRequestData(
       title = CommitMsg
         .replaceVariables(data.repoConfig.commits.messageOrDefault)(
@@ -221,7 +224,8 @@ object NewPullRequestData {
   def labelsFor(
       update: Update,
       edits: List[EditAttempt],
-      filesWithOldVersion: List[String]
+      filesWithOldVersion: List[String],
+      includeMatchedLabels: Option[Regex]
   ): List[String] = {
     val commitCount = edits.flatMap(_.commits).size
     val commitCountLabel = "commit-count:" + (commitCount match {
@@ -239,8 +243,10 @@ object NewPullRequestData {
     val scalafixLabel = edits.collectFirst { case _: ScalafixEdit => "scalafix-migrations" }
     val oldVersionLabel = Option.when(filesWithOldVersion.nonEmpty)("old-version-remains")
 
-    updateType(update) ::
+    val allLabels = updateType(update) ::
       List(earlySemVerLabel, semVerSpecLabel, scalafixLabel, oldVersionLabel).flatten ++
       List(commitCountLabel)
+
+    allLabels.filter(label => includeMatchedLabels.fold(true)(_.matches(label)))
   }
 }


Test patch:
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 56d4f40e..8783e266 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
@@ -148,7 +148,7 @@ class NewPullRequestDataTest extends FunSuite {
     val edits = List(scalafixEdit)
     val appliedMigrations = migrationNote(edits)
     val update = ("a".g % "b".a % "1" -> "2").single
-    val labels = labelsFor(update, edits, List.empty)
+    val labels = labelsFor(update, edits, List.empty, None)
 
     assert(labels.contains("scalafix-migrations"))
     assertEquals(
@@ -178,7 +178,7 @@ class NewPullRequestDataTest extends FunSuite {
     val edits = List(scalafixEdit)
     val detail = migrationNote(edits)
     val update = ("a".g % "b".a % "1" -> "2").single
-    val labels = labelsFor(update, edits, List.empty)
+    val labels = labelsFor(update, edits, List.empty, None)
 
     assert(labels.contains("scalafix-migrations"))
     assertEquals(
@@ -220,7 +220,7 @@ class NewPullRequestDataTest extends FunSuite {
     val edits = List(scalafixEdit1, scalafixEdit2)
     val detail = migrationNote(edits)
     val update = ("a".g % "b".a % "1" -> "2").single
-    val labels = labelsFor(update, edits, List.empty)
+    val labels = labelsFor(update, edits, List.empty, None)
 
     assert(labels.contains("scalafix-migrations"))
     assertEquals(
@@ -267,7 +267,7 @@ class NewPullRequestDataTest extends FunSuite {
     val files = List("Readme.md", "travis.yml")
     val update = ("com.example".g % "foo".a % "0.1" %> "0.2").single
     val note = oldVersionNote(files, update)
-    val labels = labelsFor(update, List.empty, files)
+    val labels = labelsFor(update, List.empty, files, None)
 
     assert(labels.contains("old-version-remains"))
     assertEquals(
@@ -301,10 +301,21 @@ class NewPullRequestDataTest extends FunSuite {
       Some(Commit(dummySha1))
     )
 
-    val oneEdit = labelsFor(update, List(updateEdit), List.empty)
+    val oneEdit = labelsFor(update, List(updateEdit), List.empty, None)
     assert(clue(oneEdit).contains("commit-count:1"))
 
-    val twoEdits = labelsFor(update, List(updateEdit, scalafixEdit), List.empty)
+    val twoEdits = labelsFor(update, List(updateEdit, scalafixEdit), List.empty, None)
     assert(clue(twoEdits).contains("commit-count:n:2"))
   }
+
+  test("regex label filtering") {
+    val update = ("a".g % "b".a % "1" -> "2").single
+    val updateEdit = UpdateEdit(update, Commit(dummySha1))
+
+    val first = labelsFor(update, List(updateEdit), List.empty, Some("library-.+".r))
+    assertEquals(clue(first), List("library-update"))
+
+    val second = labelsFor(update, List(updateEdit), List.empty, Some("(.*update.*)|(.*count.*)".r))
+    assertEquals(clue(second), List("library-update", "commit-count:1"))
+  }
 }