@code-pushup/cli 0.51.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/index.js +287 -135
- package/package.json +7 -4
- package/src/lib/implementation/core-config.middleware.d.ts +3 -4
- package/src/lib/implementation/filter.middleware.d.ts +2 -0
- package/src/lib/implementation/filter.model.d.ts +9 -0
- package/src/lib/implementation/filter.options.d.ts +7 -0
- package/src/lib/implementation/validate-filter-options.utils.d.ts +13 -0
- package/src/lib/options.d.ts +2 -0
- package/src/lib/implementation/filter-plugins.middleware.d.ts +0 -3
- package/src/lib/implementation/only-plugins.model.d.ts +0 -6
- package/src/lib/implementation/only-plugins.options.d.ts +0 -4
- package/src/lib/implementation/skip-plugins.model.d.ts +0 -6
- package/src/lib/implementation/skip-plugins.options.d.ts +0 -4
- package/src/lib/implementation/validate-plugin-filter-options.utils.d.ts +0 -8
package/README.md
CHANGED
|
@@ -118,7 +118,26 @@ _If you're looking for programmatic usage, then refer to the underlying [@code-p
|
|
|
118
118
|
|
|
119
119
|
## Portal integration
|
|
120
120
|
|
|
121
|
-
If you have access to the Code PushUp portal,
|
|
121
|
+
If you have access to the Code PushUp portal, you can enable report uploads by installing the `@code-pushup/portal-client` package.
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary>Installation command for <code>npm</code>, <code>yarn</code> and <code>pnpm</code></summary>
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
npm install --save-dev @code-pushup/portal-client
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
yarn add --dev @code-pushup/portal-client
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
pnpm add --save-dev @code-pushup/portal-client
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
</details>
|
|
139
|
+
|
|
140
|
+
Once the package is installed, update your configuration file to include your portal credentials:
|
|
122
141
|
|
|
123
142
|
```ts
|
|
124
143
|
const config: CoreConfig = {
|
package/index.js
CHANGED
|
@@ -2345,11 +2345,11 @@ import { bold as bold4, cyan, cyanBright, green as green2, red } from "ansis";
|
|
|
2345
2345
|
function log(msg = "") {
|
|
2346
2346
|
ui().logger.log(msg);
|
|
2347
2347
|
}
|
|
2348
|
-
function logStdoutSummary(report) {
|
|
2348
|
+
function logStdoutSummary(report, verbose = false) {
|
|
2349
2349
|
const printCategories = report.categories.length > 0;
|
|
2350
2350
|
log(reportToHeaderSection(report));
|
|
2351
2351
|
log();
|
|
2352
|
-
logPlugins(report);
|
|
2352
|
+
logPlugins(report.plugins, verbose);
|
|
2353
2353
|
if (printCategories) {
|
|
2354
2354
|
logCategories(report);
|
|
2355
2355
|
}
|
|
@@ -2360,36 +2360,49 @@ function reportToHeaderSection(report) {
|
|
|
2360
2360
|
const { packageName, version: version3 } = report;
|
|
2361
2361
|
return `${bold4(REPORT_HEADLINE_TEXT)} - ${packageName}@${version3}`;
|
|
2362
2362
|
}
|
|
2363
|
-
function logPlugins(
|
|
2364
|
-
const { plugins } = report;
|
|
2363
|
+
function logPlugins(plugins, verbose) {
|
|
2365
2364
|
plugins.forEach((plugin) => {
|
|
2366
2365
|
const { title, audits } = plugin;
|
|
2366
|
+
const filteredAudits = verbose ? audits : audits.filter(({ score }) => score !== 1);
|
|
2367
|
+
const diff = audits.length - filteredAudits.length;
|
|
2368
|
+
logAudits(title, filteredAudits);
|
|
2369
|
+
if (diff > 0) {
|
|
2370
|
+
const notice = filteredAudits.length === 0 ? `... All ${diff} audits have perfect scores ...` : `... ${diff} audits with perfect scores omitted for brevity ...`;
|
|
2371
|
+
logRow(1, notice);
|
|
2372
|
+
}
|
|
2367
2373
|
log();
|
|
2368
|
-
log(bold4.magentaBright(`${title} audits`));
|
|
2369
|
-
log();
|
|
2370
|
-
audits.forEach((audit) => {
|
|
2371
|
-
ui().row([
|
|
2372
|
-
{
|
|
2373
|
-
text: applyScoreColor({ score: audit.score, text: "\u25CF" }),
|
|
2374
|
-
width: 2,
|
|
2375
|
-
padding: [0, 1, 0, 0]
|
|
2376
|
-
},
|
|
2377
|
-
{
|
|
2378
|
-
text: audit.title,
|
|
2379
|
-
// eslint-disable-next-line no-magic-numbers
|
|
2380
|
-
padding: [0, 3, 0, 0]
|
|
2381
|
-
},
|
|
2382
|
-
{
|
|
2383
|
-
text: cyanBright(audit.displayValue || `${audit.value}`),
|
|
2384
|
-
// eslint-disable-next-line no-magic-numbers
|
|
2385
|
-
width: 20,
|
|
2386
|
-
padding: [0, 0, 0, 0]
|
|
2387
|
-
}
|
|
2388
|
-
]);
|
|
2389
|
-
});
|
|
2390
|
-
log();
|
|
2391
2374
|
});
|
|
2392
2375
|
}
|
|
2376
|
+
function logAudits(pluginTitle, audits) {
|
|
2377
|
+
log();
|
|
2378
|
+
log(bold4.magentaBright(`${pluginTitle} audits`));
|
|
2379
|
+
log();
|
|
2380
|
+
audits.forEach(({ score, title, displayValue, value }) => {
|
|
2381
|
+
logRow(score, title, displayValue || `${value}`);
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
function logRow(score, title, value) {
|
|
2385
|
+
ui().row([
|
|
2386
|
+
{
|
|
2387
|
+
text: applyScoreColor({ score, text: "\u25CF" }),
|
|
2388
|
+
width: 2,
|
|
2389
|
+
padding: [0, 1, 0, 0]
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
text: title,
|
|
2393
|
+
// eslint-disable-next-line no-magic-numbers
|
|
2394
|
+
padding: [0, 3, 0, 0]
|
|
2395
|
+
},
|
|
2396
|
+
...value ? [
|
|
2397
|
+
{
|
|
2398
|
+
text: cyanBright(value),
|
|
2399
|
+
// eslint-disable-next-line no-magic-numbers
|
|
2400
|
+
width: 20,
|
|
2401
|
+
padding: [0, 0, 0, 0]
|
|
2402
|
+
}
|
|
2403
|
+
] : []
|
|
2404
|
+
]);
|
|
2405
|
+
}
|
|
2393
2406
|
function logCategories({ categories, plugins }) {
|
|
2394
2407
|
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
2395
2408
|
const rows = categories.map(({ title, score, refs, isBinary }) => [
|
|
@@ -2535,7 +2548,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2535
2548
|
|
|
2536
2549
|
// packages/core/package.json
|
|
2537
2550
|
var name = "@code-pushup/core";
|
|
2538
|
-
var version = "0.
|
|
2551
|
+
var version = "0.52.0";
|
|
2539
2552
|
|
|
2540
2553
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2541
2554
|
import { bold as bold5 } from "ansis";
|
|
@@ -2730,10 +2743,8 @@ var PersistError = class extends Error {
|
|
|
2730
2743
|
super(`fileName: ${reportPath} could not be saved.`);
|
|
2731
2744
|
}
|
|
2732
2745
|
};
|
|
2733
|
-
async function persistReport(report, options2) {
|
|
2746
|
+
async function persistReport(report, sortedScoredReport, options2) {
|
|
2734
2747
|
const { outputDir, filename, format } = options2;
|
|
2735
|
-
const sortedScoredReport = sortReport(scoreReport(report));
|
|
2736
|
-
logStdoutSummary(sortedScoredReport);
|
|
2737
2748
|
const results = format.map((reportType) => {
|
|
2738
2749
|
switch (reportType) {
|
|
2739
2750
|
case "json":
|
|
@@ -2779,7 +2790,13 @@ function logPersistedResults(persistResults) {
|
|
|
2779
2790
|
async function collectAndPersistReports(options2) {
|
|
2780
2791
|
const { exec } = verboseUtils(options2.verbose);
|
|
2781
2792
|
const report = await collect(options2);
|
|
2782
|
-
const
|
|
2793
|
+
const sortedScoredReport = sortReport(scoreReport(report));
|
|
2794
|
+
const persistResults = await persistReport(
|
|
2795
|
+
report,
|
|
2796
|
+
sortedScoredReport,
|
|
2797
|
+
options2.persist
|
|
2798
|
+
);
|
|
2799
|
+
logStdoutSummary(sortedScoredReport, options2.verbose);
|
|
2783
2800
|
exec(() => {
|
|
2784
2801
|
logPersistedResults(persistResults);
|
|
2785
2802
|
});
|
|
@@ -2791,10 +2808,6 @@ async function collectAndPersistReports(options2) {
|
|
|
2791
2808
|
// packages/core/src/lib/compare.ts
|
|
2792
2809
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
2793
2810
|
import { join as join5 } from "node:path";
|
|
2794
|
-
import {
|
|
2795
|
-
PortalOperationError,
|
|
2796
|
-
getPortalComparisonLink
|
|
2797
|
-
} from "@code-pushup/portal-client";
|
|
2798
2811
|
|
|
2799
2812
|
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2800
2813
|
function compareCategories(reports) {
|
|
@@ -2930,6 +2943,18 @@ function selectMeta(meta) {
|
|
|
2930
2943
|
};
|
|
2931
2944
|
}
|
|
2932
2945
|
|
|
2946
|
+
// packages/core/src/lib/load-portal-client.ts
|
|
2947
|
+
async function loadPortalClient() {
|
|
2948
|
+
try {
|
|
2949
|
+
return await import("@code-pushup/portal-client");
|
|
2950
|
+
} catch {
|
|
2951
|
+
ui().logger.error(
|
|
2952
|
+
"Optional peer dependency @code-pushup/portal-client is not available. Make sure it is installed to enable upload functionality."
|
|
2953
|
+
);
|
|
2954
|
+
return null;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2933
2958
|
// packages/core/src/lib/compare.ts
|
|
2934
2959
|
async function compareReportFiles(inputPaths, persistConfig, uploadConfig, label) {
|
|
2935
2960
|
const { outputDir, filename, format } = persistConfig;
|
|
@@ -2994,6 +3019,11 @@ function reportsDiffToFileContent(reportsDiff, format) {
|
|
|
2994
3019
|
}
|
|
2995
3020
|
async function fetchPortalComparisonLink(uploadConfig, commits) {
|
|
2996
3021
|
const { server, apiKey, organization, project } = uploadConfig;
|
|
3022
|
+
const portalClient = await loadPortalClient();
|
|
3023
|
+
if (!portalClient) {
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
const { PortalOperationError, getPortalComparisonLink } = portalClient;
|
|
2997
3027
|
try {
|
|
2998
3028
|
return await getPortalComparisonLink({
|
|
2999
3029
|
server,
|
|
@@ -3016,11 +3046,6 @@ async function fetchPortalComparisonLink(uploadConfig, commits) {
|
|
|
3016
3046
|
}
|
|
3017
3047
|
}
|
|
3018
3048
|
|
|
3019
|
-
// packages/core/src/lib/upload.ts
|
|
3020
|
-
import {
|
|
3021
|
-
uploadToPortal
|
|
3022
|
-
} from "@code-pushup/portal-client";
|
|
3023
|
-
|
|
3024
3049
|
// packages/core/src/lib/implementation/report-to-gql.ts
|
|
3025
3050
|
import {
|
|
3026
3051
|
CategoryConfigRefType as PortalCategoryRefType,
|
|
@@ -3167,10 +3192,15 @@ function tableAlignmentToGQL(alignment) {
|
|
|
3167
3192
|
}
|
|
3168
3193
|
|
|
3169
3194
|
// packages/core/src/lib/upload.ts
|
|
3170
|
-
async function upload(options2
|
|
3195
|
+
async function upload(options2) {
|
|
3171
3196
|
if (options2.upload == null) {
|
|
3172
3197
|
throw new Error("Upload configuration is not set.");
|
|
3173
3198
|
}
|
|
3199
|
+
const portalClient = await loadPortalClient();
|
|
3200
|
+
if (!portalClient) {
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
const { uploadToPortal } = portalClient;
|
|
3174
3204
|
const { apiKey, server, organization, project, timeout } = options2.upload;
|
|
3175
3205
|
const report = await loadReport({
|
|
3176
3206
|
...options2.persist,
|
|
@@ -3185,7 +3215,7 @@ async function upload(options2, uploadFn = uploadToPortal) {
|
|
|
3185
3215
|
commit: report.commit.hash,
|
|
3186
3216
|
...reportToGQL(report)
|
|
3187
3217
|
};
|
|
3188
|
-
return
|
|
3218
|
+
return uploadToPortal({ apiKey, server, data, timeout });
|
|
3189
3219
|
}
|
|
3190
3220
|
|
|
3191
3221
|
// packages/core/src/lib/history.ts
|
|
@@ -3368,8 +3398,10 @@ function yargsAutorunCommandObject() {
|
|
|
3368
3398
|
renderConfigureCategoriesHint();
|
|
3369
3399
|
}
|
|
3370
3400
|
if (options2.upload) {
|
|
3371
|
-
const
|
|
3372
|
-
|
|
3401
|
+
const report = await upload(options2);
|
|
3402
|
+
if (report?.url) {
|
|
3403
|
+
uploadSuccessfulLog(report.url);
|
|
3404
|
+
}
|
|
3373
3405
|
} else {
|
|
3374
3406
|
ui().logger.warning("Upload skipped because configuration is not set.");
|
|
3375
3407
|
renderIntegratePortalHint();
|
|
@@ -3470,6 +3502,94 @@ function yargsCompareCommandObject() {
|
|
|
3470
3502
|
// packages/cli/src/lib/history/history-command.ts
|
|
3471
3503
|
import { bold as bold10, gray as gray7 } from "ansis";
|
|
3472
3504
|
|
|
3505
|
+
// packages/cli/src/lib/implementation/global.utils.ts
|
|
3506
|
+
import yargs from "yargs";
|
|
3507
|
+
|
|
3508
|
+
// packages/cli/src/lib/implementation/validate-filter-options.utils.ts
|
|
3509
|
+
var OptionValidationError = class extends Error {
|
|
3510
|
+
};
|
|
3511
|
+
function validateFilterOption(option, { plugins, categories }, { itemsToFilter, verbose }) {
|
|
3512
|
+
const itemsToFilterSet = new Set(itemsToFilter);
|
|
3513
|
+
const validItems = isCategoryOption(option) ? categories : isPluginOption(option) ? plugins : [];
|
|
3514
|
+
const invalidItems = itemsToFilter.filter(
|
|
3515
|
+
(item) => !validItems.some(({ slug }) => slug === item)
|
|
3516
|
+
);
|
|
3517
|
+
const message = createValidationMessage(option, invalidItems, validItems);
|
|
3518
|
+
if (isOnlyOption(option) && itemsToFilterSet.size > 0 && itemsToFilterSet.size === invalidItems.length) {
|
|
3519
|
+
throw new OptionValidationError(message);
|
|
3520
|
+
}
|
|
3521
|
+
if (invalidItems.length > 0) {
|
|
3522
|
+
ui().logger.warning(message);
|
|
3523
|
+
}
|
|
3524
|
+
if (isPluginOption(option) && categories.length > 0 && verbose) {
|
|
3525
|
+
const removedCategorySlugs = filterItemRefsBy(
|
|
3526
|
+
categories,
|
|
3527
|
+
({ plugin }) => isOnlyOption(option) ? !itemsToFilterSet.has(plugin) : itemsToFilterSet.has(plugin)
|
|
3528
|
+
).map(({ slug }) => slug);
|
|
3529
|
+
if (removedCategorySlugs.length > 0) {
|
|
3530
|
+
ui().logger.info(
|
|
3531
|
+
`The --${option} argument removed the following categories: ${removedCategorySlugs.join(
|
|
3532
|
+
", "
|
|
3533
|
+
)}.`
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
function validateFinalState(filteredItems, originalItems) {
|
|
3539
|
+
const { categories: filteredCategories, plugins: filteredPlugins } = filteredItems;
|
|
3540
|
+
const { categories: originalCategories, plugins: originalPlugins } = originalItems;
|
|
3541
|
+
if (filteredCategories.length === 0 && filteredPlugins.length === 0 && (originalPlugins.length > 0 || originalCategories.length > 0)) {
|
|
3542
|
+
const availablePlugins = originalPlugins.map((p) => p.slug).join(", ") || "none";
|
|
3543
|
+
const availableCategories = originalCategories.map((c) => c.slug).join(", ") || "none";
|
|
3544
|
+
throw new OptionValidationError(
|
|
3545
|
+
`Nothing to report. No plugins or categories are available after filtering. Available plugins: ${availablePlugins}. Available categories: ${availableCategories}.`
|
|
3546
|
+
);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
function isCategoryOption(option) {
|
|
3550
|
+
return option.endsWith("Categories");
|
|
3551
|
+
}
|
|
3552
|
+
function isPluginOption(option) {
|
|
3553
|
+
return option.endsWith("Plugins");
|
|
3554
|
+
}
|
|
3555
|
+
function isOnlyOption(option) {
|
|
3556
|
+
return option.startsWith("only");
|
|
3557
|
+
}
|
|
3558
|
+
function getItemType(option, count) {
|
|
3559
|
+
const itemType = isCategoryOption(option) ? "category" : isPluginOption(option) ? "plugin" : "item";
|
|
3560
|
+
return pluralize(itemType, count);
|
|
3561
|
+
}
|
|
3562
|
+
function createValidationMessage(option, invalidItems, validItems) {
|
|
3563
|
+
const invalidItem = getItemType(option, invalidItems.length);
|
|
3564
|
+
const invalidItemText = invalidItems.length === 1 ? `a ${invalidItem} that does not exist:` : `${invalidItem} that do not exist:`;
|
|
3565
|
+
const invalidSlugs = invalidItems.join(", ");
|
|
3566
|
+
const validItem = getItemType(option, validItems.length);
|
|
3567
|
+
const validItemText = validItems.length === 1 ? `The only valid ${validItem} is` : `Valid ${validItem} are`;
|
|
3568
|
+
const validSlugs = validItems.map(({ slug }) => slug).join(", ");
|
|
3569
|
+
return `The --${option} argument references ${invalidItemText} ${invalidSlugs}. ${validItemText} ${validSlugs}.`;
|
|
3570
|
+
}
|
|
3571
|
+
function handleConflictingOptions(type, onlyItems, skipItems) {
|
|
3572
|
+
const conflictingItems = onlyItems.filter((item) => skipItems.includes(item));
|
|
3573
|
+
if (conflictingItems.length > 0) {
|
|
3574
|
+
const conflictSubject = () => {
|
|
3575
|
+
switch (type) {
|
|
3576
|
+
case "categories":
|
|
3577
|
+
return conflictingItems.length > 1 ? "categories are" : "category is";
|
|
3578
|
+
case "plugins":
|
|
3579
|
+
return conflictingItems.length > 1 ? "plugins are" : "plugin is";
|
|
3580
|
+
}
|
|
3581
|
+
};
|
|
3582
|
+
const conflictingSlugs = conflictingItems.join(", ");
|
|
3583
|
+
throw new OptionValidationError(
|
|
3584
|
+
`The following ${conflictSubject()} specified in both --only${capitalize(
|
|
3585
|
+
type
|
|
3586
|
+
)} and --skip${capitalize(
|
|
3587
|
+
type
|
|
3588
|
+
)}: ${conflictingSlugs}. Please choose one option.`
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3473
3593
|
// packages/cli/src/lib/implementation/global.utils.ts
|
|
3474
3594
|
function filterKebabCaseKeys(obj) {
|
|
3475
3595
|
return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
|
|
@@ -3485,9 +3605,15 @@ function logErrorBeforeThrow(fn) {
|
|
|
3485
3605
|
try {
|
|
3486
3606
|
return await fn(...args);
|
|
3487
3607
|
} catch (error) {
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3608
|
+
if (error instanceof OptionValidationError) {
|
|
3609
|
+
ui().logger.error(error.message);
|
|
3610
|
+
await new Promise((resolve) => process.stdout.write("", resolve));
|
|
3611
|
+
yargs().exit(1, error);
|
|
3612
|
+
} else {
|
|
3613
|
+
console.error(error);
|
|
3614
|
+
await new Promise((resolve) => process.stdout.write("", resolve));
|
|
3615
|
+
throw error;
|
|
3616
|
+
}
|
|
3491
3617
|
}
|
|
3492
3618
|
};
|
|
3493
3619
|
}
|
|
@@ -3495,21 +3621,19 @@ function coerceArray(param) {
|
|
|
3495
3621
|
return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
|
|
3496
3622
|
}
|
|
3497
3623
|
|
|
3498
|
-
// packages/cli/src/lib/implementation/
|
|
3499
|
-
var
|
|
3500
|
-
describe: "List of
|
|
3624
|
+
// packages/cli/src/lib/implementation/filter.options.ts
|
|
3625
|
+
var skipCategoriesOption = {
|
|
3626
|
+
describe: "List of categories to skip. If not set all categories are run.",
|
|
3501
3627
|
type: "array",
|
|
3502
3628
|
default: [],
|
|
3503
|
-
coerce: coerceArray
|
|
3504
|
-
|
|
3629
|
+
coerce: coerceArray
|
|
3630
|
+
};
|
|
3631
|
+
var onlyCategoriesOption = {
|
|
3632
|
+
describe: "List of categories to run. If not set all categories are run.",
|
|
3633
|
+
type: "array",
|
|
3634
|
+
default: [],
|
|
3635
|
+
coerce: coerceArray
|
|
3505
3636
|
};
|
|
3506
|
-
function yargsOnlyPluginsOptionsDefinition() {
|
|
3507
|
-
return {
|
|
3508
|
-
onlyPlugins: onlyPluginsOption
|
|
3509
|
-
};
|
|
3510
|
-
}
|
|
3511
|
-
|
|
3512
|
-
// packages/cli/src/lib/implementation/skip-plugins.options.ts
|
|
3513
3637
|
var skipPluginsOption = {
|
|
3514
3638
|
describe: "List of plugins to skip. If not set all plugins are run.",
|
|
3515
3639
|
type: "array",
|
|
@@ -3517,9 +3641,19 @@ var skipPluginsOption = {
|
|
|
3517
3641
|
coerce: coerceArray,
|
|
3518
3642
|
alias: "P"
|
|
3519
3643
|
};
|
|
3520
|
-
|
|
3644
|
+
var onlyPluginsOption = {
|
|
3645
|
+
describe: "List of plugins to run. If not set all plugins are run.",
|
|
3646
|
+
type: "array",
|
|
3647
|
+
default: [],
|
|
3648
|
+
coerce: coerceArray,
|
|
3649
|
+
alias: "p"
|
|
3650
|
+
};
|
|
3651
|
+
function yargsFilterOptionsDefinition() {
|
|
3521
3652
|
return {
|
|
3522
|
-
|
|
3653
|
+
skipCategories: skipCategoriesOption,
|
|
3654
|
+
onlyCategories: onlyCategoriesOption,
|
|
3655
|
+
skipPlugins: skipPluginsOption,
|
|
3656
|
+
onlyPlugins: onlyPluginsOption
|
|
3523
3657
|
};
|
|
3524
3658
|
}
|
|
3525
3659
|
|
|
@@ -3630,17 +3764,16 @@ function yargsHistoryCommandObject() {
|
|
|
3630
3764
|
return {
|
|
3631
3765
|
command,
|
|
3632
3766
|
describe: "Collect reports for commit history",
|
|
3633
|
-
builder: (
|
|
3634
|
-
|
|
3767
|
+
builder: (yargs3) => {
|
|
3768
|
+
yargs3.options({
|
|
3635
3769
|
...yargsHistoryOptionsDefinition(),
|
|
3636
|
-
...
|
|
3637
|
-
...yargsSkipPluginsOptionsDefinition()
|
|
3770
|
+
...yargsFilterOptionsDefinition()
|
|
3638
3771
|
});
|
|
3639
|
-
|
|
3772
|
+
yargs3.group(
|
|
3640
3773
|
Object.keys(yargsHistoryOptionsDefinition()),
|
|
3641
3774
|
"History Options:"
|
|
3642
3775
|
);
|
|
3643
|
-
return
|
|
3776
|
+
return yargs3;
|
|
3644
3777
|
},
|
|
3645
3778
|
handler
|
|
3646
3779
|
};
|
|
@@ -3707,8 +3840,10 @@ function yargsUploadCommandObject() {
|
|
|
3707
3840
|
renderIntegratePortalHint();
|
|
3708
3841
|
throw new Error("Upload configuration not set");
|
|
3709
3842
|
}
|
|
3710
|
-
const
|
|
3711
|
-
|
|
3843
|
+
const report = await upload(options2);
|
|
3844
|
+
if (report?.url) {
|
|
3845
|
+
uploadSuccessfulLog(report.url);
|
|
3846
|
+
}
|
|
3712
3847
|
}
|
|
3713
3848
|
};
|
|
3714
3849
|
}
|
|
@@ -3765,80 +3900,99 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
3765
3900
|
}
|
|
3766
3901
|
var normalizeFormats = (formats) => (formats ?? []).flatMap((format) => format.split(","));
|
|
3767
3902
|
|
|
3768
|
-
// packages/cli/src/lib/implementation/
|
|
3769
|
-
function
|
|
3770
|
-
plugins,
|
|
3771
|
-
categories
|
|
3772
|
-
}, {
|
|
3773
|
-
pluginsToFilter = [],
|
|
3774
|
-
verbose = false
|
|
3775
|
-
} = {}) {
|
|
3776
|
-
const pluginsToFilterSet = new Set(pluginsToFilter);
|
|
3777
|
-
const missingPlugins = pluginsToFilter.filter(
|
|
3778
|
-
(plugin) => !plugins.some(({ slug }) => slug === plugin)
|
|
3779
|
-
);
|
|
3780
|
-
const isSkipOption = filterOption === "skipPlugins";
|
|
3781
|
-
const filterFunction = (plugin) => isSkipOption ? pluginsToFilterSet.has(plugin) : !pluginsToFilterSet.has(plugin);
|
|
3782
|
-
if (missingPlugins.length > 0) {
|
|
3783
|
-
ui().logger.warning(
|
|
3784
|
-
`The --${filterOption} argument references ${missingPlugins.length === 1 ? "a plugin that does" : "plugins that do"} not exist: ${missingPlugins.join(", ")}. The valid plugin ${plugins.length === 1 ? "slug is" : "slugs are"} ${plugins.map(({ slug }) => slug).join(", ")}.`
|
|
3785
|
-
);
|
|
3786
|
-
}
|
|
3787
|
-
if (categories.length > 0 && verbose) {
|
|
3788
|
-
const removedCategorySlugs = filterItemRefsBy(
|
|
3789
|
-
categories,
|
|
3790
|
-
({ plugin }) => filterFunction(plugin)
|
|
3791
|
-
).map(({ slug }) => slug);
|
|
3792
|
-
ui().logger.info(
|
|
3793
|
-
`The --${filterOption} argument removed the following categories: ${removedCategorySlugs.join(
|
|
3794
|
-
", "
|
|
3795
|
-
)}.`
|
|
3796
|
-
);
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
|
|
3800
|
-
// packages/cli/src/lib/implementation/filter-plugins.middleware.ts
|
|
3801
|
-
function filterPluginsMiddleware(originalProcessArgs) {
|
|
3903
|
+
// packages/cli/src/lib/implementation/filter.middleware.ts
|
|
3904
|
+
function filterMiddleware(originalProcessArgs) {
|
|
3802
3905
|
const {
|
|
3803
3906
|
plugins,
|
|
3804
3907
|
categories = [],
|
|
3908
|
+
skipCategories = [],
|
|
3909
|
+
onlyCategories = [],
|
|
3805
3910
|
skipPlugins = [],
|
|
3806
3911
|
onlyPlugins = [],
|
|
3807
|
-
verbose
|
|
3912
|
+
verbose = false
|
|
3808
3913
|
} = originalProcessArgs;
|
|
3809
|
-
if (skipPlugins.length === 0 && onlyPlugins.length === 0) {
|
|
3914
|
+
if (skipCategories.length === 0 && onlyCategories.length === 0 && skipPlugins.length === 0 && onlyPlugins.length === 0) {
|
|
3810
3915
|
return { ...originalProcessArgs, categories };
|
|
3811
3916
|
}
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
{
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
{ plugins, categories },
|
|
3820
|
-
{ pluginsToFilter: onlyPlugins, verbose }
|
|
3917
|
+
handleConflictingOptions("categories", onlyCategories, skipCategories);
|
|
3918
|
+
handleConflictingOptions("plugins", onlyPlugins, skipPlugins);
|
|
3919
|
+
const filteredCategories = applyCategoryFilters(
|
|
3920
|
+
{ categories, plugins },
|
|
3921
|
+
skipCategories,
|
|
3922
|
+
onlyCategories,
|
|
3923
|
+
verbose
|
|
3821
3924
|
);
|
|
3822
|
-
const
|
|
3823
|
-
|
|
3925
|
+
const filteredPlugins = applyPluginFilters(
|
|
3926
|
+
{ categories: filteredCategories, plugins },
|
|
3927
|
+
skipPlugins,
|
|
3928
|
+
onlyPlugins,
|
|
3929
|
+
verbose
|
|
3824
3930
|
);
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3931
|
+
const finalCategories = filterItemRefsBy(
|
|
3932
|
+
filteredCategories,
|
|
3933
|
+
(ref) => filteredPlugins.some((plugin) => plugin.slug === ref.plugin)
|
|
3827
3934
|
);
|
|
3828
|
-
|
|
3829
|
-
|
|
3935
|
+
validateFinalState(
|
|
3936
|
+
{ categories: finalCategories, plugins: filteredPlugins },
|
|
3937
|
+
{ categories, plugins }
|
|
3830
3938
|
);
|
|
3831
|
-
const filteredPlugins = validOnlyPlugins.size > 0 ? pluginsAfterSkip.filter(({ slug }) => validOnlyPlugins.has(slug)) : pluginsAfterSkip;
|
|
3832
|
-
const filteredCategories = filteredPlugins.length > 0 ? filterItemRefsBy(
|
|
3833
|
-
categories,
|
|
3834
|
-
({ plugin }) => filteredPlugins.some(({ slug }) => slug === plugin)
|
|
3835
|
-
) : categories;
|
|
3836
3939
|
return {
|
|
3837
3940
|
...originalProcessArgs,
|
|
3838
3941
|
plugins: filteredPlugins,
|
|
3839
|
-
categories:
|
|
3942
|
+
categories: finalCategories
|
|
3840
3943
|
};
|
|
3841
3944
|
}
|
|
3945
|
+
function applyFilters(items, skipItems, onlyItems, key) {
|
|
3946
|
+
return items.filter((item) => {
|
|
3947
|
+
const itemKey = item[key];
|
|
3948
|
+
return !skipItems.includes(itemKey) && (onlyItems.length === 0 || onlyItems.includes(itemKey));
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
function applyCategoryFilters({ categories, plugins }, skipCategories, onlyCategories, verbose) {
|
|
3952
|
+
if (skipCategories.length === 0 && onlyCategories.length === 0) {
|
|
3953
|
+
return categories;
|
|
3954
|
+
}
|
|
3955
|
+
validateFilterOption(
|
|
3956
|
+
"skipCategories",
|
|
3957
|
+
{ plugins, categories },
|
|
3958
|
+
{ itemsToFilter: skipCategories, verbose }
|
|
3959
|
+
);
|
|
3960
|
+
validateFilterOption(
|
|
3961
|
+
"onlyCategories",
|
|
3962
|
+
{ plugins, categories },
|
|
3963
|
+
{ itemsToFilter: onlyCategories, verbose }
|
|
3964
|
+
);
|
|
3965
|
+
return applyFilters(categories, skipCategories, onlyCategories, "slug");
|
|
3966
|
+
}
|
|
3967
|
+
function applyPluginFilters({ categories, plugins }, skipPlugins, onlyPlugins, verbose) {
|
|
3968
|
+
const filteredPlugins = filterPluginsFromCategories({
|
|
3969
|
+
categories,
|
|
3970
|
+
plugins
|
|
3971
|
+
});
|
|
3972
|
+
if (skipPlugins.length === 0 && onlyPlugins.length === 0) {
|
|
3973
|
+
return filteredPlugins;
|
|
3974
|
+
}
|
|
3975
|
+
validateFilterOption(
|
|
3976
|
+
"skipPlugins",
|
|
3977
|
+
{ plugins: filteredPlugins, categories },
|
|
3978
|
+
{ itemsToFilter: skipPlugins, verbose }
|
|
3979
|
+
);
|
|
3980
|
+
validateFilterOption(
|
|
3981
|
+
"onlyPlugins",
|
|
3982
|
+
{ plugins: filteredPlugins, categories },
|
|
3983
|
+
{ itemsToFilter: onlyPlugins, verbose }
|
|
3984
|
+
);
|
|
3985
|
+
return applyFilters(filteredPlugins, skipPlugins, onlyPlugins, "slug");
|
|
3986
|
+
}
|
|
3987
|
+
function filterPluginsFromCategories({
|
|
3988
|
+
categories,
|
|
3989
|
+
plugins
|
|
3990
|
+
}) {
|
|
3991
|
+
const validPluginSlugs = new Set(
|
|
3992
|
+
categories.flatMap((category) => category.refs.map((ref) => ref.plugin))
|
|
3993
|
+
);
|
|
3994
|
+
return categories.length > 0 ? plugins.filter((plugin) => validPluginSlugs.has(plugin.slug)) : plugins;
|
|
3995
|
+
}
|
|
3842
3996
|
|
|
3843
3997
|
// packages/cli/src/lib/middlewares.ts
|
|
3844
3998
|
var middlewares = [
|
|
@@ -3847,7 +4001,7 @@ var middlewares = [
|
|
|
3847
4001
|
applyBeforeValidation: false
|
|
3848
4002
|
},
|
|
3849
4003
|
{
|
|
3850
|
-
middlewareFunction:
|
|
4004
|
+
middlewareFunction: filterMiddleware,
|
|
3851
4005
|
applyBeforeValidation: false
|
|
3852
4006
|
}
|
|
3853
4007
|
];
|
|
@@ -3924,14 +4078,12 @@ function yargsGlobalOptionsDefinition() {
|
|
|
3924
4078
|
var options = {
|
|
3925
4079
|
...yargsGlobalOptionsDefinition(),
|
|
3926
4080
|
...yargsCoreConfigOptionsDefinition(),
|
|
3927
|
-
...
|
|
3928
|
-
...yargsSkipPluginsOptionsDefinition()
|
|
4081
|
+
...yargsFilterOptionsDefinition()
|
|
3929
4082
|
};
|
|
3930
4083
|
var groups = {
|
|
3931
4084
|
"Global Options:": [
|
|
3932
4085
|
...Object.keys(yargsGlobalOptionsDefinition()),
|
|
3933
|
-
...Object.keys(
|
|
3934
|
-
...Object.keys(yargsSkipPluginsOptionsDefinition())
|
|
4086
|
+
...Object.keys(yargsFilterOptionsDefinition())
|
|
3935
4087
|
],
|
|
3936
4088
|
"Persist Options:": Object.keys(yargsPersistConfigOptionsDefinition()),
|
|
3937
4089
|
"Upload Options:": Object.keys(yargsUploadConfigOptionsDefinition())
|
|
@@ -3939,10 +4091,10 @@ var groups = {
|
|
|
3939
4091
|
|
|
3940
4092
|
// packages/cli/src/lib/yargs-cli.ts
|
|
3941
4093
|
import { blue, dim as dim2, green as green4 } from "ansis";
|
|
3942
|
-
import
|
|
4094
|
+
import yargs2 from "yargs";
|
|
3943
4095
|
|
|
3944
4096
|
// packages/cli/package.json
|
|
3945
|
-
var version2 = "0.
|
|
4097
|
+
var version2 = "0.52.0";
|
|
3946
4098
|
|
|
3947
4099
|
// packages/cli/src/lib/implementation/formatting.ts
|
|
3948
4100
|
import { bold as bold13, dim, green as green3 } from "ansis";
|
|
@@ -3994,7 +4146,7 @@ function yargsCli(argv, cfg) {
|
|
|
3994
4146
|
const options2 = cfg.options ?? {};
|
|
3995
4147
|
const groups2 = cfg.groups ?? {};
|
|
3996
4148
|
const examples = cfg.examples ?? [];
|
|
3997
|
-
const cli2 =
|
|
4149
|
+
const cli2 = yargs2(argv);
|
|
3998
4150
|
cli2.updateLocale(yargsDecorator).wrap(Math.max(TERMINAL_WIDTH, cli2.terminalWidth())).help("help", descriptionStyle("Show help")).alias("h", "help").showHelpOnFail(false).version("version", dim2`Show version`, version2).check((args) => {
|
|
3999
4151
|
const persist = args["persist"];
|
|
4000
4152
|
return persist == null || validatePersistFormat(persist);
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-pushup/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A CLI to run all kinds of code quality measurements to align your team with company goals",
|
|
6
6
|
"bin": {
|
|
7
7
|
"code-pushup": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@code-pushup/models": "0.
|
|
11
|
-
"@code-pushup/core": "0.
|
|
12
|
-
"@code-pushup/utils": "0.
|
|
10
|
+
"@code-pushup/models": "0.52.0",
|
|
11
|
+
"@code-pushup/core": "0.52.0",
|
|
12
|
+
"@code-pushup/utils": "0.52.0",
|
|
13
13
|
"yargs": "^17.7.2",
|
|
14
14
|
"ansis": "^3.3.0",
|
|
15
15
|
"simple-git": "^3.20.0"
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"url": "git+https://github.com/code-pushup/cli.git",
|
|
24
24
|
"directory": "packages/cli"
|
|
25
25
|
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
26
29
|
"type": "module",
|
|
27
30
|
"main": "./index.js",
|
|
28
31
|
"types": "./src/index.d.ts"
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type CoreConfig, type Format } from '@code-pushup/models';
|
|
2
2
|
import type { CoreConfigCliOptions } from './core-config.model';
|
|
3
|
+
import type { FilterOptions } from './filter.model';
|
|
3
4
|
import type { GeneralCliOptions } from './global.model';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type CoreConfigMiddlewareOptions = GeneralCliOptions & CoreConfigCliOptions & OnlyPluginsOptions & SkipPluginsOptions;
|
|
7
|
-
export declare function coreConfigMiddleware<T extends CoreConfigMiddlewareOptions>(processArgs: T): Promise<GeneralCliOptions & CoreConfig & OnlyPluginsOptions & SkipPluginsOptions>;
|
|
5
|
+
export type CoreConfigMiddlewareOptions = GeneralCliOptions & CoreConfigCliOptions & FilterOptions;
|
|
6
|
+
export declare function coreConfigMiddleware<T extends CoreConfigMiddlewareOptions>(processArgs: T): Promise<GeneralCliOptions & CoreConfig & FilterOptions>;
|
|
8
7
|
export declare const normalizeFormats: (formats?: string[]) => Format[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GlobalOptions } from '@code-pushup/core';
|
|
2
|
+
import type { CategoryConfig, CoreConfig, PluginConfig } from '@code-pushup/models';
|
|
3
|
+
export type FilterOptions = Partial<GlobalOptions> & Pick<CoreConfig, 'categories' | 'plugins'> & FilterCliOptions;
|
|
4
|
+
export type FilterCliOptions = Partial<Record<FilterOptionType, string[]>>;
|
|
5
|
+
export type FilterOptionType = 'skipCategories' | 'onlyCategories' | 'skipPlugins' | 'onlyPlugins';
|
|
6
|
+
export type Filterables = {
|
|
7
|
+
categories: CategoryConfig[];
|
|
8
|
+
plugins: PluginConfig[];
|
|
9
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Options } from 'yargs';
|
|
2
|
+
import type { FilterCliOptions } from './filter.model';
|
|
3
|
+
export declare const skipCategoriesOption: Options;
|
|
4
|
+
export declare const onlyCategoriesOption: Options;
|
|
5
|
+
export declare const skipPluginsOption: Options;
|
|
6
|
+
export declare const onlyPluginsOption: Options;
|
|
7
|
+
export declare function yargsFilterOptionsDefinition(): Record<keyof FilterCliOptions, Options>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CategoryConfig, PluginConfig } from '@code-pushup/models';
|
|
2
|
+
import type { FilterOptionType, Filterables } from './filter.model';
|
|
3
|
+
export declare class OptionValidationError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export declare function validateFilterOption(option: FilterOptionType, { plugins, categories }: Filterables, { itemsToFilter, verbose }: {
|
|
6
|
+
itemsToFilter: string[];
|
|
7
|
+
verbose: boolean;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function validateFinalState(filteredItems: Filterables, originalItems: Filterables): void;
|
|
10
|
+
export declare function isOnlyOption(option: FilterOptionType): boolean;
|
|
11
|
+
export declare function getItemType(option: FilterOptionType, count: number): string;
|
|
12
|
+
export declare function createValidationMessage(option: FilterOptionType, invalidItems: string[], validItems: Pick<PluginConfig | CategoryConfig, 'slug'>[]): string;
|
|
13
|
+
export declare function handleConflictingOptions(type: 'categories' | 'plugins', onlyItems: string[], skipItems: string[]): void;
|
package/src/lib/options.d.ts
CHANGED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { GlobalOptions } from '@code-pushup/core';
|
|
2
|
-
import type { CoreConfig } from '@code-pushup/models';
|
|
3
|
-
export type OnlyPluginsCliOptions = {
|
|
4
|
-
onlyPlugins?: string[];
|
|
5
|
-
};
|
|
6
|
-
export type OnlyPluginsOptions = Partial<GlobalOptions> & Pick<CoreConfig, 'categories' | 'plugins'> & OnlyPluginsCliOptions;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { GlobalOptions } from '@code-pushup/core';
|
|
2
|
-
import type { CoreConfig } from '@code-pushup/models';
|
|
3
|
-
export type SkipPluginsCliOptions = {
|
|
4
|
-
skipPlugins?: string[];
|
|
5
|
-
};
|
|
6
|
-
export type SkipPluginsOptions = Partial<GlobalOptions> & Pick<CoreConfig, 'categories' | 'plugins'> & SkipPluginsCliOptions;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { CategoryConfig, PluginConfig } from '@code-pushup/models';
|
|
2
|
-
export declare function validatePluginFilterOption(filterOption: 'onlyPlugins' | 'skipPlugins', { plugins, categories, }: {
|
|
3
|
-
plugins: PluginConfig[];
|
|
4
|
-
categories: CategoryConfig[];
|
|
5
|
-
}, { pluginsToFilter, verbose, }?: {
|
|
6
|
-
pluginsToFilter?: string[];
|
|
7
|
-
verbose?: boolean;
|
|
8
|
-
}): void;
|