diff --git a/Changelog.md b/Changelog.md index 5fcc859a7b..59ee5de2ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ ## Unreleased - Maven: add support for maven scope filtering ([#1331](https://github.com/fossas/fossa-cli/pull/1331)) - `fossa init`: adds new `fossa init` command which creates `.fossa.yml.example`, and `fossa-deps.yml.example` file. ([#1323](https://github.com/fossas/fossa-cli/pull/1323)) +- Maven: add support for maven submodule filtering ## v3.8.24 diff --git a/docs/references/strategies/languages/gradle/gradle.md b/docs/references/strategies/languages/gradle/gradle.md index 35b6873624..ffea291bd4 100644 --- a/docs/references/strategies/languages/gradle/gradle.md +++ b/docs/references/strategies/languages/gradle/gradle.md @@ -338,7 +338,7 @@ targets: ``` -1) Running `fossa analyze --output -c .fossa.yml`, will only analyze `utilities` submodule. +1) Running `fossa analyze --output -c .fossa.yml`, will only analyze `list` submodule. ## (Experimental) Only Selecting Set of Configurations For Analysis diff --git a/docs/references/strategies/languages/maven/maven.md b/docs/references/strategies/languages/maven/maven.md index d3d574995b..cfb90c34e0 100644 --- a/docs/references/strategies/languages/maven/maven.md +++ b/docs/references/strategies/languages/maven/maven.md @@ -51,6 +51,67 @@ maven: - test ``` +## Filtering by submodules + +If you have maven project which has one or more subprojects, you may +only want to analyze a specific set of subprojects in some cases. + + +In `fossa-cli`, this can be achieved by using [exclusion filtering](./../../../files/fossa-yml.md). + +1) Run `fossa list-targets`, to identify project directory and identifier of subprojects. +```bash +[ INFO] Found project: maven@./ +[ INFO] Found target: maven@./:com.fossa:app +[ INFO] Found target: maven@./:com.fossa:list +[ INFO] Found target: maven@./:com.fossa:utilities +``` + +Note that, targets are denoted in following format `type@path:target`. For example `maven@./:com.fossa:utilities`: + +Note: maven submodules targets are composed of `:`, so the utilities submodule here is referenced by "com.fossa:utilities" +``` +maven @ ./ : com.fossa:utilities +------ --- --- --- ----------- +Type Path Path Target Target + separator separator +``` + +2) Now to analyze only `utilities`, use a `.fossa.yml` file in the project root. + +```yaml +# filename: .fossa.yml +# +# analyze only maven@./:com.fossa:utilities +version: 3 +targets: + only: + - type: maven + path: ./ + target: 'com.fossa:utilities' +``` + +Likewise, if you want to exclude specific set of subprojects, you can do following: + +```yaml +# filename: .fossa.yml +# +# do not analyze maven@./:com.fossa:app, and maven@./:com.fossa:utilities +version: 3 +targets: + only: + - type: maven + exclude: + - type: maven + path: ./ + target: 'com.fossa:app' + - type: gradle + path: ./ + target: 'com.fossa:utilities' +``` + +1) Running `fossa analyze --output -c .fossa.yml`, will only analyze `utilities` submodule. + 4 - - let graph :: Graphing Int - graph = Graphing.edges [(1, 2), (3, 4), (2, 4), (2, 5), (5, 7), (5, 6)] <> Graphing.directs [1, 3] - - graph' :: Graphing Int -> Graphing Int - graph' g = Graphing.deleteNodeAndChildren (\x -> x == 4 ) 3 g - - x = graph - x' = graph' x - - traceM("This is the graph---------------" ++ show(x)) - traceM("This is the graph after delete ************" ++ show(x')) - traceM("This is the graph after invoking the delete---------------" ++ show(x)) - expectDirect [1] x' - expectDeps [1, 2, 5, 6, 7] x' - expectEdges [(1, 2), (2, 5), (5, 7), (5, 6)] x' - it "should not promote any deps to direct" $ do -- 1 -> 2 -> 5 -> 6 -- \ \ diff --git a/test/Maven/CommonSpec.hs b/test/Maven/CommonSpec.hs index 84849c6b52..4f9668b687 100644 --- a/test/Maven/CommonSpec.hs +++ b/test/Maven/CommonSpec.hs @@ -10,7 +10,7 @@ import Data.Set qualified as Set import Discovery.Filters (MavenScopeFilters (MavenScopeExcludeFilters, MavenScopeIncludeFilters), setExclude, setInclude) import GraphUtil import Graphing qualified -import Strategy.Maven.Common (MavenDependency (..), filterMavenDependencyByScope) +import Strategy.Maven.Common (MavenDependency (..), filterMavenDependencyByScope, filterMavenSubmodules) import Test.Hspec (Spec, describe, it) createDepFromScopes :: [Text] -> MavenDependency @@ -26,8 +26,21 @@ createDepFromScopes scopes = do } MavenDependency dep (Set.fromList scopes) -spec :: Spec -spec = do +createDepFromName :: Text -> MavenDependency +createDepFromName depName = do + let dep = + Dependency + { dependencyType = MavenType + , dependencyName = depName + , dependencyVersion = Just (CEq "1.0.0") + , dependencyLocations = [] + , dependencyEnvironments = Set.fromList [] + , dependencyTags = Map.fromList [] + } + MavenDependency dep (Set.fromList []) + +scopeFilters :: Spec +scopeFilters = do describe "Filter maven deps by scope" $ do it "should not filter from empty include set" $ do let compileDep = createDepFromScopes ["compile"] @@ -175,3 +188,64 @@ spec = do expectDirect [compileDep] graph' expectDeps [compileDep, runtimeDep] graph' expectEdges [(compileDep, runtimeDep)] graph' + + +submoduleFilters :: Spec +submoduleFilters = do + describe "Filter maven deps by submodule" $ do + it "should filter submodule but not its shared dependencies" $ do + -- com.fossa:submodule2 -> com.fossa:filteredDep + -- \ + -- com.fossa:submodule1 -> com.fossa:sharedDep -> com.fossa:sharedDepTransitive + -- + -- After filtering with includedSubmodules = Set("com.fossa:submodule1") + -- + -- com.fossa:submodule1 -> com.fossa:sharedDep -> com.fossa:sharedDepTransitive + let submoduleDep1 = createDepFromName "com.fossa:submodule1" + let submoduleDep2 = createDepFromName "com.fossa:submodule2" + let sharedDep = createDepFromName "com.fossa:sharedDep" + let sharedDepTransitive = createDepFromName "com.fossa:sharedDepTransitive" + let filteredDep = createDepFromName "com.fossa:filteredDep" + + let includedSubmodules = Set.fromList ["com.fossa:submodule1"] + let allSubmodules = Set.fromList ["com.fossa:submodule1", "com.fossa:submodule2"] + + let graph = Graphing.directs [submoduleDep1, submoduleDep2] <> Graphing.edges [(submoduleDep1, sharedDep), (submoduleDep2, sharedDep), (submoduleDep2, filteredDep), (sharedDep, sharedDepTransitive)] + let graph' = filterMavenSubmodules includedSubmodules allSubmodules graph + + expectDirect [submoduleDep1] graph' + expectDeps [submoduleDep1, sharedDep, sharedDepTransitive] graph' + expectEdges [(submoduleDep1, sharedDep), (sharedDep, sharedDepTransitive)] graph' + + it "should filter submodule when it is a dependency of another submodule and keep the shared dependencies" $ do + -- com.fossa:submodule1 -> com.fossa:sharedDep -> com.fossa:sharedDepTransitive + -- \ / + -- com.fossa:submodule2 -> com.fossa:filteredDep -> com.fossa:filteredDepTransitive + -- + -- After filtering with includedSubmodules = Set("com.fossa:submodule1") + -- + -- com.fossa:submodule1 -> com.fossa:sharedDep -> com.fossa:sharedDepTransitive + let submoduleDep1 = createDepFromName "com.fossa:submodule1" + let submoduleDep2 = createDepFromName "com.fossa:submodule2" + let sharedDep = createDepFromName "com.fossa:sharedDep" + let sharedDepTransitive = createDepFromName "com.fossa:sharedDepTransitive" + let filteredDep = createDepFromName "com.fossa:filteredDep" + let filteredDepTransitive = createDepFromName "com.fossa:filteredDepTransitive" + + + + let includedSubmodules = Set.fromList ["com.fossa:submodule1"] + let allSubmodules = Set.fromList ["com.fossa:submodule1", "com.fossa:submodule2"] + + let graph = Graphing.directs [submoduleDep1, submoduleDep2] <> Graphing.edges [(submoduleDep1, sharedDep), (submoduleDep1, submoduleDep2), (submoduleDep2, sharedDep), (submoduleDep2, filteredDep), (sharedDep, sharedDepTransitive), (filteredDep, filteredDepTransitive)] + let graph' = filterMavenSubmodules includedSubmodules allSubmodules graph + + expectDirect [submoduleDep1] graph' + expectDeps [submoduleDep1, sharedDep, sharedDepTransitive] graph' + expectEdges [(submoduleDep1, sharedDep), (sharedDep, sharedDepTransitive)] graph' + + +spec :: Spec +spec = do + scopeFilters + submoduleFilters \ No newline at end of file diff --git a/test/Maven/DepTreeSpec.hs b/test/Maven/DepTreeSpec.hs index 502d4a2ae0..ec7eb962ed 100644 --- a/test/Maven/DepTreeSpec.hs +++ b/test/Maven/DepTreeSpec.hs @@ -10,6 +10,7 @@ import DepTypes ( VerConstraint (..), ) import GraphUtil +import Graphing (shrinkRoots) import Strategy.Maven.Common (MavenDependency (..)) import Strategy.Maven.DepTree ( DotGraph (..), @@ -86,7 +87,9 @@ spec = let mavenDepHamcrestCore = MavenDependency depHamcrestCore (Set.fromList ["test"]) -- Act - let graph = buildGraph fixtureSingleGraph + -- NOTE: Previously shrinkRoots was applied at this level, but it has now been moved upstream to allow for submodule filtering + -- Adding shrinkRoots to our buildGraph function to mimic prior behavior + let graph = shrinkRoots $ buildGraph fixtureSingleGraph -- Assert expectDeps [mavenDepRngCore, mavenDepMath3, mavenDepJunit, mavenDepRngClientApi, mavenDepHamcrestCore] graph diff --git a/test/Maven/PluginStrategySpec.hs b/test/Maven/PluginStrategySpec.hs index 6d67e063a3..f1396664e3 100644 --- a/test/Maven/PluginStrategySpec.hs +++ b/test/Maven/PluginStrategySpec.hs @@ -29,6 +29,7 @@ import Strategy.Maven.Plugin ( ) import Strategy.Maven.PluginStrategy (buildGraph) +import Graphing (shrinkRoots) import Strategy.Maven.Common (MavenDependency (..)) import Test.Hspec (Spec, describe, it) @@ -275,30 +276,32 @@ mavenCrossDependentSubModules = spec :: Spec spec = do describe "buildGraph" $ do + -- NOTE: Previously shrinkRoots was applied at this level, but it has now been moved upstream to allow for submodule filtering + -- Adding shrinkRoots to our buildGraph function to mimic prior behavior it "should produce expected output" $ do - let graph = buildGraph (ReactorOutput []) mavenOutput + let graph = shrinkRoots $ buildGraph (ReactorOutput []) mavenOutput expectDeps [packageOne, packageTwo] graph expectDirect [] graph expectEdges [(packageOne, packageTwo)] graph it "Should promote children of root dep(s) to direct" $ do - let graph = buildGraph (ReactorOutput []) mavenOutputWithDirects + let graph = shrinkRoots $ buildGraph (ReactorOutput []) mavenOutputWithDirects expectDeps [packageTwo] graph expectDirect [packageTwo] graph expectEdges [] graph it "Should promote children of root dep(s) to direct in multimodule projects" $ do - let graph = buildGraph (ReactorOutput []) mavenMultimoduleOutputWithDirects + let graph = shrinkRoots $ buildGraph (ReactorOutput []) mavenMultimoduleOutputWithDirects expectDeps [packageTwo, packageFour] graph expectDirect [packageTwo, packageFour] graph expectEdges [] graph it "Should parse all scopes" $ do - let graph = buildGraph (ReactorOutput []) mavenMultiScopeOutput + let graph = shrinkRoots $ buildGraph (ReactorOutput []) mavenMultiScopeOutput expectDeps [packageMultiScope] graph - let graph = buildGraph (ReactorOutput [ReactorArtifact "packageThree"]) mavenCrossDependentSubModules + let graph = shrinkRoots $ buildGraph (ReactorOutput [ReactorArtifact "packageThree"]) mavenCrossDependentSubModules it "Should mark top-level graph artifacts and known submodules as direct, then shrinkRoots" $ do expectDirect [packageTwo, packageFour] graph