[2.x] fix: Fix snapshot sbt version invalidation 7713 (#8486)

When using a SNAPSHOT version of sbt, if the sbt jars are republished
with breaking binary changes, the build definition should be recompiled.
Previously, the cache key only considered the source content, not the
classpath, causing NoSuchMethodError when cached classes referenced
methods that no longer exist.

This fix includes a hash of SNAPSHOT and -bin- jars (including their
modification times) in the cache key. When these jars change, the build
definition is recompiled.

Fixes #7713
This commit is contained in:
MkDev11 2026-01-11 21:01:05 -05:00 committed by GitHub
parent 4606c90f69
commit 21b5c3b8df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 20 additions and 0 deletions

View File

@ -45,6 +45,24 @@ class Eval(
.map(_.toString)
.mkString(java.io.File.pathSeparator)
// Compute a hash of SNAPSHOT jars to invalidate cache when sbt SNAPSHOT version changes.
// Include modification time to detect republished snapshots with the same version string.
// This fixes #7713: build.sbt not recompiled when SNAPSHOT sbt breaks binary compatibility.
private val snapshotClasspathHash: String =
val snapshotJars = classpath.filter { path =>
val name = path.getFileName.toString
name.contains("SNAPSHOT") || name.contains("-bin-")
}
if snapshotJars.isEmpty then ""
else
val digester = MessageDigest.getInstance("SHA")
snapshotJars.sorted.foreach { path =>
val file = path.toFile
digester.update(bytes(path.toString))
if file.exists then digester.update(bytes(file.lastModified.toString))
}
Hash.toHex(digester.digest())
final class EvalDriver(reporter: EvalReporter) extends Driver:
val compileCtx0 = initCtx.fresh
val options = nonCpOptions ++ Seq("-classpath", classpathString, "dummy.scala")
@ -196,6 +214,8 @@ class Eval(
digester.update(bytes(tpe))
}
digester.update(bytes(ev.extraHash))
// Include SNAPSHOT classpath hash to invalidate cache when sbt version changes (fixes #7713)
digester.update(bytes(snapshotClasspathHash))
val d = digester.digest()
val hash = Hash.toHex(d)
val moduleName = makeModuleName(hash)