diff --git a/Changelog.md b/Changelog.md index 08a06be75..5b53f3f6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ ## Unreleased +- Maven: Static analysis (`pomxml`, used when `mvn` is unavailable or with `--static-only-analysis`) no longer reports the project itself as the only direct dependency. Dependencies declared in `pom.xml` are now reported as direct and the project's own modules are removed from the graph, matching dynamic analysis. - Diagnostics: When an error or warning group contains multiple errors, each error's `Traceback:` header is now printed on its own line instead of being glued onto the last line of the preceding error message (e.g. `...none passed validationTraceback:`). ([#1758](https://github.com/fossas/fossa-cli/pull/1758)) ## 3.18.2 diff --git a/src/Strategy/Maven.hs b/src/Strategy/Maven.hs index 8f5bef247..4e9dbedb4 100644 --- a/src/Strategy/Maven.hs +++ b/src/Strategy/Maven.hs @@ -3,6 +3,7 @@ module Strategy.Maven ( mkProject, MavenProject (..), getDeps, + getStaticAnalysis, ) where import App.Fossa.Analyze.LicenseAnalyze (LicenseAnalyzeProject, licenseAnalyzeProject) @@ -18,14 +19,14 @@ import Data.Set (Set) import Data.Set qualified as Set import Data.Set.NonEmpty (nonEmpty, toSet) import Data.Text hiding (group, map) -import DepTypes (Dependency) +import DepTypes (Dependency (..)) import Diag.Common (MissingDeepDeps (MissingDeepDeps), MissingEdges (MissingEdges)) import Discovery.Filters (AllFilters, MavenScopeFilters, mavenScopeFilterSet) import Discovery.Simple (simpleDiscover) import Effect.Exec (CandidateCommandEffs, GetDepsEffs) import Effect.ReadFS (ReadFS) import GHC.Generics (Generic) -import Graphing (Graphing, gmap, shrinkRoots) +import Graphing (Graphing, gmap, shrink, shrinkRoots) import Path (Abs, Dir, Path, parent) import Strategy.Maven.Common (MavenDependency (..), filterMavenDependencyByScope, filterMavenSubmodules, mavenDependencyToDependency) import Strategy.Maven.DepTree qualified as DepTreeCmd @@ -188,7 +189,12 @@ getStaticAnalysis submoduleTargets closure = do let allSubmodules = PomClosure.closureSubmodules closure (graph, graphBreadth) <- context "Static analysis" $ pure (Pom.analyze' closure, Partial) filteredGraph <- applyMavenFilters submoduleTargets allSubmodules graph - pure (filteredGraph, graphBreadth) + pure (withoutProjectAsDep filteredGraph, graphBreadth) + where + -- The static graph is rooted at the project itself, with submodules as its children. + -- Those are the users' packages, not dependencies, so remove them and promote their + -- children to direct, like the dynamic strategies do with `shrinkRoots`. + withoutProjectAsDep = shrink (\dep -> dependencyName dep `Set.notMember` PomClosure.closureSubmodules closure) applyMavenFilters :: (Has Diagnostics sig m, Has (Reader MavenScopeFilters) sig m) => Set Text -> Set Text -> Graphing MavenDependency -> m (Graphing Dependency) applyMavenFilters targetSet submoduleSet graph = do diff --git a/test/Maven/PomClosureSpec.hs b/test/Maven/PomClosureSpec.hs index 73fa3fcda..a9d2baeb3 100644 --- a/test/Maven/PomClosureSpec.hs +++ b/test/Maven/PomClosureSpec.hs @@ -2,16 +2,19 @@ module Maven.PomClosureSpec (spec) where +import Control.Carrier.Reader (runReader) import Control.Effect.Lift (sendIO) import Data.ByteString.Char8 qualified as BS import Data.List (find, sort) import Data.Map.Strict qualified as Map import Data.Set qualified as Set import Data.Text (Text) -import GraphUtil (expectDeps') +import DepTypes (DepType (MavenType), Dependency (..), VerConstraint (CEq)) +import GraphUtil (expectDeps', expectDirect') import Graphing (shrinkRoots) import Path (Abs, Dir, Path, reldir, relfile, toFilePath, ()) import Path.IO qualified as PIO +import Strategy.Maven (getStaticAnalysis) import Strategy.Maven.Plugin (Artifact (..), Edge (Edge), PluginOutput (..)) import Strategy.Maven.PluginStrategy (buildGraph) import Strategy.Maven.Pom.Closure ( @@ -22,6 +25,7 @@ import Strategy.Maven.Pom.Closure ( ) import Strategy.Maven.Pom.PomFile (MavenCoordinate (..)) import Test.Effect (EffectStack, itWithTempDir', shouldBe') +import Test.Fixtures (mavenScopeFilterSet) import Test.Hspec spec :: Spec @@ -106,6 +110,32 @@ spec = do graph = shrinkRoots $ buildGraph submodules output expectDeps' [] graph + describe "getStaticAnalysis" $ do + -- The static (pomxml) graph is rooted at the project itself with its + -- modules as children. Those are first-party and must not be reported; + -- their declared dependencies must be promoted to direct, as the dynamic + -- strategies already do via shrinkRoots. + itWithTempDir' "reports declared dependencies as direct and drops the project and its modules" $ \dir -> do + createParentlessFixture dir + closures <- findProjects dir + case find ((== rootCoord) . closureRootCoord) closures of + Nothing -> sendIO $ expectationFailure "expected a closure rooted at com.example:root-a" + Just closure -> do + (graph, _) <- runReader mavenScopeFilterSet $ getStaticAnalysis (closureSubmodules closure) closure + expectDirect' [libDep] graph + expectDeps' [libDep] graph + +libDep :: Dependency +libDep = + Dependency + { dependencyType = MavenType + , dependencyName = "org.example:lib" + , dependencyVersion = Just (CEq "1.0") + , dependencyLocations = [] + , dependencyEnvironments = mempty + , dependencyTags = Map.empty + } + rootCoord :: MavenCoordinate rootCoord = MavenCoordinate "com.example" "root-a" "1.0" @@ -192,6 +222,13 @@ parentedChildPom = , " ../pom.xml\n" , " \n" , " child-parented\n" + , " \n" + , " \n" + , " org.example\n" + , " lib\n" + , " 1.0\n" + , " \n" + , " \n" , "\n" ]