Skip to content
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

feat(plugin-js-packages): set up and implement configuration for audit plugin #551

Merged
merged 4 commits into from
Mar 11, 2024
Merged
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ mocks/ @Tlacenka
# Plugins
/packages/plugin-eslint/ @matejchalk
/packages/plugin-coverage/ @Tlacenka
/packages/plugin-js-packages/ @Tlacenka
/packages/plugin-lighthouse/ @BioPhoton
/examples/plugins/ @BioPhoton
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
- changed-files:
- any-glob-to-any-file: 'packages/plugin-coverage/src/**'

🧩 js-packages-plugin:
- changed-files:
- any-glob-to-any-file: 'packages/plugin-js-packages/src/**'

🧩 utils:
- changed-files:
- any-glob-to-any-file: 'packages/utils/src/**'
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ jobs:
strategy:
fail-fast: false
matrix:
lib: [cli, core, models, utils, plugin-eslint, plugin-coverage]
lib:
- cli
- core
- models
- utils
- plugin-eslint
- plugin-coverage
- plugin-js-packages
scope: [unit, integration]
name: Update code coverage
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ This monorepo contains code for open-source Code PushUp NPM packages:
- plugins:
- [📦 @code-pushup/eslint-plugin](./packages/plugin-eslint#readme) - static analysis using **ESLint** rules
- [📦 @code-pushup/coverage-plugin](./packages/plugin-coverage#readme) - code coverage analysis
- [📦 @code-pushup/js-packages-plugin](./packages/plugin-js-packages#readme) - package audit and outdated dependencies

If you want to contribute, please refer to [CONTRIBUTING.md](./CONTRIBUTING.md).
1 change: 1 addition & 0 deletions e2e/cli-e2e/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"plugin-eslint",
"plugin-lighthouse",
"plugin-coverage",
"plugin-js-packages",
"react-todos-app"
],
"tags": ["scope:core", "scope:plugin", "type:e2e"]
Expand Down
19 changes: 19 additions & 0 deletions packages/plugin-js-packages/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parserOptions": {
"project": ["packages/plugin-js-packages/tsconfig.*?.json"]
}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
}
}
]
}
160 changes: 160 additions & 0 deletions packages/plugin-js-packages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# @code-pushup/js-packages-plugin

[![npm](https://img.shields.io/npm/v/%40code-pushup%2Fjs-packages-plugin.svg)](https://www.npmjs.com/package/@code-pushup/js-packages-plugin)
[![downloads](https://img.shields.io/npm/dm/%40code-pushup%2Fjs-packages-plugin)](https://npmtrends.com/@code-pushup/js-packages-plugin)
[![dependencies](https://img.shields.io/librariesio/release/npm/%40code-pushup/js-packages-plugin)](https://www.npmjs.com/package/@code-pushup/js-packages-plugin?activeTab=dependencies)

📦 **Code PushUp plugin for JavaScript packages.** 🛡️

This plugin checks for known vulnerabilities and outdated dependencies.
It supports the following package managers:

- [NPM](https://docs.npmjs.com/)
- [Yarn v1](https://classic.yarnpkg.com/docs/) & [Yarn v2+](https://yarnpkg.com/getting-started)
- [PNPM](https://pnpm.io/pnpm-cli)

## Getting started

1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file.

2. Insert plugin configuration with your package manager. By default, both `audit` and `outdated` checks will be run. The result should look as follows:

```js
import jsPackagesPlugin from '@code-pushup/js-packages-plugin';

export default {
// ...
plugins: [
// ...
await jsPackagesPlugin({ packageManager: 'npm' }), // replace with your package manager
],
};
```

You may run this plugin with a custom configuration for any supported package manager or command. A custom configuration will look similarly to the following:

```js
import jsPackagesPlugin from '@code-pushup/js-packages-plugin';

export default {
// ...
plugins: [
// ...
await jsPackagesPlugin({ packageManager: ['yarn'], checks: ['audit'] }),
],
};
```

3. (Optional) Reference individual audits or the provided plugin groups which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups).

💡 Assign weights based on what influence each command should have on the overall category score (assign weight 0 to only include as extra info, without influencing category score).

```js
export default {
// ...
categories: [
{
slug: 'security',
title: 'Security',
refs: [
{
type: 'group',
plugin: 'npm-audit', // replace prefix with your package manager
slug: 'js-packages',
weight: 1,
},
],
},
{
slug: 'up-to-date',
title: 'Up-to-date tools',
refs: [
{
type: 'group',
plugin: 'npm-outdated', // replace prefix with your package manager
slug: 'js-packages',
weight: 1,
},
// ...
],
},
// ...
],
};
```

4. Run the CLI with `npx code-pushup collect` and view or upload report (refer to [CLI docs](../cli/README.md)).

## Plugin architecture

### Plugin configuration specification

The plugin accepts the following parameters:

- `packageManager`: The package manager you are using. Supported values: `npm`, `yarn-classic` (v1), `yarn-modern` (v2+), `pnpm`.
- (optional) `checks`: Array of checks to be run. Supported commands: `audit`, `outdated`. Both are configured by default.
- (optional) `auditLevelMapping`: If you wish to set a custom level of issue severity based on audit vulnerability level, you may do so here. Any omitted values will be filled in by defaults. Audit levels are: `critical`, `high`, `moderate`, `low` and `info`. Issue severities are: `error`, `warn` and `info`. By default the mapping is as follows: `critical` and `high` → `error`; `moderate` and `low` → `warning`; `info` → `info`.

### Audits and group

This plugin provides a group per check for a convenient declaration in your config.

```ts
// ...
categories: [
{
slug: 'dependencies',
title: 'Package dependencies',
refs: [
{
type: 'group',
plugin: 'js-packages',
slug: 'npm-audit', // replace prefix with your package manager
weight: 1,
},
{
type: 'group',
plugin: 'js-packages',
slug: 'npm-outdated', // replace prefix with your package manager
weight: 1,
},
// ...
],
},
// ...
],
```

Each dependency group has its own audit. If you want to check only a subset of dependencies (e.g. run audit and outdated for production dependencies) or assign different weights to them, you can do so in the following way:

```ts
// ...
categories: [
{
slug: 'dependencies',
title: 'Package dependencies',
refs: [
{
type: 'audit',
plugin: 'js-packages',
slug: 'npm-audit-prod', // replace prefix with your package manager
weight: 2,
},
{
type: 'audit',
plugin: 'js-packages',
slug: 'npm-audit-dev', // replace prefix with your package manager
weight: 1,
},
{
type: 'audit',
plugin: 'js-packages',
slug: 'npm-outdated-prod', // replace prefix with your package manager
weight: 2,
},
// ...
],
},
// ...
],
```
9 changes: 9 additions & 0 deletions packages/plugin-js-packages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@code-pushup/js-packages-plugin",
"version": "0.26.1",
"dependencies": {
"@code-pushup/models": "*",
"@code-pushup/utils": "*",
"zod": "^3.22.4"
}
}
56 changes: 56 additions & 0 deletions packages/plugin-js-packages/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "plugin-js-packages",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/plugin-js-packages/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/plugin-js-packages",
"main": "packages/plugin-js-packages/src/index.ts",
"tsConfig": "packages/plugin-js-packages/tsconfig.lib.json",
"additionalEntryPoints": ["packages/plugin-js-packages/src/bin.ts"],
"assets": ["packages/plugin-js-packages/*.md"],
"esbuildConfig": "esbuild.config.js"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/plugin-js-packages/**/*.ts",
"packages/plugin-js-packages/package.json"
]
}
},
"unit-test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"config": "packages/plugin-js-packages/vite.config.unit.ts",
"reportsDirectory": "../../coverage/plugin-js-packages/unit-tests"
}
},
"integration-test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"config": "packages/plugin-js-packages/vite.config.integration.ts",
"reportsDirectory": "../../coverage/plugin-js-packages/integration-tests"
}
},
"deploy": {
"options": {
"distFolderPath": "dist/packages/plugin-js-packages"
}
},
"publish": {
"command": "node tools/scripts/publish.mjs plugin-js-packages {args.ver} {args.tag}",
"dependsOn": ["build"]
}
},
"tags": ["scope:plugin", "type:feature"]
}
3 changes: 3 additions & 0 deletions packages/plugin-js-packages/src/bin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { executeRunner } from './lib/runner';

