Skip to content

Commit

Permalink
Merge pull request #1320 from CycloneDX/pixisupport
Browse files Browse the repository at this point in the history
Feat: Add support for pixi package manager
  • Loading branch information
aryan-rajoria authored Aug 30, 2024
2 parents d9d8f0f + d9b739b commit d3036fb
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 7 deletions.
105 changes: 104 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ import {
parsePackageJsonName,
parsePaketLockData,
parsePiplockData,
parsePixiLockFile,
parsePixiTomlFile,
parsePkgJson,
parsePkgLock,
parsePnpmLock,
Expand Down Expand Up @@ -2703,6 +2705,92 @@ export async function createNodejsBom(path, options) {
});
}

/**
* Function to create bom string for Projects that use Pixi package manager.
* createPixiBom is based on createPythonBom.
* Pixi package manager utilizes many languages like python, rust, C/C++, ruby, etc.
* It produces a Lockfile which help produce reproducible envs across operating systems.
* This code will look at the operating system of our machine and create a BOM specific to that machine.
*
*
* @param {String} path
* @param {Object} options
*/
export async function createPixiBom(path, options) {
const allImports = {};
let metadataFilename = "";
let dependencies = [];
let pkgList = [];
let formulationList = [];
let frozen = true;
let parentComponent = createDefaultParentComponent(path, "pypi", options);
let PixiLockData = {};

const pixiToml = join(path, "pixi.toml");

// if pixi.toml file found then we
// Add parentComponent Details
const pixiTomlMode = existsSync(pixiToml);
if (pixiTomlMode) {
const tmpParentComponent = parsePixiTomlFile(pixiToml);
parentComponent = tmpParentComponent;
parentComponent.type = "application";
const ppurl = new PackageURL(
"pixi",
parentComponent.group || "",
parentComponent.name,
parentComponent.version || "latest",
null,
null,
).toString();
parentComponent["bom-ref"] = decodeURIComponent(ppurl);
parentComponent["purl"] = ppurl;
}

const pixiLockFile = join(path, "pixi.lock");
const pixiFilesMode = existsSync(pixiLockFile);
if (pixiFilesMode) {
// Instead of what we do in createPythonBOM
// where we install packages and run `getPipFrozenTree`
// here I assume `pixi.lock` file to contain the accuracte version information
// across all platforms
PixiLockData = parsePixiLockFile(pixiLockFile, path);
metadataFilename = "pixi.lock";
} else {
if (options.installDeps) {
generatePixiLockFile(path);

const pixiLockFile = join(path, "pixi.lock");
if (!existsSync(pixiLockFile) && DEBUG_MODE) {
console.log(
"Unexpected Error tried to generate pixi.lock file but failed.",
);
console.log("This will result in creations of empty BOM.");
}
PixiLockData = parsePixiLockFile(pixiLockFile);
metadataFilename = "pixi.lock";
} else {
// If no pixi.lock and installDeps is false
// then return None and let `createPythonBOM()` handle generation of BOM.
return null;
}
}

pkgList = PixiLockData.pkgList;
frozen = PixiLockData.frozen;
formulationList = PixiLockData.formulationList;
dependencies = PixiLockData.dependencies;

return buildBomNSData(options, pkgList, "pypi", {
allImports,
src: path,
filename: metadataFilename,
dependencies,
parentComponent,
formulationList,
});
}

/**
* Function to create bom string for Python projects
*
Expand All @@ -2718,6 +2806,19 @@ export async function createPythonBom(path, options) {
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-venv-"));
let parentComponent = createDefaultParentComponent(path, "pypi", options);
const pipenvMode = existsSync(join(path, "Pipfile"));

// If pixi is used then just return that as output instead
const pixiLockFile = join(path, "pixi.lock");
const pixiFilesMode = existsSync(pixiLockFile);
const pixiToml = join(path, "pixi.toml");
const pixiTomlMode = existsSync(pixiToml);
if (pixiTomlMode || pixiFilesMode) {
const BomNSData = createPixiBom(path, options);
if (BomNSData) {
return BomNSData;
}
}

let poetryFiles = getAllFiles(
path,
`${options.multiProject ? "**/" : ""}poetry.lock`,
Expand Down Expand Up @@ -2759,7 +2860,7 @@ export async function createPythonBom(path, options) {
`${options.multiProject ? "**/" : ""}*.egg-info`,
options,
);
const setupPy = join(path, "setup.py");

