Skip to content

Commit

Permalink
Merge branch 'development' of https://github.com/brainhubeu/license-a…
Browse files Browse the repository at this point in the history
…uditor into adjust-default-templates
  • Loading branch information
F-Kublin committed Dec 5, 2024
2 parents 69d5ba9 + c11b110 commit 420c3a6
Show file tree
Hide file tree
Showing 15 changed files with 5,603 additions and 37 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"packageManager": "npm@10.8.2",
"workspaces": ["packages/*", "tooling/*", "test"],
"bin": {
"@brainhubeu/license-auditor": "packages/cli/dist/cli.js"
"license-auditor": "packages/cli/dist/cli.js"
},
"config": {
"commitizen": {
Expand Down
22 changes: 15 additions & 7 deletions packages/cli/src/components/audit-licenses/audit-licenses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
LicenseAuditResult,
LicenseStatus,
} from "@license-auditor/data";
import { Box, useApp } from "ink";
import { Box, Text, useApp } from "ink";
import { useEffect, useState } from "react";
import { envSchema } from "../../env.js";
import { saveResultToJson } from "../../utils/save-result-to-json.js";
Expand All @@ -18,7 +18,7 @@ export type AuditLicensesProps = {
config: ConfigType;
filter: LicenseStatus | undefined;
json: string | undefined;
filterRegex: string | undefined;
filterRegex?: string | undefined;
production?: boolean | undefined;
};

Expand Down Expand Up @@ -46,12 +46,13 @@ export default function AuditLicenses({
setError(parsedEnv.error.message);
return;
}
const { warning, ...result } = await auditLicenses(
parsedEnv.data.ROOT_DIR,
const { warning, ...result } = await auditLicenses({
cwd: parsedEnv.data.ROOT_DIR,
config,
filterRegex,
production,
);
verbose,
});
setResult(result);
if (warning) {
setWarning(warning);
Expand All @@ -67,7 +68,7 @@ export default function AuditLicenses({
}
};
void getResults();
}, [exit, config, production, filterRegex]);
}, [exit, config, production, filterRegex, verbose]);

