-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathreport
More file actions
executable file
·232 lines (208 loc) · 9.14 KB
/
Copy pathreport
File metadata and controls
executable file
·232 lines (208 loc) · 9.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/local/bin/env -S scala -Dlog4j.configurationFile=scripts/log4j.properties shebang --quiet
// scripts must work on JDK 8 so we can't go past Scala 3.7
//> using scala 3.7.4
//> using option -source:future
object ClocReport:
// sometimes there's an extra "[info]" in the line, so we have to be careful
val Regex = """\[([^\]]+)\] (?:\[info\] )?\*\* COMMUNITY BUILD LINE COUNT: (\d+)""".r
def apply(log: io.Source): Unit =
val results = collection.mutable.Map.empty[String, Int].withDefaultValue(0)
for case Regex(project, count) <- log.getLines do
results(project) += count.toInt
println("Lines of Scala code recompiled during this run only:")
for (project, sum) <- results.toSeq.sortBy(_._2).reverse do
println(f"$sum%9d $project")
println(f"${results.values.sum}%9d TOTAL")
object SuccessReport:
// sample inputs:
// [info] Project foo-bar-baz---------------: DID NOT RUN (stuck on broken dependencies: frob, akka-grue, zorch)
// [info] Project utest---------------------: FAILED (MatchError: d48a6cde+20180920-1730 (of class java.lang.St...)
// regex features used:
// \w = word character
// ?: = not a capturing group
// (?!-) = negative lookahead -- next character is not "-"
val Regex =
"""\[info\] Project ((?:\w|-(?!-))+)-*: ([^\(]+) \((?:stuck on broken dependencies: )?(.*)\)""".r
val requiresJdk11Plus = Set[String](
"scala-logging", // JDK 8 support dropped (February 2026)
)
val requiresJdk17Plus = Set[String](
"airframe",
"americium", // some test code uses `String#translateEscapes`
"better-tostring", // JDK 8 and 11 support dropped (February 2026)
"cachecontrol", // JDK 11 support dropped (July 2024)
"difflicious", // JDK 8 support dropped (July 2026)
"doobie", // JDK 8 support dropped (January 2024)
"fastscala",
"fs2",
"json4s", // Jackson dependency requires 17+ (October 2025)
"jsoniter-scala", // JDK 8 support dropped (February 2022)
"mdoc", // JDK 8 support dropped (October 2023)
"metals", // JDK 11 support dropped (August 2024)
"metrics-scala", // JDK 8 support dropped (August 2023)
"parboiled", // JDK 8 support dropped (March 2022)
"play-file-watch", // JDK 8 support dropped (October 2022)
"play-ws", // JDK 11 support dropped (July 2024)
"playframework", // JDK 8 support dropped (September 2022)
"pureconfig", // errors on JDK 8 in zio/internal/stacktracer (June 2025)
"refined", // test failure on JDK 8; their own CI uses 17
"requests-scala", // JDK 8 support dropped (June 2024)
"scaffeine", // they seem to be on a caffeine version that requires JDK 11
"scala-commons",
"scalachess",
"scalafmt", // uses mdoc, which dropped JDK 8
"scastie",
"shapeless-java-records", // inherently requires JDK 15+
"slick", // JDK 8 support dropped (June 2024)
"sttp", // uses java.net.http
"twirl", // JDK 11 support dropped (July 2024)
"unfiltered", // they seem to be on a jetty version that requires JDK 11+
"utest", // JDK 8 support dropped (when fork updated August 2025)
)
val requiresJdk21Plus = Set[String](
"cats-effect-direct", // uses jdk.internal.vm.Continuation
)
val jdk17Failures = Set[String](
"akka", // needs newer sbt-osgi; not sure about status w/r/t JEP 403
"finagle", // Unrecognized VM option 'AggressiveOpts'
"mockito-scala", // reflection-related test failures
"scala-debugger", // "object FieldInfo is not a member of package sun.reflect"
"scala-refactoring", // needs scala/bug#11125 workaround?
"scrooge", // Unrecognized VM option 'AggressiveOpts'
"ssl-config", // compilation errors involving sun.security.x509
"twitter-util", // Unrecognized VM option 'AggressiveOpts'
)
val jdk21Failures = Set[String](
"kafka", // cannot override final member Thread#threadId
)
val jdk25Failures = Set[String](
"cats-effect", // test failure; https://github.com/typelevel/cats-effect/issues/4471
"playframework", // not supported by the current version of Byte Buddy
"twirl", // Unsupported class file major version
"zinc", // Unsupported class file major version
)
val requiresJdk25Plus = Set[String](
"scalafx", // uses -release:23
)
val jdk26Failures = Set[String](
"ammonite", // value stop is not a member of Thread
"metals", // value stop is not a member of Thread
"scalatest", // value stop is not a member of Thread
"scala-swing", // object JApplet is not a member of package javax.swing
)
val expectedToFail: Set[String] =
sys.env.get("jvmVersion") match
case Some("8") =>
requiresJdk11Plus ++ requiresJdk17Plus ++ requiresJdk21Plus ++ requiresJdk25Plus
case Some("11") =>
requiresJdk17Plus ++ requiresJdk21Plus ++ requiresJdk25Plus
case Some("17") =>
jdk17Failures ++ requiresJdk21Plus ++ requiresJdk25Plus
case Some("21") =>
jdk17Failures ++ jdk21Failures ++ requiresJdk25Plus
case Some("25") =>
jdk17Failures ++ jdk21Failures ++ jdk25Failures
case Some(_) =>
jdk17Failures ++ jdk21Failures ++ jdk25Failures ++ jdk26Failures
case None => // we're not running on Jenkins, apparently
Set()
def apply(log: io.Source): Option[Int] =
val lines = log.getLines.dropWhile(!_.contains("---== Execution Report ==---"))
var success, failed, didNotRun = 0
val unexpectedSuccesses = collection.mutable.Buffer[String]()
val unexpectedFailures = collection.mutable.Buffer[String]()
val blockerCounts = collection.mutable.Map[String, Int]()
for case Regex(name, status, blockers) <- lines do
status match
case "EXTRACTION FAILED" =>
success = -1000
case "SUCCESS" =>
success += 1
if expectedToFail(name) then
unexpectedSuccesses += name
case "FAILED" =>
failed += 1
if !expectedToFail(name) then
unexpectedFailures += name
case "DID NOT RUN" =>
didNotRun += 1
for blocker <- blockers.split(',').map(_.trim) do
blockerCounts(blocker) = 1 + blockerCounts.getOrElse(blocker, 0)
if success < 0 then return None
val total = success + failed + didNotRun
println(s"SUCCEEDED: $success")
val sortedFailures =
unexpectedFailures.sortBy(blockerCounts.withDefaultValue(0)).reverse
if unexpectedFailures.nonEmpty then
val uf = sortedFailures.mkString(",")
println(s"FAILURES (UNEXPECTED): $uf")
if didNotRun > 0 then
val blockers =
blockerCounts.toList.sortBy(_._2).reverse
.collect: (blocker, count) =>
s"$blocker ($count)"
.mkString(", ")
println(s"BLOCKING DOWNSTREAM: $blockers")
if unexpectedSuccesses.nonEmpty then
val us = unexpectedSuccesses.mkString(",")
println(s"SUCCESSES (UNEXPECTED): $us")
println(s"FAILED: $failed")
println(s"BLOCKED, DID NOT RUN: $didNotRun")
println(s"TOTAL: $total")
for url <- Option(System.getenv("BUILD_URL")) do
if unexpectedFailures.nonEmpty then
println()
for failed <- sortedFailures do
println(s"""<a href="${url}artifact/logs/$failed-build.log">$failed</a>""")
if success == 0 then
Some(1)
else
Some(unexpectedFailures.size)
object SplitLog:
val BeginDependencies = """\[info\] ---== Dependency Information ===---""".r
val EndDependencies = """\[info\] ---== ()End Dependency Information ===---""".r
val BeginExtract = """\[([^\]]+)\] --== Extracting dependencies for .+ ==--""".r
val EndExtract = """\[([^\]]+)\] --== End Extracting dependencies for .+ ==--""".r
val BeginBuild = """\[([^\]]+)\] --== Building .+ ==--""".r
val EndBuild = """\[([^\]]+)\] --== End Building .+ ==--""".r
def apply(log: io.Source): Unit =
val dir = java.io.File("logs")
dir.mkdirs()
val lines = log.getLines
while lines.hasNext do
lines.next match
case BeginDependencies() =>
slurp(lines, makeWriter("logs/_dependencies.log"), EndDependencies)
case BeginExtract(name) =>
slurp(lines, makeWriter(s"logs/$name-extract.log"), EndExtract)
case BeginBuild(name) =>
slurp(lines, makeWriter(s"logs/$name-build.log"), EndBuild)
case _ =>
import java.io.PrintWriter
def makeWriter(path: String): PrintWriter =
import java.io.*
val file = File(path)
val foStream = FileOutputStream(file, false) // false = overwrite, don't append
val osWriter = OutputStreamWriter(foStream)
PrintWriter(osWriter)
import scala.util.matching.Regex, annotation.tailrec
def slurp(lines: Iterator[String], writer: java.io.PrintWriter, sentinel: Regex): Unit =
@tailrec def iterate(): Unit =
if lines.hasNext then
lines.next match
case sentinel(_) =>
writer.close()
case line =>
writer.println(line)
iterate()
else
writer.close()
iterate()
// main
def log = io.Source.fromFile(args(0))
println("<pre>")
ClocReport(log)
val unexpectedFailureCount = SuccessReport(log)
println("</pre>")
SplitLog(log)
sys.exit(unexpectedFailureCount.getOrElse(1))