const pyProjectFile = join(path, "pyproject.toml");
const pyProjectMode = existsSync(pyProjectFile);
if (pyProjectMode) {
Expand Down Expand Up @@ -2787,6 +2888,7 @@ export async function createPythonBom(path, options) {
}
const requirementsMode = reqFiles?.length || reqDirFiles?.length;
const poetryMode = poetryFiles?.length;
const setupPy = join(path, "setup.py");
const setupPyMode = existsSync(setupPy);
// Poetry sets up its own virtual env containing site-packages so
// we give preference to poetry lock file. Issue# 129
Expand Down Expand Up @@ -3069,6 +3171,7 @@ export async function createPythonBom(path, options) {
}
}
}

// Final fallback is to manually parse setup.py if we still
// have an empty list
if (!pkgList.length && setupPyMode) {
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@
"ssri": "^10.0.6",
"table": "^6.8.2",
"tar": "^6.2.1",
"toml": "^3.0.0",
"uuid": "^10.0.0",
"validate-iri": "^1.0.1",
"xml-js": "^1.6.11",
"yargs": "^17.7.2",
"validate-iri": "^1.0.1"
"yargs": "^17.7.2"
},
"optionalDependencies": {
"@appthreat/atom": "2.0.17",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

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

14 changes: 14 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ export function createJavaBom(path: string, options: any): Promise<any>;
* @param {Object} options Parse options from the cli
*/
export function createNodejsBom(path: string, options: any): Promise<any>;
/**
* Function to create bom string for Projects that use Pixi package manager.
* createPixiBom is based on createPythonBom.
* Pixi package manager utilizes many languages like python, rust, C/C++, ruby, etc.
* It produces a Lockfile which help produce reproducible envs across operating systems.
* This code will look at the operating system of our machine and create a BOM specific to that machine.
*
* TODO: make sure pixi.lock package information compiled for all operating systems is actually accurate.
* TODO: measure difference between current pixi.lock package version vs actuall
*
* @param {String} path
* @param {Object} options
*/
export function createPixiBom(path: string, options: any): Promise<any>;
/**
* Function to create bom string for Python projects
*
Expand Down
2 changes: 1 addition & 1 deletion types/index.d.ts.map

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

33 changes: 33 additions & 0 deletions types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,12 +455,45 @@ export function getPyModules(src: string, epkgList: any[], options: any): Promis
* @param {Object} setupPyData Contents of setup.py
*/
export function parseSetupPyFile(setupPyData: any): Promise<any[]>;
/**
* Method to parse pixi.lock data
*
* @param {Object} pixiData Contents of pixi.lock file
*/
export function parsePixiLockFile(pixiLockFileName: any, path: any): {
pkgList: any;
formulationList: any[];
rootList: any[];
dependenciesList: {
ref: string;
dependsOn: string[];
}[];
frozen: boolean;
};
/**
* Method to parse pixi.toml file
*
* @param {String} pixiToml
*/
export function parsePixiTomlFile(pixiToml: string): {
description: any;
name: any;
version: any;
homepage: any;
repository: any;
};
/**
* Method to construct a GitHub API url for the given repo metadata
* @param {Object} repoMetadata Repo metadata with group and name
* @return {String|undefined} github api url (or undefined - if not enough data)
*/
export function repoMetadataToGitHubApiUrl(repoMetadata: any): string | undefined;
/**
* Method to run cli command `pixi install`
*
*
*/
export function generatePixiLockFile(path: any): void;
/**
* Method to split GitHub url into its parts
* @param {String} repoUrl Repository url
Expand Down
Loading

0 comments on commit d3036fb

Please sign in to comment.