diff --git a/Changelog.md b/Changelog.md index 279746783..a751c1cdc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ ## 3.18.3 +- 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)) 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.