useEffect(() => {
if (result && json) {
Expand All @@ -77,7 +78,14 @@ export default function AuditLicenses({

if (error) {
return (
<ErrorBox>An error occurred while auditing licenses: {error}</ErrorBox>
<>
<ErrorBox>An error occurred while auditing licenses: {error}</ErrorBox>
{!verbose && (
<Text color="red">
Run the command with --verbose flag to get full error output
</Text>
)}
</>
);
}

Expand Down
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
"@total-typescript/ts-reset": "0.6.1",
"detect-package-manager": "3.0.2",
"fast-glob": "3.3.2",
"lodash.flattendeep": "4.4.0",
"spdx-expression-parse": "4.0.0",
"zod": "3.23.8"
},
"devDependencies": {
"@license-auditor/typescript-config": "*",
"tsup": "8.3.5",
"@types/lodash.flattendeep": "4.4.9",
"@types/spdx-expression-parse": "3.0.5",
"@vitest/coverage-v8": "2.1.5",
"tsup": "8.3.5",
"tsx": "4.19.1",
"typescript": "5.6.2",
"vitest": "2.1.5"
Expand Down
22 changes: 16 additions & 6 deletions packages/core/src/audit-licenses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,27 @@ import { findLicenses } from "./license-finder/find-license.js";
import { parseVerificationStatusToMessage } from "./parse-verification-status-to-message.js";
import { resolveLicenseStatus } from "./resolve-license-status.js";

export async function auditLicenses(
cwd: string,
config: ConfigType,
filterRegex?: string,
production?: boolean | undefined,
): Promise<LicenseAuditResult> {
interface AuditLicensesProps {
cwd: string;
config: ConfigType;
filterRegex?: string | undefined;
production?: boolean | undefined;
verbose?: boolean | undefined;
}

export async function auditLicenses({
cwd,
config,
filterRegex,
production,
verbose,
}: AuditLicensesProps): Promise<LicenseAuditResult> {
const packageManager = await findPackageManager(cwd);
const { dependencies: packagePaths, warning } = await findDependencies({
packageManager,
projectRoot: cwd,
production,
verbose,
});

const resultMap = new Map<string, DetectedLicense>();
Expand Down
19 changes: 13 additions & 6 deletions packages/core/src/dependency-finder/exec-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ import { ExecCommandException } from "../exceptions/index.js";
export async function execCommand(
command: string,
cwd: string,
verbose?: boolean | undefined,
): Promise<string> {
return new Promise((resolve, reject) => {
exec(command, { cwd }, (error, stdout, stderr) => {
if (stderr && !stderr.includes("Debugger attached")) {
if (error || (stderr && !stderr.includes("Debugger attached"))) {
reject(
new ExecCommandException(`Command "${command}" returned an error.`, {
originalError: error,
stdout,
stderr,
}),
new ExecCommandException(
error?.stack && verbose
? `\n${error.stack}\n\n${stdout}`
: `Command "${command}" returned an error.`,
{
originalError: error,
stdout,
stderr,
},
),
);
}

resolve(stdout);
});
});
Expand Down
17 changes: 13 additions & 4 deletions packages/core/src/dependency-finder/find-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ export async function findDependencies({
packageManager,
projectRoot,
production,
verbose,
}: {
packageManager: SupportedPm;
projectRoot: string;
production?: boolean | undefined;
verbose?: boolean | undefined;
}): Promise<DependenciesResult> {
const [{ dependencies: dependencyPaths, warning }, internalPackages] =
await Promise.all([
findExternalDependencies({ packageManager, projectRoot, production }),
findExternalDependencies({
packageManager,
projectRoot,
production,
verbose,
}),
findInternalPackages(projectRoot),
]);

Expand All @@ -35,19 +42,21 @@ function findExternalDependencies({
packageManager,
projectRoot,
production,
verbose,
}: {
packageManager: SupportedPm;
projectRoot: string;
production?: boolean | undefined;
verbose?: boolean | undefined;
}): Promise<DependenciesResult> {
switch (packageManager) {
case "npm":
case "yarn":
return findNpmDependencies(projectRoot, production);
return findNpmDependencies(projectRoot, production, verbose);
case "pnpm":
return findPnpmDependencies(projectRoot, production);
return findPnpmDependencies(projectRoot, production, verbose);
case "yarn-classic":
return findYarnClassicDependencies(projectRoot, production);
return findYarnClassicDependencies(projectRoot, production, verbose);
default:
throw new UnsupportedPackageManagerException(
"Unsupported package manager",
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/dependency-finder/npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export const findNpmProdDepsCommand = "npm ls --all -p --omit=dev";
export async function findNpmDependencies(
projectRoot: string,
production?: boolean | undefined,
verbose?: boolean | undefined,
): Promise<DependenciesResult> {
const { output, warning } = await (async () => {
try {
return {
output: await execCommand(
production ? findNpmProdDepsCommand : findNpmDepsCommand,
projectRoot,
verbose,
),
};
} catch (error) {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/dependency-finder/pnpm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ export const findPnpmProdDepsCommand =
export async function findPnpmDependencies(
projectRoot: string,
production?: boolean | undefined,
verbose?: boolean | undefined,
): Promise<DependenciesResult> {
const output = await execCommand(
production ? findPnpmProdDepsCommand : findPnpmDepsCommand,
projectRoot,
verbose,
);

const [_, ...dependencies] = output.split("\n");
Expand Down
25 changes: 13 additions & 12 deletions packages/core/src/dependency-finder/yarn-classic.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import fs from "node:fs/promises";
import path from "node:path";
import type { DependenciesResult } from "@license-auditor/data";
import flattenDeep from "lodash.flattendeep";
import { z } from "zod";
import { FindDependenciesException } from "../exceptions/find-dependecies.exception.js";
import { execCommand } from "./exec-command.js";

const YarnDependencySchema = z.object({
name: z.string(),
children: z.array(z.any()).length(0),
children: z.array(z.any()),
hint: z.string().nullable(),
color: z.string().nullable(),
depth: z.literal(0),
});

const YarnListOutputSchema = z.object({
Expand All @@ -23,32 +23,33 @@ const YarnListOutputSchema = z.object({

type YarnDependency = z.infer<typeof YarnDependencySchema>;

export const findYarnClassicDepsCommand = "yarn list --depth=0 --json -R";
export const findYarnClassicProdDepsCommand =
"yarn list --depth=0 --json -R --prod";
export const findYarnClassicDepsCommand = "yarn list --json -R";
export const findYarnClassicProdDepsCommand = "yarn list --json -R --prod";

export async function findYarnClassicDependencies(
projectRoot: string,
production?: boolean | undefined,
verbose?: boolean | undefined,
): Promise<DependenciesResult> {
const output = await execCommand(
production ? findYarnClassicProdDepsCommand : findYarnClassicDepsCommand,
projectRoot,
verbose,
);
const dependenciesList = JSON.parse(output);

const validationResult = YarnListOutputSchema.safeParse(dependenciesList);

if (!validationResult.success) {
throw new FindDependenciesException(
"Invalid yarn list --depth=0 --json -R output",
{
originalError: validationResult.error,
},
);
throw new FindDependenciesException("Invalid yarn list -R output", {
originalError: validationResult.error,
});
}

const dependencies = flattenDeep(validationResult.data.data.trees);

const dependencyPaths = await extractDependencyPaths(
validationResult.data.data.trees,
dependencies,
projectRoot,
);

Expand Down
17 changes: 17 additions & 0 deletions test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,20 @@ export const pnpmFixture = test.extend<TestContext>({
await fs.rm(testDirectory, { recursive: true });
},
});

export const yarnFixture = test.extend<TestContext>({
// biome-ignore lint/correctness/noEmptyPattern: destructuring pattern is required in fixture
testDirectory: async ({}, use) => {
const testDirectory = path.resolve(
TEST_TEMP_DIRECTORY,
`testProject-${Math.random().toString(36).substring(2)}`,
);
await fs.cp(path.resolve(TEST_PROJECTS_DIRECTORY, "yarn"), testDirectory, {
recursive: true,
});

await use(testDirectory);

await fs.rm(testDirectory, { recursive: true });
},
});
15 changes: 15 additions & 0 deletions test/test/yarn.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect } from "vitest";
import { yarnFixture } from "../fixtures";
import { getCliPath } from "../utils/get-cli-path";
import { runCliCommand } from "../utils/run-cli-command";

yarnFixture("yarn", async ({ testDirectory }) => {
const { output, errorCode } = await runCliCommand({
command: "npx",
args: [getCliPath(), "--production"],
cwd: testDirectory,
});

expect(errorCode).toBe(0);
expect(output).toContain("66 licenses are compliant");
});
Loading

0 comments on commit 420c3a6

Please sign in to comment.