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"
]