mirror of https://github.com/sbt/sbt.git
[2.x] fix: Avoid following symlinks during scripted test cleanup (#8985)
**Problem** When a scripted test creates a symbolic link within the test directory pointing to a file or directory outside of it, the batch cleanup code follows the symlink and deletes the external target. This is because `view.list(base / **, !keep)` recursively traverses symlinked directories, and the resulting paths resolve to the external location. **Solution** Replace the flat recursive glob listing with manual recursion that checks `FileAttributes.isSymbolicLink` before descending into directories, matching the pattern already used in `Clean.scala`. Symlinks are now deleted as leaf nodes without traversing their targets. Also hoists the cleanup helpers out of the per-test loop since they only depend on the (constant) temp directory. Fixes sbt/sbt#7331
This commit is contained in:
parent
f81a1524bb
commit
f624998c91
|
|
@ -225,6 +225,27 @@ final class ScriptedTests(
|
|||
val seqHandlers = handlers.values.toList
|
||||
runner.initStates(states, seqHandlers)
|
||||
|
||||
// Delete test contents between runs, but preserve global dirs.
|
||||
// Uses manual recursion to avoid following symlinks (sbt/sbt#7331).
|
||||
val view = sbt.nio.file.FileTreeView.default
|
||||
val base = tempTestDir.getCanonicalFile.toGlob
|
||||
val global = base / "global"
|
||||
val globalLogging = base / ** / "global-logging"
|
||||
def recursiveFilter(glob: Glob): PathFilter = (glob: PathFilter) || glob / **
|
||||
val keep: PathFilter = recursiveFilter(global) || recursiveFilter(globalLogging)
|
||||
def deleteContents(dir: java.nio.file.Path): Unit =
|
||||
view
|
||||
.list(Glob(dir, AnyPath), !keep)
|
||||
.foreach:
|
||||
case (p, attrs) if attrs.isDirectory && !attrs.isSymbolicLink =>
|
||||
deleteContents(p)
|
||||
try Files.deleteIfExists(p)
|
||||
catch { case _: IOException => }
|
||||
case (p, _) =>
|
||||
try Files.deleteIfExists(p)
|
||||
catch { case _: IOException => }
|
||||
val tempTestPath = tempTestDir.getCanonicalFile.toPath
|
||||
|
||||
def runBatchTests = {
|
||||
groupedTests.map { case ((group, name), originalDir) =>
|
||||
val label = s"$group/$name"
|
||||
|
|
@ -261,19 +282,7 @@ final class ScriptedTests(
|
|||
|
||||
// Run the test and delete files (except global that holds local scala jars)
|
||||
val result = runOrHandleDisabled(label, tempTestDir, runTest, buffer)
|
||||
if (!keepTempDirectory) {
|
||||
val view = sbt.nio.file.FileTreeView.default
|
||||
val base = tempTestDir.getCanonicalFile.toGlob
|
||||
val global = base / "global"
|
||||
val globalLogging = base / ** / "global-logging"
|
||||
def recursiveFilter(glob: Glob): PathFilter = (glob: PathFilter) || glob / **
|
||||
val keep: PathFilter = recursiveFilter(global) || recursiveFilter(globalLogging)
|
||||
val toDelete = view.list(base / **, !keep).map(_._1).sorted.reverse
|
||||
toDelete.foreach { p =>
|
||||
try Files.deleteIfExists(p)
|
||||
catch { case _: IOException => }
|
||||
}
|
||||
}
|
||||
if (!keepTempDirectory) deleteContents(tempTestPath)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue