Skip to content

Commit 1e2253b

Browse files
committed
Switch to ArtifactUpdateCandidates
# Conflicts: # modules/core/src/main/scala/org/scalasteward/core/update/FilterAlg.scala
1 parent 7499da8 commit 1e2253b

File tree

17 files changed

+290
-164
lines changed

17 files changed

+290
-164
lines changed

modules/benchmark/src/main/scala/org/scalasteward/benchmark/UpdatesConfigBenchmark.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class UpdatesConfigBenchmark {
3333
val newerVersions = Nel
3434
.of("2.0.0", "2.1.0", "2.1.1", "2.2.0", "3.0.0", "3.1.0", "3.2.1", "3.3.3", "4.0", "5.0")
3535
.map(Version.apply)
36-
val update = Update.ForArtifactId(dependency, newerVersions)
36+
val update = ArtifactUpdateCandidates(ArtifactForUpdate(dependency), newerVersions)
3737

3838
UpdatesConfig().keep(update)
3939
UpdatesConfig(allow = Some(List(UpdatePattern(groupId, None, None)))).keep(update)

modules/core/src/main/scala/org/scalasteward/core/data/Update.scala

Lines changed: 123 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,44 @@ import org.scalasteward.core.repoconfig.PullRequestGroup
2323
import org.scalasteward.core.util
2424
import org.scalasteward.core.util.Nel
2525

26+
case class ArtifactForUpdate(
27+
crossDependency: CrossDependency,
28+
newerGroupId: Option[GroupId] = None,
29+
newerArtifactId: Option[String] = None
30+
) {
31+
private val headDependency: Dependency = crossDependency.head
32+
def groupId: GroupId = headDependency.groupId
33+
def artifactId: ArtifactId = headDependency.artifactId
34+
def currentVersion: Version = headDependency.version
35+
}
36+
37+
trait ArtifactUpdateVersions {
38+
val artifactForUpdate: ArtifactForUpdate
39+
40+
val refersToUpdateVersions: Nel[Version]
41+
42+
def show: String
43+
}
44+
45+
/**
46+
* Captures possible ''candidate'' newer versions that we may update an artifact to.
47+
*
48+
* Compare with the other subclass of [[ArtifactUpdateVersions]], [[Update.ForArtifactId]],
49+
* which denotes the specific ''single'' next version ultimately used in a PR.
50+
*/
51+
case class ArtifactUpdateCandidates(
52+
artifactForUpdate: ArtifactForUpdate,
53+
newerVersions: Nel[Version]
54+
) extends ArtifactUpdateVersions {
55+
override val refersToUpdateVersions: Nel[Version] = newerVersions
56+
57+
def asSpecificUpdate(nextVersion: Version): Update.ForArtifactId =
58+
Update.ForArtifactId(artifactForUpdate, nextVersion)
59+
60+
override def show: String =
61+
s"${artifactForUpdate.groupId}:${artifactForUpdate.crossDependency.showArtifactNames} : ${Version.show((artifactForUpdate.currentVersion +: refersToUpdateVersions.toList)*)}"
62+
}
63+
2664
sealed trait Update {
2765

2866
def on[A](update: Update.Single => A, grouped: Update.Grouped => A): A = this match {
@@ -37,6 +75,13 @@ sealed trait Update {
3775

3876
object Update {
3977

78+
/**
79+
* Denotes the update of one or more artifacts, which have all matched the same
80+
* `pullRequests.grouping` config rule.
81+
*
82+
* An `Update.Grouped` PR looks like this:
83+
* [[https://github.com/guardian/etag-caching/pull/62]]
84+
*/
4085
final case class Grouped(
4186
name: String,
4287
title: Option[String],
@@ -49,47 +94,55 @@ object Update {
4994

5095
sealed trait Single extends Product with Serializable with Update {
5196
override val asSingleUpdates: List[Update.Single] = List(this)
97+
def artifactsForUpdate: Nel[ArtifactForUpdate]
5298
def forArtifactIds: Nel[ForArtifactId]
5399
def crossDependencies: Nel[CrossDependency]
54100
def dependencies: Nel[Dependency]
55101
def groupId: GroupId
56102
def artifactIds: Nel[ArtifactId]
57103
def mainArtifactId: String
104+
def showArtifacts: String
58105
def groupAndMainArtifactId: (GroupId, String) = (groupId, mainArtifactId)
59106
def currentVersion: Version
60-
def newerVersions: Nel[Version]
107+
def nextVersion: Version
61108

62109
final def name: String = Update.nameOf(groupId, mainArtifactId)
63110

64-
final def nextVersion: Version = newerVersions.head
111+
final override def show: String =
112+
s"$groupId:$showArtifacts : ${Version.show(currentVersion, nextVersion)}"
65113

66-
final override def show: String = {
67-
val artifacts = this match {
68-
case s: ForArtifactId => s.crossDependency.showArtifactNames
69-
case g: ForGroupId => g.crossDependencies.map(_.showArtifactNames).mkString_("{", ", ", "}")
70-
}
71-
val versions = {
72-
val vs0 = (currentVersion :: newerVersions).toList
73-
val vs1 = if (vs0.size > 6) vs0.take(3) ++ ("..." :: vs0.takeRight(3)) else vs0
74-
vs1.mkString("", " -> ", "")
75-
}
76-
s"$groupId:$artifacts : $versions"
77-
}
78-
79-
def withNewerVersions(versions: Nel[Version]): Update.Single = this match {
80-
case s @ ForArtifactId(_, _, _, _) =>
81-
s.copy(newerVersions = versions)
82-
case ForGroupId(forArtifactIds) =>
83-
ForGroupId(forArtifactIds.map(_.copy(newerVersions = versions)))
114+
def withNextVersion(nextVersion: Version): Update.Single = this match {
115+
case s: ForArtifactId =>
116+
s.copy(nextVersion = nextVersion)
117+
case g: ForGroupId =>
118+
g.copy(nextVersion = nextVersion)
84119
}
85120
}
86121

122+
/**
123+
* Denotes the update of a specific single artifact to some particular chosen next version.
124+
*
125+
* An update PR with a single `Update.ForArtifactId` looks like this:
126+
* [[https://github.com/guardian/etag-caching/pull/125]]
127+
*
128+
* In the other subclass of [[ArtifactUpdateVersions]], [[ArtifactUpdateCandidates]], _multiple_ possible candidate
129+
* newer versions are stored.
130+
*/
87131
final case class ForArtifactId(
88-
crossDependency: CrossDependency,
89-
newerVersions: Nel[Version],
90-
newerGroupId: Option[GroupId] = None,
91-
newerArtifactId: Option[String] = None
92-
) extends Single {
132+
artifactForUpdate: ArtifactForUpdate,
133+
nextVersion: Version
134+
) extends Single
135+
with ArtifactUpdateVersions {
136+
val crossDependency: CrossDependency = artifactForUpdate.crossDependency
137+
138+
override val refersToUpdateVersions: Nel[Version] = Nel.one(nextVersion)
139+
140+
override def artifactsForUpdate: Nel[ArtifactForUpdate] = Nel.one(artifactForUpdate)
141+
142+
override def showArtifacts: String = crossDependency.showArtifactNames
143+
144+
lazy val versionUpdate: Version.Update = Version.Update(currentVersion, nextVersion)
145+
93146
override def forArtifactIds: Nel[ForArtifactId] =
94147
Nel.one(this)
95148

@@ -100,7 +153,7 @@ object Update {
100153
crossDependency.dependencies
101154

102155
override def groupId: GroupId =
103-
crossDependency.head.groupId
156+
artifactForUpdate.groupId
104157

105158
override def artifactIds: Nel[ArtifactId] =
106159
dependencies.map(_.artifactId)
@@ -109,17 +162,30 @@ object Update {
109162
artifactId.name
110163

111164
override def currentVersion: Version =
112-
crossDependency.head.version
165+
artifactForUpdate.currentVersion
113166

114167
def artifactId: ArtifactId =
115-
crossDependency.head.artifactId
168+
artifactForUpdate.artifactId
116169
}
117170

171+
/**
172+
* Denotes the update of several artifacts which all have the same Maven group-id,
173+
* and also are all are being updated ''from'' the same version, and updated ''to'' the same `nextVersion`.
174+
*
175+
* An `Update.ForGroupId` PR looks like this:
176+
* [[https://github.com/guardian/etag-caching/pull/128]]
177+
*
178+
*/
118179
final case class ForGroupId(
119-
forArtifactIds: Nel[ForArtifactId]
180+
artifactsForUpdate: Nel[ArtifactForUpdate],
181+
nextVersion: Version
120182
) extends Single {
183+
184+
override def forArtifactIds: Nel[ForArtifactId] =
185+
artifactsForUpdate.map(ForArtifactId(_, nextVersion))
186+
121187
override def crossDependencies: Nel[CrossDependency] =
122-
forArtifactIds.map(_.crossDependency)
188+
artifactsForUpdate.map(_.crossDependency)
123189

124190
override def dependencies: Nel[Dependency] =
125191
crossDependencies.flatMap(_.dependencies)
@@ -142,12 +208,12 @@ object Update {
142208
.getOrElse(artifactIds.head.name)
143209
}
144210

211+
override def showArtifacts: String =
212+
crossDependencies.map(_.showArtifactNames).mkString_("{", ", ", "}")
213+
145214
override def currentVersion: Version =
146215
dependencies.head.version
147216

148-
override def newerVersions: Nel[Version] =
149-
forArtifactIds.head.newerVersions
150-
151217
def artifactIdsPrefix: Option[String] =
152218
util.string.longestCommonPrefixGteq(artifactIds.map(_.name), 3)
153219
}
@@ -163,19 +229,23 @@ object Update {
163229

164230
def groupByArtifactIdName(updates: List[ForArtifactId]): List[ForArtifactId] = {
165231
val groups0 =
166-
updates.groupByNel(s => (s.groupId, s.artifactId.name, s.currentVersion, s.newerVersions))
232+
updates.groupByNel(s => (s.groupId, s.artifactId.name, s.currentVersion, s.nextVersion))
167233
val groups1 = groups0.values.map { group =>
168-
val dependencies = group.flatMap(_.crossDependency.dependencies).distinct.sorted
169-
group.head.copy(crossDependency = CrossDependency(dependencies))
234+
val dependencies = group.flatMap(_.dependencies).distinct.sorted
235+
val update: Update.ForArtifactId = group.head
236+
update.copy(artifactForUpdate =
237+
update.artifactForUpdate.copy(crossDependency = CrossDependency(dependencies))
238+
)
170239
}
171240
groups1.toList.distinct.sortBy(u => u: Update.Single)
172241
}
173242

174243
def groupByGroupId(updates: List[ForArtifactId]): List[Single] = {
175244
val groups0 =
176-
updates.groupByNel(s => (s.groupId, s.currentVersion, s.newerVersions))
177-
val groups1 = groups0.values.map { group =>
178-
if (group.tail.isEmpty) group.head else ForGroupId(group)
245+
updates.groupByNel(s => (s.groupId, s.versionUpdate))
246+
val groups1 = groups0.map { case ((_, versionUpdate), group) =>
247+
if (group.tail.isEmpty) group.head
248+
else ForGroupId(group.map(_.artifactForUpdate), versionUpdate.nextVersion)
179249
}
180250
groups1.toList.distinct.sorted
181251
}
@@ -197,7 +267,7 @@ object Update {
197267
}
198268

199269
implicit val SingleOrder: Order[Single] =
200-
Order.by((u: Single) => (u.crossDependencies, u.newerVersions))
270+
Order.by((u: Single) => (u.crossDependencies, u.nextVersion))
201271

202272
// Encoder and Decoder instances
203273

@@ -206,19 +276,28 @@ object Update {
206276
implicit private val forArtifactIdEncoder: Encoder[ForArtifactId] =
207277
Encoder.forProduct1("ForArtifactId")(identity[ForArtifactId]) {
208278
Encoder.forProduct4("crossDependency", "newerVersions", "newerGroupId", "newerArtifactId") {
209-
s => (s.crossDependency, s.newerVersions, s.newerGroupId, s.newerArtifactId)
279+
s =>
280+
(
281+
s.crossDependency,
282+
Seq(s.nextVersion),
283+
s.artifactForUpdate.newerGroupId,
284+
s.artifactForUpdate.newerArtifactId
285+
)
210286
}
211287
}
212288

213289
private val unwrappedForArtifactIdDecoder: Decoder[ForArtifactId] =
214290
Decoder.forProduct4("crossDependency", "newerVersions", "newerGroupId", "newerArtifactId") {
215291
(
216292
crossDependency: CrossDependency,
217-
newerVersions: Nel[Version],
293+
newerVersions: List[Version],
218294
newerGroupId: Option[GroupId],
219295
newerArtifactId: Option[String]
220296
) =>
221-
ForArtifactId(crossDependency, newerVersions, newerGroupId, newerArtifactId)
297+
ForArtifactId(
298+
ArtifactForUpdate(crossDependency, newerGroupId, newerArtifactId),
299+
newerVersions.head
300+
)
222301
}
223302

224303
private val forArtifactIdDecoderV2 =
@@ -236,7 +315,7 @@ object Update {
236315

237316
private val unwrappedForGroupIdDecoderV3: Decoder[ForGroupId] =
238317
Decoder.forProduct1("forArtifactIds") { (forArtifactIds: Nel[ForArtifactId]) =>
239-
ForGroupId(forArtifactIds)
318+
ForGroupId(forArtifactIds.map(_.artifactForUpdate), forArtifactIds.head.nextVersion)
240319
}
241320

242321
private val forGroupIdDecoderV3: Decoder[ForGroupId] =

modules/core/src/main/scala/org/scalasteward/core/data/Version.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ final case class Version(value: String) {
124124
object Version {
125125
case class Update(currentVersion: Version, nextVersion: Version)
126126

127+
def show(versions: Version*): String = {
128+
val vs0 = versions.map(_.value)
129+
val vs1: Seq[String] = if (vs0.size > 6) (vs0.take(3) :+ "...") ++ vs0.takeRight(3) else vs0
130+
vs1.mkString(" -> ")
131+
}
132+
127133
val tagNames: List[Version => String] = List("v" + _, _.value, "release-" + _)
128134

129135
implicit val versionDecoder: Decoder[Version] =
@@ -138,6 +144,9 @@ object Version {
138144
c1.compare(c2)
139145
}
140146

147+
implicit val versionUpdateOrder: Order[Version.Update] =
148+
Order.by((vu: Version.Update) => (vu.currentVersion, vu.nextVersion))
149+
141150
private def padToSameLength[A](l1: List[A], l2: List[A], elem: A): (List[A], List[A]) = {
142151
val maxLength = math.max(l1.length, l2.length)
143152
(l1.padTo(maxLength, elem), l2.padTo(maxLength, elem))

modules/core/src/main/scala/org/scalasteward/core/edit/update/Selector.scala

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,21 +204,19 @@ object Selector {
204204
update: Update.Single,
205205
modulePositions: List[ModulePosition]
206206
): List[Substring.Replacement] =
207-
update.forArtifactIds.toList.flatMap { forArtifactId =>
208-
val newerGroupId = forArtifactId.newerGroupId
209-
val newerArtifactId = forArtifactId.newerArtifactId
207+
update.artifactsForUpdate.toList.flatMap { artifactForUpdate =>
208+
val newerGroupId = artifactForUpdate.newerGroupId
209+
val newerArtifactId = artifactForUpdate.newerArtifactId
210210
if (newerGroupId.isEmpty && newerArtifactId.isEmpty) List.empty
211211
else {
212-
val currentGroupId = forArtifactId.groupId
213-
val currentArtifactId = forArtifactId.artifactIds.head
214212
modulePositions
215213
.filter { p =>
216-
p.groupId.value === currentGroupId.value &&
217-
currentArtifactId.names.contains_(p.artifactId.value)
214+
p.groupId.value === artifactForUpdate.groupId.value &&
215+
artifactForUpdate.artifactId.names.contains_(p.artifactId.value)
218216
}
219217
.flatMap { p =>
220-
newerGroupId.map(g => p.groupId.replaceWith(g.value)).toList ++
221-
newerArtifactId.map(a => p.artifactId.replaceWith(a)).toList
218+
newerGroupId.map(g => p.groupId.replaceWith(g.value)) ++
219+
newerArtifactId.map(a => p.artifactId.replaceWith(a))
222220
}
223221
}
224222
}

modules/core/src/main/scala/org/scalasteward/core/forge/data/NewPullRequestData.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ object NewPullRequestData {
363363

364364
val artifactMigrationsLabel = Option.when {
365365
update.asSingleUpdates
366-
.flatMap(_.forArtifactIds.toList)
366+
.flatMap(_.artifactsForUpdate.toList)
367367
.exists(u => u.newerGroupId.nonEmpty || u.newerArtifactId.nonEmpty)
368368
}("artifact-migrations")
369369
val scalafixLabel = edits.collectFirst { case _: ScalafixEdit => "scalafix-migrations" }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class PullRequestRepository[F[_]](kvStore: KeyValueStore[F, Repo, Map[Uri,
7272
_.collect {
7373
case (url, Entry(baseSha1, u: Update.Single, state, _, number, updateBranch))
7474
if state === PullRequestState.Open &&
75-
u.withNewerVersions(update.newerVersions) === update &&
75+
u.withNextVersion(update.nextVersion) === update &&
7676
u.nextVersion < update.nextVersion =>
7777
for {
7878
number <- number

modules/core/src/main/scala/org/scalasteward/core/repoconfig/UpdatePattern.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package org.scalasteward.core.repoconfig
1919
import cats.syntax.all.*
2020
import io.circe.Codec
2121
import io.circe.generic.semiauto.*
22-
import org.scalasteward.core.data.{GroupId, Update, Version}
22+
import org.scalasteward.core.data.{ArtifactUpdateVersions, GroupId, Version}
2323

2424
final case class UpdatePattern(
2525
groupId: GroupId,
@@ -37,12 +37,14 @@ object UpdatePattern {
3737

3838
def findMatch(
3939
patterns: List[UpdatePattern],
40-
update: Update.ForArtifactId,
40+
update: ArtifactUpdateVersions,
4141
include: Boolean
4242
): MatchResult = {
43-
val byGroupId = patterns.filter(_.groupId === update.groupId)
44-
val byArtifactId = byGroupId.filter(_.artifactId.forall(_ === update.artifactId.name))
45-
val filteredVersions = update.newerVersions.filter(newVersion =>
43+
val artifactForUpdate = update.artifactForUpdate
44+
val byGroupId = patterns.filter(_.groupId === artifactForUpdate.groupId)
45+
val byArtifactId =
46+
byGroupId.filter(_.artifactId.forall(_ === artifactForUpdate.artifactId.name))
47+
val filteredVersions = update.refersToUpdateVersions.filter(newVersion =>
4648
byArtifactId.exists(_.version.forall(_.matches(newVersion.value))) === include
4749
)
4850
MatchResult(byArtifactId, filteredVersions)

0 commit comments

Comments
 (0)