Skip to content

Commit d15e2bd

Browse files
committed
Improve bisect script - bisect releases from a given range
1 parent 7337b6f commit d15e2bd

File tree

1 file changed

+55
-11
lines changed

1 file changed

+55
-11
lines changed

project/scripts/bisect.scala

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ import java.io.File
1313

1414
val usageMessage = """
1515
|Usage:
16-
| > scala-cli project/scripts/bisect.scala -- <validation-script>
16+
| > scala-cli project/scripts/bisect.scala -- <validation-script-path> [versions-range]
1717
|
1818
|The validation script should be executable and accept a single parameter, which will be the scala version to validate.
1919
|Look at bisect-cli-example.sh and bisect-expect-example.exp for reference.
20+
|The optional versions range specifies which releases should be taken into account while bisecting.
21+
|The range format is <first>...<last>, where both <first> and <last> are optional, e.g.
22+
|* 3.1.0-RC1-bin-20210827-427d313-NIGHTLY..3.2.1-RC1-bin-20220716-bb9c8ff-NIGHTLY
23+
|* 3.2.1-RC1-bin-20220620-de3a82c-NIGHTLY..
24+
|* ..3.3.0-RC1-bin-20221124-e25362d-NIGHTLY
25+
|The ranges are treated as inclusive.
26+
|
2027
|Don't use the example scripts modified in place as they might disappear from the repo during a checkout.
2128
|Instead copy them to a different location first.
2229
|
@@ -26,18 +33,23 @@ val usageMessage = """
2633
""".stripMargin
2734

2835
@main def dottyCompileBisect(args: String*): Unit =
29-
val validationScriptPath = args match
36+
val (validationScriptRawPath, versionsRange) = args match
3037
case Seq(path) =>
31-
(new File(path)).getAbsolutePath.toString
38+
(path, VersionsRange.all)
39+
case Seq(path, ParsedVersionsRange(range)) =>
40+
(path, range)
3241
case _ =>
3342
println("Wrong script parameters.")
3443
println()
3544
println(usageMessage)
3645
System.exit(1)
3746
null
3847

48+
val validationScriptPath = (new File(validationScriptRawPath)).getAbsolutePath.toString
49+
given releases: Releases = Releases.fromRange(versionsRange)
50+
3951
val releaseBisect = ReleaseBisect(validationScriptPath)
40-
val bisectedBadRelease = releaseBisect.bisectedBadRelease(Releases.allReleases)
52+
val bisectedBadRelease = releaseBisect.bisectedBadRelease(releases.releases)
4153
println("\nFinished bisecting releases\n")
4254

4355
bisectedBadRelease match
@@ -53,9 +65,37 @@ val usageMessage = """
5365
case None =>
5466
println(s"No bad release found")
5567

68+
case class VersionsRange(first: Option[String], last: Option[String]):
69+
def filter(versions: Seq[String]) =
70+
def versionIndex(version: String) =
71+
val lastMatchingNightly =
72+
if version.contains("-bin-") then version else
73+
versions.filter(_.startsWith(version)).last
74+
versions.indexOf(lastMatchingNightly)
75+
76+
val startIdx = first.map(versionIndex(_)).getOrElse(0)
77+
assert(startIdx >= 0, s"${first} is not a nightly compiler release")
78+
val endIdx = last.map(versionIndex(_) + 1).getOrElse(versions.length)
79+
assert(endIdx > 0, s"${endIdx} is not a nightly compiler release")
80+
val filtered = versions.slice(startIdx, endIdx).toVector
81+
assert(filtered.nonEmpty, "No matching releases")
82+
filtered
83+
84+
85+
object VersionsRange:
86+
def all = VersionsRange(None, None)
87+
88+
object ParsedVersionsRange:
89+
def unapply(range: String): Option[VersionsRange] = range match
90+
case s"${first}...${last}" => Some(VersionsRange(
91+
Some(first).filter(_.nonEmpty),
92+
Some(last).filter(_.nonEmpty)
93+
))
94+
case _ => None
95+
5696
class ReleaseBisect(validationScriptPath: String):
5797
def bisectedBadRelease(releases: Vector[Release]): Option[Release] =
58-
Some(bisect(releases: Vector[Release]))
98+
Some(bisect(releases))
5999
.filter(!isGoodRelease(_))
60100

61101
def bisect(releases: Vector[Release]): Release =
@@ -75,11 +115,15 @@ class ReleaseBisect(validationScriptPath: String):
75115
println(s"Test result: ${release.version} is a ${if isGood then "good" else "bad"} release\n")
76116
isGood
77117

118+
class Releases(val releases: Vector[Release])
119+
78120
object Releases:
79-
lazy val allReleases: Vector[Release] =
80-
val re = raw"(?<=title=$")(.+-bin-\d{8}-\w{7}-NIGHTLY)(?=/$")".r
121+
private lazy val allReleases: Vector[String] =
122+
val re = raw"""(?<=title=")(.+-bin-\d{8}-\w{7}-NIGHTLY)(?=/")""".r
81123
val html = Source.fromURL("https://repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/")
82-
re.findAllIn(html.mkString).map(Release.apply).toVector
124+
re.findAllIn(html.mkString).toVector
125+
126+
def fromRange(range: VersionsRange): Releases = Releases(range.filter(allReleases).map(Release(_)))
83127

84128
case class Release(version: String):
85129
private val re = raw".+-bin-(\d{8})-(\w{7})-NIGHTLY".r
@@ -92,10 +136,10 @@ object Releases:
92136
case re(_, hash) => hash
93137
case _ => sys.error(s"Could not extract hash from version $version")
94138

95-
def previous: Option[Release] =
96-
val idx = allReleases.indexOf(this)
139+
def previous(using r: Releases): Option[Release] =
140+
val idx = r.releases.indexOf(this)
97141
if idx == 0 then None
98-
else Some(allReleases(idx - 1))
142+
else Some(r.releases(idx - 1))
99143

100144
override def toString: String = version
101145

0 commit comments

Comments
 (0)