From 9795297a15c7b1d66e2cfa3451e92ebeff4682e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 5 Sep 2026 01:57:42 +0000 Subject: [PATCH 1/2] [ANE-Bot] Fix fatal crash when Maven plugin scratch dir is removed before cleanup Maven analysis unpacks its depgraph plugin jar into a scratch directory created under the system temp dir. Cleanup used a hand-rolled bracket/removeDirRecur that escalated any IO error to a fatal, analysis-wide error. When the OS temp reaper (or a race) removed the directory before cleanup ran, the whole run died with: An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist (No such file or directory) Use withSystemTempDir instead, which removes the scratch directory on a best-effort basis (ignoring IO errors during cleanup), matching the temp dir handling used elsewhere in the analyzer. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019qjHR5TZSM1At5k3XBw7J8 --- Changelog.md | 1 + src/Strategy/Maven/Plugin.hs | 15 +++++++++------ test/Maven/PluginSpec.hs | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3efc8532f..744899277 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ ## Unreleased +- Maven: analysis no longer fails with `An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist (No such file or directory)` when the plugin's scratch directory is removed (e.g. by the OS temp reaper) before cleanup runs; the scratch directory is now cleaned up on a best-effort basis. - Dart: `pubspec.yaml` files using valid dependency forms the parser previously rejected no longer fail analysis with `Aeson exception: ... empty` or `failed parsing pub package's source!`: a bare dependency with no value (any version), a `version:`-only entry, the `hosted: ` shorthand introduced in Dart 2.15, a `hosted:` map without a `version`, and a `git:` map without a `ref`. ([#1760](https://github.com/fossas/fossa-cli/pull/1760)) - Workflows: `fossa analyze --x-workflow ` runs a dependency-usage workflow analyzer through the embedded ficus and records its result in the debug bundle. ([#1761](https://github.com/fossas/fossa-cli/pull/1761)) - Workflows: the `--x-workflow` result is uploaded to FOSSA against the analyzed revision once the dependency upload succeeds; `--output` runs still upload nothing. ([#1762](https://github.com/fossas/fossa-cli/pull/1762)) diff --git a/src/Strategy/Maven/Plugin.hs b/src/Strategy/Maven/Plugin.hs index f348e1813..31e8d02d0 100644 --- a/src/Strategy/Maven/Plugin.hs +++ b/src/Strategy/Maven/Plugin.hs @@ -25,8 +25,9 @@ module Strategy.Maven.Plugin ( import Control.Algebra (Has) import Control.Effect.Diagnostics (Diagnostics, ToDiagnostic (renderDiagnostic), recover, warn) -import Control.Effect.Exception (Lift, bracket) +import Control.Effect.Exception (Lift) import Control.Effect.Lift (sendIO) +import Control.Effect.Path (withSystemTempDir) import Control.Monad (when) import Data.Aeson (FromJSON, parseJSON, withObject, (.!=), (.:), (.:?)) import Data.ByteString (ByteString) @@ -79,7 +80,6 @@ import Path ( toFilePath, (), ) -import Path.IO (createTempDir, getTempDir, removeDirRecur) import Strategy.Maven.PluginTree (TextArtifact (..), parseTextArtifacts) import Strategy.Maven.Pom.PomFile qualified as PomFile import System.FilePath qualified as FP @@ -117,10 +117,13 @@ withUnpackedPlugin :: (FP.FilePath -> m a) -> m a withUnpackedPlugin plugin act = - bracket - (sendIO (getTempDir >>= \tmp -> createTempDir tmp "fossa-maven")) - (sendIO . removeDirRecur) - go + -- 'withSystemTempDir' removes the scratch directory on a best-effort basis + -- (it ignores IO errors during cleanup). A previous hand-rolled + -- 'bracket'/'removeDirRecur' cleanup would instead escalate any cleanup + -- failure to a fatal, analysis-wide error -- e.g. when the OS temp reaper (or + -- a race) removed the directory first, the run died with + -- @An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist@. + withSystemTempDir "fossa-maven" go where go tmpDir = do let pluginJarFilepath = fromAbsDir tmpDir FP. "plugin.jar" diff --git a/test/Maven/PluginSpec.hs b/test/Maven/PluginSpec.hs index ad15e2d84..5926eba83 100644 --- a/test/Maven/PluginSpec.hs +++ b/test/Maven/PluginSpec.hs @@ -18,13 +18,17 @@ import Strategy.Maven.Plugin ( VerboseEdge (..), VerboseGraph (..), augmentWithDuplicateEdges, + depGraphPlugin, deriveVerboseGraphPaths, parsePluginOutput, parseVerboseGraphs, textArtifactToPluginOutput, + withUnpackedPlugin, ) import Strategy.Maven.PluginTree (TextArtifact (..), parseTextArtifact) import Strategy.Maven.Pom.PomFile (MavenCoordinate (..), Pom (..), PomBuild (..)) +import System.Directory qualified as Dir +import System.FilePath qualified as FP import Test.Effect ( expectFatal', expectationFailure', @@ -47,6 +51,22 @@ spec = do verboseGraphCollectionSpec deriveVerboseGraphPathsSpec parsePluginOutputSpec + withUnpackedPluginSpec + +-- | 'withUnpackedPlugin' unpacks the plugin jar into a scratch directory and +-- must clean it up on a best-effort basis. If the directory is already gone +-- when cleanup runs (e.g. an OS temp reaper removed it, or a concurrent task +-- did), cleanup must not turn that into a fatal, analysis-wide error such as +-- @An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist@. +withUnpackedPluginSpec :: Spec +withUnpackedPluginSpec = + describe "withUnpackedPlugin" $ + it' "does not fail when the scratch directory is already gone at cleanup time" $ do + result <- withUnpackedPlugin depGraphPlugin $ \pluginJarFilepath -> do + -- Simulate the scratch directory vanishing before cleanup runs. + sendIO $ Dir.removeDirectoryRecursive (FP.takeDirectory pluginJarFilepath) + pure (42 :: Int) + result `shouldBe'` (42 :: Int) -- | The @graph@ goal is not an aggregator: in a multi-module build it runs once -- per reactor module, writing into each module's own build directory. From 3f609caaea28a859073462ab36c773d824441ae1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 5 Sep 2026 02:11:38 +0000 Subject: [PATCH 2/2] Changelog: link Maven scratch-dir cleanup fix to its PR Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019qjHR5TZSM1At5k3XBw7J8 --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 744899277..3c5904f55 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ ## Unreleased -- Maven: analysis no longer fails with `An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist (No such file or directory)` when the plugin's scratch directory is removed (e.g. by the OS temp reaper) before cleanup runs; the scratch directory is now cleaned up on a best-effort basis. +- Maven: analysis no longer fails with `An exception occurred: /tmp/fossa-maven-... removeDirectoryRecursive:getSymbolicLinkStatus: does not exist (No such file or directory)` when the plugin's scratch directory is removed (e.g. by the OS temp reaper) before cleanup runs; the scratch directory is now cleaned up on a best-effort basis. ([#1771](https://github.com/fossas/fossa-cli/pull/1771)) - Dart: `pubspec.yaml` files using valid dependency forms the parser previously rejected no longer fail analysis with `Aeson exception: ... empty` or `failed parsing pub package's source!`: a bare dependency with no value (any version), a `version:`-only entry, the `hosted: ` shorthand introduced in Dart 2.15, a `hosted:` map without a `version`, and a `git:` map without a `ref`. ([#1760](https://github.com/fossas/fossa-cli/pull/1760)) - Workflows: `fossa analyze --x-workflow ` runs a dependency-usage workflow analyzer through the embedded ficus and records its result in the debug bundle. ([#1761](https://github.com/fossas/fossa-cli/pull/1761)) - Workflows: the `--x-workflow` result is uploaded to FOSSA against the analyzed revision once the dependency upload succeeds; `--output` runs still upload nothing. ([#1762](https://github.com/fossas/fossa-cli/pull/1762))