-
Notifications
You must be signed in to change notification settings - Fork 79
Detect property to exclude Cargo dependencies #1421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
10883f5
0354a8e
2830904
1ac92d4
5344176
fba1188
f6e48d9
b66b9f7
37c4ed6
9e1e4a4
95d1bac
1293fb9
1a0e93a
0d8f6f2
d2a8a6c
5eb9b16
d2be399
83ebdf1
ae1ee8e
383b99e
204b05f
538bc03
537760c
f1be648
f3130f8
9955084
44a70b2
26ec154
8bdcb52
ff370af
2f9f6a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,7 @@ | |
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.*; | ||
|
||
public class CargoCliExtractor { | ||
private static final List<String> CARGO_TREE_COMMAND = Arrays.asList("tree", "--no-dedupe", "--prefix", "depth"); | ||
|
@@ -32,8 +30,12 @@ public CargoCliExtractor(DetectableExecutableRunner executableRunner, CargoDepen | |
this.cargoTomlParser = cargoTomlParser; | ||
} | ||
|
||
public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoTomlFile) throws ExecutableFailedException, IOException { | ||
ExecutableOutput cargoOutput = executableRunner.executeSuccessfully(ExecutableUtils.createFromTarget(directory, cargoExe, CARGO_TREE_COMMAND)); | ||
public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoTomlFile, CargoDetectableOptions cargoDetectableOptions) throws ExecutableFailedException, IOException { | ||
List<String> cargoTreeCommand = new ArrayList<>(CARGO_TREE_COMMAND); | ||
|
||
addEdgeExclusions(cargoTreeCommand, cargoDetectableOptions); | ||
|
||
ExecutableOutput cargoOutput = executableRunner.executeSuccessfully(ExecutableUtils.createFromTarget(directory, cargoExe, cargoTreeCommand)); | ||
List<String> cargoTreeOutput = cargoOutput.getStandardOutputAsList(); | ||
|
||
DependencyGraph graph = cargoDependencyTransformer.transform(cargoTreeOutput); | ||
|
@@ -51,4 +53,24 @@ public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoT | |
.nameVersionIfPresent(projectNameVersion) | ||
.build(); | ||
} | ||
|
||
private void addEdgeExclusions(List<String> cargoTreeCommand, CargoDetectableOptions options) { | ||
Map<CargoDependencyType, String> exclusionMap = new EnumMap<>(CargoDependencyType.class); | ||
exclusionMap.put(CargoDependencyType.NORMAL, "no-normal"); | ||
exclusionMap.put(CargoDependencyType.BUILD, "no-build"); | ||
exclusionMap.put(CargoDependencyType.DEV, "no-dev"); | ||
exclusionMap.put(CargoDependencyType.PROC_MACRO, "no-proc-macro"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you describe situations in which PROC_MACRO exclusion is useful? And I guess it's not applicable to lock file extractions? |
||
|
||
List<String> exclusions = new ArrayList<>(); | ||
for (Map.Entry<CargoDependencyType, String> entry : exclusionMap.entrySet()) { | ||
if (options.getDependencyTypeFilter().shouldExclude(entry.getKey())) { | ||
exclusions.add(entry.getValue()); | ||
} | ||
} | ||
|
||
if (!exclusions.isEmpty()) { | ||
cargoTreeCommand.add("--edges"); | ||
cargoTreeCommand.add(String.join(",", exclusions)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.blackduck.integration.detectable.detectables.cargo; | ||
|
||
public enum CargoDependencyType { | ||
NORMAL, | ||
BUILD, | ||
DEV, | ||
PROC_MACRO | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.blackduck.integration.detectable.detectables.cargo; | ||
|
||
import com.blackduck.integration.detectable.detectable.util.EnumListFilter; | ||
|
||
public class CargoDetectableOptions { | ||
private final EnumListFilter<CargoDependencyType> dependencyTypeFilter; | ||
|
||
public CargoDetectableOptions(EnumListFilter<CargoDependencyType> dependencyTypeFilter) { | ||
this.dependencyTypeFilter = dependencyTypeFilter; | ||
} | ||
|
||
public EnumListFilter<CargoDependencyType> getDependencyTypeFilter() { | ||
return dependencyTypeFilter; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,10 @@ | |
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.*; | ||
andrian-sevastyanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import java.util.stream.Collectors; | ||
|
||
import com.blackduck.integration.detectable.detectables.cargo.data.CargoLockPackageData; | ||
import org.apache.commons.io.FileUtils; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
|
@@ -23,8 +22,11 @@ | |
import com.blackduck.integration.detectable.detectables.cargo.transform.CargoLockPackageTransformer; | ||
import com.blackduck.integration.detectable.extraction.Extraction; | ||
import com.blackduck.integration.util.NameVersion; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class CargoExtractor { | ||
private static final Logger logger = LoggerFactory.getLogger(CargoExtractor.class); | ||
private final CargoTomlParser cargoTomlParser; | ||
private final CargoLockPackageDataTransformer cargoLockPackageDataTransformer; | ||
private final CargoLockPackageTransformer cargoLockPackageTransformer; | ||
|
@@ -39,26 +41,73 @@ public CargoExtractor( | |
this.cargoLockPackageTransformer = cargoLockPackageTransformer; | ||
} | ||
|
||
public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile) throws IOException, DetectableException, MissingExternalIdException { | ||
public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile, CargoDetectableOptions cargoDetectableOptions) throws IOException, DetectableException, MissingExternalIdException { | ||
CargoLockData cargoLockData = new Toml().read(cargoLockFile).to(CargoLockData.class); | ||
List<CargoLockPackage> packages = cargoLockData.getPackages() | ||
.orElse(new ArrayList<>()).stream() | ||
List<CargoLockPackageData> cargoLockPackageDataList = cargoLockData.getPackages().orElse(new ArrayList<>()); | ||
List<CargoLockPackageData> filteredPackages = cargoLockPackageDataList; | ||
String cargoTomlContents = FileUtils.readFileToString(cargoTomlFile, StandardCharsets.UTF_8); | ||
|
||
if (cargoDetectableOptions != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of checking on cargoDetectableOptions object, can we check on dependencyTypeFilter instead? There could be more properties added to this detector in future not related to dependency exclusion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, you're right. The actual filtering is handled by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that, my only concern is that if in future lets say we add some new property for excluding workspaces as an example then those options will also be passed in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current code setup doesn't call the methods of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know, currently your code is not getting executed if cargoDetectableOptions is null. It is not guaranteed that you are going to work on this in any future enhancement related to Cargo and it could be easily missed. I believe we should address this now, its a simple change and not a complicated ask. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, sure. I'll update accordingly. |
||
Map<String, String> excludableDependencyMap = cargoTomlParser.parseDependencyNameVersions(cargoTomlContents, cargoDetectableOptions); | ||
filteredPackages = excludeDependencies(cargoLockPackageDataList, excludableDependencyMap); | ||
} | ||
|
||
List<CargoLockPackage> packages = filteredPackages.stream() | ||
.map(cargoLockPackageDataTransformer::transform) | ||
.collect(Collectors.toList()); | ||
|
||
DependencyGraph graph = cargoLockPackageTransformer.transformToGraph(packages); | ||
|
||
Optional<NameVersion> projectNameVersion = Optional.empty(); | ||
if (cargoTomlFile != null) { | ||
String cargoTomlContents = FileUtils.readFileToString(cargoTomlFile, StandardCharsets.UTF_8); | ||
andrian-sevastyanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
projectNameVersion = cargoTomlParser.parseNameVersionFromCargoToml(cargoTomlContents); | ||
} | ||
|
||
CodeLocation codeLocation = new CodeLocation(graph); //TODO: Consider for producing a ProjectDependencyGraph | ||
|
||
return new Extraction.Builder() | ||
.success(codeLocation) | ||
.nameVersionIfPresent(projectNameVersion) | ||
.build(); | ||
} | ||
|
||
private List<CargoLockPackageData> excludeDependencies( | ||
List<CargoLockPackageData> packages, | ||
Map<String, String> excludableDependencyMap | ||
) { | ||
Set<String> excludedNames = new HashSet<>(); | ||
|
||
List<CargoLockPackageData> filtered = packages.stream() | ||
.filter(pkg -> { | ||
String name = pkg.getName().orElse(null); | ||
String version = pkg.getVersion().orElse(null); | ||
if (name == null || version == null) return true; | ||
|
||
if (excludableDependencyMap.containsKey(name)) { | ||
String constraint = excludableDependencyMap.get(name); | ||
boolean matches = constraint == null || VersionUtils.versionMatches(constraint, version); | ||
if (matches) { | ||
logger.debug("Excluding package '{}' version '{}' due to constraint '{}'", name, version, constraint); | ||
andrian-sevastyanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
excludedNames.add(name); | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
}) | ||
.collect(Collectors.toList()); | ||
|
||
return filtered.stream() | ||
.map(pkg -> new CargoLockPackageData( | ||
pkg.getName().orElse(null), | ||
pkg.getVersion().orElse(null), | ||
pkg.getSource().orElse(null), | ||
pkg.getChecksum().orElse(null), | ||
pkg.getDependencies() | ||
.orElse(new ArrayList<>()) | ||
.stream() | ||
.filter(dep -> !excludedNames.contains(dep)) | ||
.collect(Collectors.toList()) | ||
)) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,22 @@ | ||
package com.blackduck.integration.detectable.detectables.cargo.parse; | ||
|
||
import java.util.Optional; | ||
import java.util.*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another wildcard import statement. |
||
|
||
import com.blackduck.integration.detectable.detectables.cargo.CargoDependencyType; | ||
import com.blackduck.integration.detectable.detectables.cargo.CargoDetectableOptions; | ||
import org.tomlj.Toml; | ||
import org.tomlj.TomlParseResult; | ||
|
||
import com.blackduck.integration.util.NameVersion; | ||
import org.tomlj.TomlTable; | ||
|
||
public class CargoTomlParser { | ||
private static final String NAME_KEY = "name"; | ||
private static final String VERSION_KEY = "version"; | ||
private static final String PACKAGE_KEY = "package"; | ||
private static final String NORMAL_DEPENDENCIES_KEY = "dependencies"; | ||
private static final String BUILD_DEPENDENCIES_KEY = "build-dependencies"; | ||
private static final String DEV_DEPENDENCIES_KEY = "dev-dependencies"; | ||
|
||
public Optional<NameVersion> parseNameVersionFromCargoToml(String tomlFileContents) { | ||
TomlParseResult cargoTomlObject = Toml.parse(tomlFileContents); | ||
|
@@ -22,4 +28,41 @@ public Optional<NameVersion> parseNameVersionFromCargoToml(String tomlFileConten | |
return Optional.empty(); | ||
} | ||
|
||
public Map<String, String> parseDependencyNameVersions(String tomlFileContents, CargoDetectableOptions cargoDetectableOptions) { | ||
andrian-sevastyanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
TomlParseResult toml = Toml.parse(tomlFileContents); | ||
Map<String, String> allDeps = new HashMap<>(); | ||
|
||
if (cargoDetectableOptions.getDependencyTypeFilter().shouldExclude(CargoDependencyType.NORMAL)) { | ||
allDeps.putAll(parseNamedDependenciesFromTable(toml, NORMAL_DEPENDENCIES_KEY)); | ||
} | ||
if (cargoDetectableOptions.getDependencyTypeFilter().shouldExclude(CargoDependencyType.BUILD)) { | ||
allDeps.putAll(parseNamedDependenciesFromTable(toml, BUILD_DEPENDENCIES_KEY)); | ||
} | ||
if (cargoDetectableOptions.getDependencyTypeFilter().shouldExclude(CargoDependencyType.DEV)) { | ||
allDeps.putAll(parseNamedDependenciesFromTable(toml, DEV_DEPENDENCIES_KEY)); | ||
} | ||
|
||
return allDeps; | ||
} | ||
|
||
private Map<String, String> parseNamedDependenciesFromTable(TomlParseResult toml, String sectionKey) { | ||
Map<String, String> deps = new HashMap<>(); | ||
TomlTable table = toml.getTable(sectionKey); | ||
if (table == null) { | ||
return deps; | ||
} | ||
|
||
for (String key : table.keySet()) { | ||
Object value = table.get(key); | ||
if (value instanceof String) { | ||
deps.put(key, (String) value); | ||
} else if (value instanceof TomlTable) { | ||
TomlTable dependencyTable = (TomlTable) value; | ||
String version = dependencyTable.getString(VERSION_KEY); // May be null | ||
deps.put(key, version); | ||
} | ||
} | ||
|
||
return deps; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.