executeRunner();
4 changes: 4 additions & 0 deletions packages/plugin-js-packages/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { jsPackagesPlugin } from './lib/js-packages-plugin';

export default jsPackagesPlugin;
export type { JSPackagesPluginConfig } from './lib/config';
70 changes: 70 additions & 0 deletions packages/plugin-js-packages/src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { z } from 'zod';
import { IssueSeverity, issueSeveritySchema } from '@code-pushup/models';

const packageCommandSchema = z.enum(['audit', 'outdated']);
export type PackageCommand = z.infer<typeof packageCommandSchema>;

const packageManagerSchema = z.enum([
'npm',
'yarn-classic',
'yarn-modern',
'pnpm',
]);
export type PackageManager = z.infer<typeof packageManagerSchema>;

const packageAuditLevelSchema = z.enum([
'info',
'low',
'moderate',
'high',
'critical',
]);
export type PackageAuditLevel = z.infer<typeof packageAuditLevelSchema>;

const defaultAuditLevelMapping: Record<PackageAuditLevel, IssueSeverity> = {
critical: 'error',
high: 'error',
moderate: 'warning',
low: 'warning',
info: 'info',
};

export function fillAuditLevelMapping(
mapping: Partial<Record<PackageAuditLevel, IssueSeverity>>,
): Record<PackageAuditLevel, IssueSeverity> {
return {
critical: mapping.critical ?? defaultAuditLevelMapping.critical,
high: mapping.high ?? defaultAuditLevelMapping.high,
moderate: mapping.moderate ?? defaultAuditLevelMapping.moderate,
low: mapping.low ?? defaultAuditLevelMapping.low,
info: mapping.info ?? defaultAuditLevelMapping.info,
};
}

export const jsPackagesPluginConfigSchema = z.object({
checks: z
.array(packageCommandSchema, {
description:
'Package manager commands to be run. Defaults to both audit and outdated.',
})
.min(1)
.default(['audit', 'outdated']),
packageManager: packageManagerSchema.describe('Package manager to be used.'),
auditLevelMapping: z
.record(packageAuditLevelSchema, issueSeveritySchema, {
description:
'Mapping of audit levels to issue severity. Custom mapping or overrides may be entered manually, otherwise has a default preset.',
})
.default(defaultAuditLevelMapping)
.transform(fillAuditLevelMapping),
});

export type JSPackagesPluginConfig = z.input<
typeof jsPackagesPluginConfigSchema
>;

export type FinalJSPackagesPluginConfig = z.infer<
typeof jsPackagesPluginConfigSchema
>;

export type PackageDependencyType = 'prod' | 'dev' | 'optional';
Loading