Skip to content

Commit

Permalink
workspace props validation (#1585)
Browse files Browse the repository at this point in the history
* Reduce validation errors

Signed-off-by: Prabhu Subramanian <[email protected]>

---------

Signed-off-by: Prabhu Subramanian <[email protected]>
  • Loading branch information
prabhu authored Jan 20, 2025
1 parent b92f4bd commit eb3ed20
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 9 deletions.
72 changes: 68 additions & 4 deletions lib/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4928,10 +4928,33 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
depsMap[pkgParentRef].add(pkg["bom-ref"]);
}
}
if (apkg.dependencies) {
let optionalDependencies = [];
let devDependencies = [];
if (apkg["dev-dependencies"]) {
for (const agroup of Object.keys(apkg["dev-dependencies"])) {
devDependencies = devDependencies.concat(
apkg["dev-dependencies"][agroup],
);
}
}
if (apkg["optional-dependencies"]) {
for (const agroup of Object.keys(apkg["optional-dependencies"])) {
optionalDependencies = optionalDependencies.concat(
apkg["optional-dependencies"][agroup],
);
}
}
if (
apkg.dependencies ||
devDependencies.length ||
optionalDependencies.length
) {
if (Array.isArray(apkg.dependencies)) {
// pdm.lock files
for (const apkgDep of apkg.dependencies) {
let allDeps = apkg.dependencies;
allDeps = allDeps.concat(devDependencies);
allDeps = allDeps.concat(optionalDependencies);
for (const apkgDep of allDeps) {
// Example: "msgpack>=0.5.2"
const nameStr =
apkgDep.name || apkgDep.split(/(==|<=|~=|>=)/)[0].split(" ")[0];
Expand All @@ -4944,6 +4967,18 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
const dependentPkg = pkgBomRefMap[existingPkgMap[nameStr]];
dependentPkg.properties = dependentPkg.properties || [];
const addedValue = {};
// Is the parent a workspace
if (workspaceComponentMap[pkg.name]) {
dependentPkg.properties.push({
name: "internal:workspaceRef",
value: pkg["bom-ref"],
});
dependentPkg.properties.push({
name: "internal:workspaceSrcFile",
value: workspaceRefPyProjMap[pkg["bom-ref"]],
});
addedValue[pkg["bom-ref"]] = true;
}
for (const pprop of pkg.properties) {
if (
pprop.name.startsWith("internal:workspace") &&
Expand All @@ -4955,14 +4990,13 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
}
}
}
} else if (Object.keys(apkg.dependencies).length) {
} else if (pkg.dependencies && Object.keys(apkg.dependencies).length) {
for (const apkgDep of Object.keys(apkg.dependencies)) {
depsMap[pkg["bom-ref"]].add(existingPkgMap[apkgDep] || apkgDep);
}
}
}
}
pkgList = await getPyMetadata(pkgList, false);
for (const key of Object.keys(depsMap)) {
const dependsOnList = new Set();
const parentPkg = pkgBomRefMap[key];
Expand Down Expand Up @@ -4995,6 +5029,35 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
) {
dependentPkg.properties.push(pprop);
addedValue[pprop.value] = true;
} else if (pprop.name === "internal:is_workspace") {
dependentPkg.properties.push({
name: "internal:workspaceRef",
value: parentPkg["bom-ref"],
});
dependentPkg.properties.push({
name: "internal:workspaceSrcFile",
value: workspaceRefPyProjMap[parentPkg["bom-ref"]],
});
addedValue[parentPkg["bom-ref"]] = true;
addedValue[workspaceRefPyProjMap[parentPkg["bom-ref"]]] = true;
const childDeps = depsMap[dependentPkg["bom-ref"]];
for (const childRef of childDeps) {
if (!childRef.startsWith("pkg:")) {
continue;
}
const childPkg = pkgBomRefMap[childRef];
if (childPkg) {
childPkg.properties = childPkg.properties || [];
childPkg.properties.push({
name: "internal:workspaceRef",
value: parentPkg["bom-ref"],
});
childPkg.properties.push({
name: "internal:workspaceSrcFile",
value: workspaceRefPyProjMap[parentPkg["bom-ref"]],
});
}
}
}
}
}
Expand All @@ -5004,6 +5067,7 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
dependsOn: [...dependsOnList].sort(),
});
}
pkgList = await getPyMetadata(pkgList, false);
return {
parentComponent,
pkgList,
Expand Down
64 changes: 61 additions & 3 deletions lib/helpers/validator.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { join } from "node:path";
import Ajv from "ajv";
import addFormats from "ajv-formats";
import { PackageURL } from "packageurl-js";
import { DEBUG_MODE, dirNameStr, isPartialTree } from "./utils.js";

import { URL, fileURLToPath } from "node:url";
import { URL } from "node:url";
let url = import.meta.url;
if (!url.startsWith("file://")) {
url = new URL(`file://${import.meta.url}`).toString();
Expand Down Expand Up @@ -58,7 +58,10 @@ export const validateBom = (bomJson) => {
}
// Deep validation tests
return (
validateMetadata(bomJson) && validatePurls(bomJson) && validateRefs(bomJson)
validateMetadata(bomJson) &&
validatePurls(bomJson) &&
validateRefs(bomJson) &&
validateProps(bomJson)
);
};

Expand Down Expand Up @@ -266,3 +269,58 @@ export const validateRefs = (bomJson) => {
}
return true;
};

/**
* Validate the component properties
*
* @param {object} bomJson Bom json object
*/
export function validateProps(bomJson) {
const errorList = [];
const warningsList = [];
let isWorkspaceMode = false;
if (bomJson?.components) {
for (const comp of bomJson.components) {
if (!comp.properties) {
warningsList.push(`${comp["bom-ref"]} lacks properties.`);
} else {
let srcFilePropFound = false;
let workspacePropFound = false;
for (const p of comp.properties) {
if (p.name === "SrcFile") {
srcFilePropFound = true;
}
if (p.name === "internal:workspaceRef") {
isWorkspaceMode = true;
workspacePropFound = true;
}
}
if (
["library", "framework"].includes(comp.type) &&
isWorkspaceMode &&
!workspacePropFound &&
comp?.scope !== "optional"
) {
warningsList.push(
`${comp["bom-ref"]} lacks workspace-related properties.`,
);
}
if (!srcFilePropFound) {
warningsList.push(`${comp["bom-ref"]} lacks SrcFile property.`);
}
}
if (!comp.evidence) {
warningsList.push(`${comp["bom-ref"]} lacks evidence.`);
}
}
}
if (DEBUG_MODE && warningsList.length !== 0) {
console.log("===== WARNINGS =====");
console.log(warningsList);
}
if (errorList.length !== 0) {
console.log(errorList);
return false;
}
return true;
}
2 changes: 1 addition & 1 deletion types/lib/helpers/utils.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions types/lib/helpers/validator.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Validate the component properties
*
* @param {object} bomJson Bom json object
*/
export function validateProps(bomJson: object): boolean;
export function validateBom(bomJson: object): boolean;
export function validateMetadata(bomJson: object): boolean;
export function validatePurls(bomJson: object): boolean;
Expand Down
2 changes: 1 addition & 1 deletion types/lib/helpers/validator.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit eb3ed20

Please sign in to comment.