Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retract previously opened Pull Request #3488

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/repo-specific-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ updates.pin = [ { groupId = "com.example", artifactId="foo", version = "1.1." }
# Defaults to empty `[]` which mean Scala Steward will not ignore dependencies.
updates.ignore = [ { groupId = "org.acme", artifactId="foo", version = "1.0" } ]

# The dependencies which match the given pattern are retracted: Their existing Pull-Request will be closed.
#
# Each entry must have a `reason, a `doc` URL and a list of dependency patterns.
updates.retracted = [
{
reason = "Ignore version 3.6.0 as it is abandoned due to broken compatibility",
doc = "https://contributors.scala-lang.org/t/broken-scala-3-6-0-release/6792",
artifacts = [
{ groupId = "org.scala-lang", artifactId = "scala3-compiler", version = { exact = "3.6.0" } }
]
}
]

# The dependencies which match the given patterns are allowed to be updated to pre-release from stable.
# This also implies, that it will be allowed for snapshot versions to be updated to snapshots of different series.
#
Expand Down
20 changes: 20 additions & 0 deletions modules/core/src/main/resources/default.scala-steward.conf
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,23 @@ updates.ignore = [
{ groupId = "org.tpolecat", artifactId="doobie-hikari", version="1.0.0-RC6" },
{ groupId = "org.tpolecat", artifactId="doobie-postgres-circe", version="1.0.0-RC6" },
]

updates.retracted = [
{
reason = "Ignore version 3.6.0 as it is abandoned due to broken compatibility",
doc = "https://contributors.scala-lang.org/t/broken-scala-3-6-0-release/6792",
artifacts = [
{ groupId = "org.scala-lang", artifactId = "scala3-compiler", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-library", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "tasty-core", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala2-library-cc-tasty-experimental", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala2-library-tasty-experimental", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-language-server", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-presentation-compiler", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-staging", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scala3-tasty-inspector", version = { exact = "3.6.0" } },
{ groupId = "org.scala-lang", artifactId = "scaladoc", version = { exact = "3.6.0" } },
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@
logger.infoTotalTime(label) {
logger.attemptError.label(util.string.lineLeftRight(label), Some(label)) {
F.guarantee(
repoCacheAlg.checkCache(repo).flatMap { case (data, fork) =>
pruningAlg.needsAttention(data).flatMap {
_.traverse_(states => nurtureAlg.nurture(data, fork, states.map(_.update)))
}
},
for {
dataAndFork <- repoCacheAlg.checkCache(repo)
(data, fork) = dataAndFork
_ <- nurtureAlg.closeRetractedPullRequests(data)
states <- pruningAlg.needsAttention(data)
result <- states.traverse_(states =>
nurtureAlg.nurture(data, fork, states.map(_.update))

Check warning on line 59 in modules/core/src/main/scala/org/scalasteward/core/application/StewardAlg.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/application/StewardAlg.scala#L54-L59

Added lines #L54 - L59 were not covered by tests
)
} yield result,
gitAlg.removeClone(repo)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.scalasteward.core.forge.data._
import org.scalasteward.core.forge.{ForgeApiAlg, ForgeRepoAlg}
import org.scalasteward.core.git.{Branch, Commit, GitAlg}
import org.scalasteward.core.repoconfig.PullRequestUpdateStrategy
import org.scalasteward.core.repoconfig.{PullRequestUpdateStrategy, RetractedArtifact}
import org.scalasteward.core.util.logger.LoggerOps
import org.scalasteward.core.util.{Nel, UrlChecker}
import org.scalasteward.core.{git, util}
Expand Down Expand Up @@ -306,4 +306,30 @@
requestData <- preparePullRequest(data, edits)
_ <- forgeApiAlg.updatePullRequest(number: PullRequestNumber, data.repo, requestData)
} yield result

def closeRetractedPullRequests(data: RepoData): F[Unit] =
pullRequestRepository
.getRetractedPullRequests(data.repo, data.config.updates.retracted)
.flatMap {
_.traverse_ { case (oldPr, retractedArtifact) =>
closeRetractedPullRequest(data, oldPr, retractedArtifact)

Check warning on line 315 in modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala#L312-L315

Added lines #L312 - L315 were not covered by tests
}
}

private def closeRetractedPullRequest(
data: RepoData,
oldPr: PullRequestData[Id],
retractedArtifact: RetractedArtifact
): F[Unit] =
logger.attemptWarn.label_(

Check warning on line 324 in modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala#L324

Added line #L324 was not covered by tests
s"Closing retracted PR ${oldPr.url.renderString} for ${oldPr.update.show} because of '${retractedArtifact.reason}'"
) {

Check warning on line 326 in modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala#L326

Added line #L326 was not covered by tests
for {
_ <- pullRequestRepository.changeState(data.repo, oldPr.url, PullRequestState.Closed)
comment = retractedArtifact.retractionMsg
_ <- forgeApiAlg.commentPullRequest(data.repo, oldPr.number, comment)
_ <- forgeApiAlg.closePullRequest(data.repo, oldPr.number)
_ <- deleteRemoteBranch(data.repo, oldPr.updateBranch)
} yield F.unit

Check warning on line 333 in modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/nurture/NurtureAlg.scala#L328-L333

Added lines #L328 - L333 were not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.scalasteward.core.git.{Branch, Sha1}
import org.scalasteward.core.nurture.PullRequestRepository.Entry
import org.scalasteward.core.persistence.KeyValueStore
import org.scalasteward.core.repoconfig.RetractedArtifact
import org.scalasteward.core.update.UpdateAlg
import org.scalasteward.core.util.{DateTimeAlg, Timestamp}

Expand Down Expand Up @@ -80,6 +81,29 @@
}.flatten.toList.sortBy(_.number.value)
}

def getRetractedPullRequests(
repo: Repo,
allRetractedArtifacts: List[RetractedArtifact]
): F[List[(PullRequestData[Id], RetractedArtifact)]] =
kvStore.getOrElse(repo, Map.empty).map { pullRequets: Map[Uri, Entry] =>
pullRequets.flatMap {
case (
url,
Entry(baseSha1, u: Update.Single, PullRequestState.Open, _, number, updateBranch)
) =>
for {
prNumber <- number
retractedArtifact <- allRetractedArtifacts.find(_.isRetracted(u))
} yield {
val branch = updateBranch.getOrElse(git.branchFor(u, repo.branch))
val data =
PullRequestData[Id](url, baseSha1, u, PullRequestState.Open, prNumber, branch)
(data, retractedArtifact)
}
case _ => Map.empty

Check warning on line 103 in modules/core/src/main/scala/org/scalasteward/core/nurture/PullRequestRepository.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/nurture/PullRequestRepository.scala#L103

Added line #L103 was not covered by tests
}.toList
}

def findLatestPullRequest(
repo: Repo,
crossDependency: CrossDependency,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.repoconfig

import io.circe.Codec
import io.circe.generic.semiauto.deriveCodec
import org.scalasteward.core.data.Update

final case class RetractedArtifact(
reason: String,
doc: String,
artifacts: List[UpdatePattern] = List.empty
) {
def isRetracted(updateSingle: Update.Single): Boolean =
updateSingle.forArtifactIds.exists { updateForArtifactId =>
UpdatePattern
.findMatch(artifacts, updateForArtifactId, include = true)
.filteredVersions
.nonEmpty
}

def retractionMsg: String =
s"""|Retracted because of: ${reason}.
|
|Documentation: ${doc}
|""".stripMargin.trim

Check warning on line 40 in modules/core/src/main/scala/org/scalasteward/core/repoconfig/RetractedArtifact.scala

View check run for this annotation

Codecov / codecov/patch

modules/core/src/main/scala/org/scalasteward/core/repoconfig/RetractedArtifact.scala#L40

Added line #L40 was not covered by tests
}

object RetractedArtifact {
implicit val retractedPatternCodec: Codec[RetractedArtifact] =
deriveCodec
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ final case class UpdatesConfig(
allow: List[UpdatePattern] = List.empty,
allowPreReleases: List[UpdatePattern] = List.empty,
ignore: List[UpdatePattern] = List.empty,
retracted: List[RetractedArtifact] = List.empty,
limit: Option[NonNegInt] = defaultLimit,
fileExtensions: Option[List[String]] = None
) {
Expand Down Expand Up @@ -124,6 +125,7 @@ object UpdatesConfig {
allow = mergeAllow(x.allow, y.allow),
allowPreReleases = mergeAllow(x.allowPreReleases, y.allowPreReleases),
ignore = mergeIgnore(x.ignore, y.ignore),
retracted = x.retracted ::: y.retracted,
limit = x.limit.orElse(y.limit),
fileExtensions = mergeFileExtensions(x.fileExtensions, y.fileExtensions)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.scalasteward.core.mock.MockContext.context.pullRequestRepository
import org.scalasteward.core.mock.MockState.TraceEntry
import org.scalasteward.core.mock.MockState.TraceEntry.Cmd
import org.scalasteward.core.mock.{MockEff, MockState}
import org.scalasteward.core.repoconfig.{RetractedArtifact, UpdatePattern, VersionPattern}
import org.scalasteward.core.util.Nel

import java.util.concurrent.atomic.AtomicInteger
Expand Down Expand Up @@ -122,6 +123,33 @@ class PullRequestRepositoryTest extends FunSuite {
assertEquals(result, List.empty)
}

test("getRetractedPullRequests with no retractions defined") {
val (_, obtained) = beforeAndAfterPRCreation(portableScala) { repo =>
pullRequestRepository.getRetractedPullRequests(repo, List.empty)
}
assertEquals(obtained, List.empty[(PullRequestData[Id], RetractedArtifact)])
}

test("getRetractedPullRequests with retractions") {
val retractedPortableScala = RetractedArtifact(
"a reason",
"doc URI",
List(
UpdatePattern(
"org.portable-scala".g,
Some("sbt-scalajs-crossproject"),
Some(VersionPattern(exact = Some("1.0.0")))
)
)
)
val (_, obtained) = beforeAndAfterPRCreation(portableScala) { repo =>
pullRequestRepository.getRetractedPullRequests(repo, List(retractedPortableScala))
}
assertEquals(obtained.size, 1)
assertEquals(obtained.head._1.update, portableScala)
assertEquals(obtained.head._2, retractedPortableScala)
}

test("findLatestPullRequest ignores grouped updates") {
val (_, result) = beforeAndAfterPRCreation(groupedUpdate(portableScala)) { repo =>
pullRequestRepository.findLatestPullRequest(repo, portableScala.crossDependency, "1.0.0".v)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.scalasteward.core.repoconfig

import org.scalasteward.core.TestSyntax._

import munit.FunSuite

class RetractedArtifactTest extends FunSuite {
private val retractedArtifact = RetractedArtifact(
"a reason",
"doc URI",
List(
UpdatePattern(
"org.portable-scala".g,
Some("sbt-scalajs-crossproject"),
Some(VersionPattern(exact = Some("1.0.0")))
)
)
)

test("isRetracted") {
val update = ("org.portable-scala".g % "sbt-scalajs-crossproject".a % "0.9.2" %> "1.0.0").single
assert(retractedArtifact.isRetracted(update))
}

test("not isRetracted") {
val update = ("org.portable-scala".g % "sbt-scalajs-crossproject".a % "0.9.2" %> "0.9.3").single
assert(!retractedArtifact.isRetracted(update))
}
}
13 changes: 13 additions & 0 deletions modules/docs/mdoc/repo-specific-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ updates.pin = [ { groupId = "com.example", artifactId="foo", version = "1.1." }
# Defaults to empty `[]` which mean Scala Steward will not ignore dependencies.
updates.ignore = [ { groupId = "org.acme", artifactId="foo", version = "1.0" } ]

# The dependencies which match the given pattern are retracted: Their existing Pull-Request will be closed.
#
# Each entry must have a `reason, a `doc` URL and a list of dependency patterns.
updates.retracted = [
{
reason = "Ignore version 3.6.0 as it is abandoned due to broken compatibility",
doc = "https://contributors.scala-lang.org/t/broken-scala-3-6-0-release/6792",
artifacts = [
{ groupId = "org.scala-lang", artifactId = "scala3-compiler", version = { exact = "3.6.0" } }
]
}
]

# The dependencies which match the given patterns are allowed to be updated to pre-release from stable.
# This also implies, that it will be allowed for snapshot versions to be updated to snapshots of different series.
#
Expand Down