mirror of https://github.com/sbt/sbt.git
[2.x] fix: Widen IOException catch in organicTask to cover result serialization (#9050)
**Problem** When a file referenced during cache serialization is deleted between task execution and cache storage, sbt crashes with an uncaught NoSuchFileException. The IOException catch added in #8699 only wraps store.put(), but Converter.toJsonUnsafe(result) and mkInput() also call hashedVirtualFileRefToStr which calls Files.readAttributes — and these are outside the try-catch. **Solution** Move the try/catch IOException to wrap the entire post-action cache storage section, so NoSuchFileException from result serialization or cache key computation is caught and handled gracefully (skip caching, return result). Fixes #9044 Co-authored-by: bittoby <218712309+bittoby@users.noreply.github.co>
This commit is contained in:
parent
d765ba263a
commit
e0bdb531f7
|
|
@ -82,40 +82,40 @@ object ActionCache:
|
|||
case e: Exception =>
|
||||
cacheEventLog.append(ActionCacheEvent.Error)
|
||||
throw e
|
||||
val json = Converter.toJsonUnsafe(result)
|
||||
val normalizedOutputDir = outputDirectory.toAbsolutePath.normalize()
|
||||
val uncacheableOutputs =
|
||||
outputs.filter(f =>
|
||||
f match
|
||||
case vf if vf.id.endsWith(ActionCache.dirZipExt) =>
|
||||
false
|
||||
case _ =>
|
||||
val outputPath = fileConverter.toPath(f).toAbsolutePath.normalize()
|
||||
!outputPath.startsWith(normalizedOutputDir)
|
||||
)
|
||||
if uncacheableOutputs.nonEmpty then
|
||||
cacheEventLog.append(ActionCacheEvent.Error)
|
||||
logger.error(
|
||||
s"Cannot cache task because its output files are outside the output directory: \n" +
|
||||
uncacheableOutputs.mkString(" - ", "\n - ", "")
|
||||
)
|
||||
result
|
||||
else
|
||||
cacheEventLog.append(ActionCacheEvent.OnsiteTask)
|
||||
val (input, valuePath) = mkInput(key, codeContentHash, extraHash, config.cacheVersion)
|
||||
val valueFile = StringVirtualFile1(valuePath, CompactPrinter(json))
|
||||
val newOutputs = Vector(valueFile) ++ outputs.toVector
|
||||
try
|
||||
try
|
||||
val json = Converter.toJsonUnsafe(result)
|
||||
val normalizedOutputDir = outputDirectory.toAbsolutePath.normalize()
|
||||
val uncacheableOutputs =
|
||||
outputs.filter(f =>
|
||||
f match
|
||||
case vf if vf.id.endsWith(ActionCache.dirZipExt) =>
|
||||
false
|
||||
case _ =>
|
||||
val outputPath = fileConverter.toPath(f).toAbsolutePath.normalize()
|
||||
!outputPath.startsWith(normalizedOutputDir)
|
||||
)
|
||||
if uncacheableOutputs.nonEmpty then
|
||||
cacheEventLog.append(ActionCacheEvent.Error)
|
||||
logger.error(
|
||||
s"Cannot cache task because its output files are outside the output directory: \n" +
|
||||
uncacheableOutputs.mkString(" - ", "\n - ", "")
|
||||
)
|
||||
result
|
||||
else
|
||||
cacheEventLog.append(ActionCacheEvent.OnsiteTask)
|
||||
val (input, valuePath) = mkInput(key, codeContentHash, extraHash, config.cacheVersion)
|
||||
val valueFile = StringVirtualFile1(valuePath, CompactPrinter(json))
|
||||
val newOutputs = Vector(valueFile) ++ outputs.toVector
|
||||
store.put(UpdateActionResultRequest(input, newOutputs, exitCode = 0)) match
|
||||
case Right(cachedResult) =>
|
||||
store.syncBlobs(cachedResult.outputFiles, outputDirectory)
|
||||
result
|
||||
case Left(e) => throw e
|
||||
catch
|
||||
case e: IOException =>
|
||||
logger.debug(s"Skipping cache storage due to error: ${e.getMessage}")
|
||||
cacheEventLog.append(ActionCacheEvent.Error)
|
||||
result
|
||||
catch
|
||||
case e: IOException =>
|
||||
logger.debug(s"Skipping cache storage due to error: ${e.getMessage}")
|
||||
cacheEventLog.append(ActionCacheEvent.Error)
|
||||
result
|
||||
|
||||
// Single cache lookup - use exitCode to distinguish success from failure
|
||||
getWithFailure(key, codeContentHash, extraHash, tags, config) match
|
||||
|
|
|
|||
Loading…
Reference in New Issue