Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blackduck.integration.detectable.detectable.codelocation.CodeLocation;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmLockYaml;
Expand All @@ -19,6 +21,8 @@
import com.blackduck.integration.util.NameVersion;

public class PnpmLockYamlParser {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

private static final Predicate<String> isNodeRoot = "."::equals;

private PnpmYamlTransformer pnpmTransformer;
Expand All @@ -41,6 +45,9 @@ public List<CodeLocation> parse(File parentFile, PnpmLockYaml pnpmLockYaml,
private List<CodeLocation> createCodeLocationsFromRoot(File sourcePath, PnpmLockYaml pnpmLockYaml,
@Nullable NameVersion projectNameVersion, PnpmLinkedPackageResolver linkedPackageResolver)
throws IntegrationException {
if (pnpmLockYaml.packages == null) {
logger.warn("The pnpm-lock.yaml file has no 'packages' section. No resolved dependencies are present. The scan will continue with an empty dependency graph.");
}
CodeLocation codeLocation = pnpmTransformer.generateCodeLocation(sourcePath, pnpmLockYaml, projectNameVersion,
linkedPackageResolver);
return Collections.singletonList(codeLocation);
Expand All @@ -52,7 +59,14 @@ private List<CodeLocation> createCodeLocationsFromImports(File sourcePath, PnpmL
if (MapUtils.isEmpty(pnpmLockYaml.importers)) {
return Collections.emptyList();
}


if (pnpmLockYaml.packages == null) {
logger.warn("The pnpm-lock.yaml file contains {} importer(s) {} but has no 'packages' section. "
+ "No resolved dependencies are available. All workspaces will have empty dependency graphs.",
pnpmLockYaml.importers.size(),
pnpmLockYaml.importers.keySet());
}

ExcludedIncludedWildcardFilter workspacesFilter;
if (excludedDirectories.isEmpty() && includedDirectories.isEmpty()) {
workspacesFilter = null; // Include all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
Expand All @@ -29,6 +32,8 @@
*/
public class PnpmLockYamlParserInitial {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

private final EnumListFilter<PnpmDependencyType> dependencyFilter;
private final List<String> excludedDirectories;
private final List<String> includedDirectories;
Expand All @@ -42,8 +47,19 @@ public PnpmLockYamlParserInitial(PnpmLockOptions pnpmLockOptions) {
public List<CodeLocation> parse(File pnpmLockYamlFile, @Nullable NameVersion projectNameVersion, PnpmLinkedPackageResolver linkedPackageResolver)
throws IOException, IntegrationException {
PnpmLockYamlBase pnpmLockYaml = parseYamlFile(pnpmLockYamlFile);


if (pnpmLockYaml == null) {
logger.warn("The pnpm-lock.yaml file '{}' is empty and contains no parsable content. No dependencies will be extracted.",
pnpmLockYamlFile.getAbsolutePath());
return Collections.emptyList();
}
Comment thread
bd-spratikbharti marked this conversation as resolved.

if (pnpmLockYaml instanceof PnpmLockYaml) {
if (pnpmLockYaml.lockfileVersion == null) {
throw new IntegrationException(
"The pnpm-lock.yaml file does not contain a 'lockfileVersion' field. "
+ "This is required for parsing. Please regenerate the lock file by running 'pnpm install'.");
}
PnpmYamlTransformer pnpmYamlTransformer = new PnpmYamlTransformer(dependencyFilter, pnpmLockYaml.lockfileVersion);
PnpmLockYamlParser pnpmYamlParser = new PnpmLockYamlParser(pnpmYamlTransformer);
return pnpmYamlParser.parse(pnpmLockYamlFile.getParentFile(), (PnpmLockYaml) pnpmLockYaml, linkedPackageResolver, projectNameVersion, excludedDirectories, includedDirectories);
Expand All @@ -70,15 +86,41 @@ private PnpmLockYamlBase parseYamlFile(File pnpmLockYamlFile) throws FileNotFoun
representer.getPropertyUtils().setSkipMissingProperties(true);

LoaderOptions loaderOptions = new LoaderOptions();

try {
// Try to read the lockfile into the current Yaml classes. It's more common and
// should hopefully work more of the time.
// Try to read the lockfile into the v6/v9 Yaml classes first (more common).
logger.debug("Parsing through v6/v9 format");
Yaml yaml = new Yaml(new Constructor(PnpmLockYaml.class, loaderOptions), representer);
return yaml.load(new FileReader(pnpmLockYamlFile));
PnpmLockYamlBase result = yaml.load(new FileReader(pnpmLockYamlFile));

Comment thread
bd-spratikbharti marked this conversation as resolved.
// If we got a valid result with a v6+ lockfileVersion, use it.
if (result != null && isV6OrNewer(result.lockfileVersion)) {
return result;
}
} catch (ConstructorException e) {
// If the reading fails try to read a v5 Yaml.
Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer);
return yaml.load(new FileReader(pnpmLockYamlFile));
// Fall through to try v5 parsing
}

// Either: lockfileVersion was null, indicated v5, or a ConstructorException was thrown.
// Re-parse as v5.
logger.debug("Parsing through v5 format");
Yaml yaml = new Yaml(new Constructor(PnpmLockYamlv5.class, loaderOptions), representer);
return yaml.load(new FileReader(pnpmLockYamlFile));
}

/**
* Returns true if the lockfileVersion string represents a v6 or newer pnpm lockfile.
* Returns false if the version is v5 or older, or null.
*/
private boolean isV6OrNewer(@Nullable String lockfileVersion) {
if (lockfileVersion == null) {
return false;
}
try {
return Double.parseDouble(lockfileVersion) >= 6.0;
} catch (NumberFormatException e) {
logger.debug("Unable to parse lockfileVersion '{}'; treating as v5/unknown.", lockfileVersion);
return false;
}
}
Comment thread
Copilot marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ private List<CodeLocation> createCodeLocationsFromRoot(
@Nullable NameVersion projectNameVersion,
PnpmLinkedPackageResolver linkedPackageResolver
) throws IntegrationException {
if (pnpmLockYaml.packages == null) {
logger.warn("The pnpm-lock.yaml file has no 'packages' section. No resolved dependencies are present. The scan will continue with an empty dependency graph.");
}
CodeLocation codeLocation = pnpmTransformer.generateCodeLocation(sourcePath, pnpmLockYaml, projectNameVersion, linkedPackageResolver);
return Collections.singletonList(codeLocation);
}
Expand All @@ -61,6 +64,13 @@ private List<CodeLocation> createCodeLocationsFromImports(
return Collections.emptyList();
}

if (pnpmLockYaml.packages == null) {
logger.warn("The pnpm-lock.yaml file contains {} importer(s) {} but has no 'packages' section. "
+ "No resolved dependencies are available. All workspaces will have empty dependency graphs.",
pnpmLockYaml.importers.size(),
pnpmLockYaml.importers.keySet());
}

List<CodeLocation> codeLocations = new LinkedList<>();
for (Map.Entry<String, PnpmProjectPackagev5> projectPackageInfo : pnpmLockYaml.importers.entrySet()) {
String projectKey = projectPackageInfo.getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.blackduck.integration.bdio.model.dependency.Dependency;
import com.blackduck.integration.bdio.model.externalid.ExternalId;
import com.blackduck.integration.detectable.detectable.codelocation.CodeLocation;
import com.blackduck.integration.detectable.detectable.exception.DetectableException;
import com.blackduck.integration.detectable.detectable.util.EnumListFilter;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmDependencyInfo;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmDependencyType;
Expand Down Expand Up @@ -80,7 +79,9 @@ private void buildGraph(
@Nullable Map<String, PnpmPackageInfo> snapshots
) throws IntegrationException {
if (packageMap == null) {
throw new DetectableException("Could not parse 'packages' section of the pnpm-lock.yaml file.");
logger.debug("No packages available to build dependency graph for workspace '{}'. Skipping graph construction.",
reportingProjectPackagePath != null ? reportingProjectPackagePath : "root");
return;
}
for (Map.Entry<String, PnpmPackageInfo> packageEntry : packageMap.entrySet()) {
String packageId = packageEntry.getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.blackduck.integration.bdio.model.dependency.Dependency;
import com.blackduck.integration.bdio.model.externalid.ExternalId;
import com.blackduck.integration.detectable.detectable.codelocation.CodeLocation;
import com.blackduck.integration.detectable.detectable.exception.DetectableException;
import com.blackduck.integration.detectable.detectable.util.EnumListFilter;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmDependencyType;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmLockYamlv5;
Expand Down Expand Up @@ -73,7 +72,9 @@ private void buildGraph(
@Nullable String reportingProjectPackagePath
) throws IntegrationException {
if (packageMap == null) {
throw new DetectableException("Could not parse 'packages' section of the pnpm-lock.yaml file.");
logger.debug("No packages available to build dependency graph for workspace '{}'. Skipping graph construction.",
reportingProjectPackagePath != null ? reportingProjectPackagePath : "root");
return;
}
for (Map.Entry<String, PnpmPackageInfov5> packageEntry : packageMap.entrySet()) {
String packageId = packageEntry.getKey();
Expand Down