@code-pushup/cli 0.35.0 → 0.39.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 +22 -3
- package/index.js +300 -101
- package/package.json +5 -4
- package/src/lib/history/history-command.d.ts +7 -0
- package/src/lib/history/history.model.d.ts +5 -0
- package/src/lib/history/history.options.d.ts +3 -0
package/README.md
CHANGED
|
@@ -168,9 +168,9 @@ Each example is fully tested to demonstrate best practices for plugin testing as
|
|
|
168
168
|
|
|
169
169
|
**Example for custom plugins:**
|
|
170
170
|
|
|
171
|
-
- 📏 [File Size](../../examples/plugins/src/file-size)
|
|
172
|
-
- 📦 [Package Json](../../examples/plugins/src/package-json)
|
|
173
|
-
- 🔥 [Lighthouse](../../examples/plugins/src/lighthouse) (official implementation [here](../../../../packages/plugin-lighthouse))
|
|
171
|
+
- 📏 [File Size](../../examples/plugins/src/file-size) - example of basic runner executor
|
|
172
|
+
- 📦 [Package Json](../../examples/plugins/src/package-json) - example of audits and groups
|
|
173
|
+
- 🔥 [Lighthouse](../../examples/plugins/src/lighthouse) (official implementation [here](../../../../packages/plugin-lighthouse)) - example of a basic command executor
|
|
174
174
|
|
|
175
175
|
## CLI commands and options
|
|
176
176
|
|
|
@@ -240,6 +240,25 @@ Run plugins, collect results and upload the report to the Code PushUp portal.
|
|
|
240
240
|
|
|
241
241
|
Refer to the [Common Command Options](#common-command-options) for the list of available options.
|
|
242
242
|
|
|
243
|
+
#### `history` command
|
|
244
|
+
|
|
245
|
+
Usage:
|
|
246
|
+
`code-pushup history`
|
|
247
|
+
|
|
248
|
+
Description:
|
|
249
|
+
Run plugins, collect results and upload the report to the Code PushUp portal for a specified number of commits.
|
|
250
|
+
|
|
251
|
+
Refer to the [Common Command Options](#common-command-options) for the list of available options.
|
|
252
|
+
|
|
253
|
+
| Option | Type | Default | Description |
|
|
254
|
+
| ------------------------ | --------- | ------- | ---------------------------------------------------------------- |
|
|
255
|
+
| **`--targetBranch`** | `string` | 'main' | Branch to crawl history. |
|
|
256
|
+
| **`--forceCleanStatus`** | `boolean` | `false` | If we reset the status to a clean git history forcefully or not. |
|
|
257
|
+
| **`--maxCount`** | `number` | 5 | Number of commits. |
|
|
258
|
+
| **`--skipUploads`** | `boolean` | `false` | Upload created reports |
|
|
259
|
+
| **`--from`** | `string` | n/a | Hash to start in history |
|
|
260
|
+
| **`--to`** | `string` | n/a | Hash to end in history |
|
|
261
|
+
|
|
243
262
|
### `compare` command
|
|
244
263
|
|
|
245
264
|
Usage:
|
package/index.js
CHANGED
|
@@ -591,10 +591,14 @@ function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
|
591
591
|
{ description }
|
|
592
592
|
);
|
|
593
593
|
}
|
|
594
|
-
var scorableMetaSchema = z14.object({
|
|
594
|
+
var scorableMetaSchema = z14.object({
|
|
595
|
+
slug: slugSchema,
|
|
596
|
+
title: titleSchema,
|
|
597
|
+
docsUrl: docsUrlSchema
|
|
598
|
+
});
|
|
595
599
|
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
596
600
|
z14.object({
|
|
597
|
-
plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
|
|
601
|
+
plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
|
|
598
602
|
})
|
|
599
603
|
);
|
|
600
604
|
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
@@ -904,7 +908,7 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
904
908
|
await mkdir(baseDir, { recursive: true });
|
|
905
909
|
return;
|
|
906
910
|
} catch (error) {
|
|
907
|
-
ui().logger.
|
|
911
|
+
ui().logger.info(error.message);
|
|
908
912
|
if (error.code !== "EEXIST") {
|
|
909
913
|
throw error;
|
|
910
914
|
}
|
|
@@ -1316,11 +1320,9 @@ function toUnixPath(path) {
|
|
|
1316
1320
|
async function getLatestCommit(git = simpleGit()) {
|
|
1317
1321
|
const log2 = await git.log({
|
|
1318
1322
|
maxCount: 1,
|
|
1323
|
+
// git log -1 --pretty=format:"%H %s %an %aI" - See: https://git-scm.com/docs/pretty-formats
|
|
1319
1324
|
format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
|
|
1320
1325
|
});
|
|
1321
|
-
if (!log2.latest) {
|
|
1322
|
-
return null;
|
|
1323
|
-
}
|
|
1324
1326
|
return commitSchema.parse(log2.latest);
|
|
1325
1327
|
}
|
|
1326
1328
|
function getGitRoot(git = simpleGit()) {
|
|
@@ -1331,6 +1333,58 @@ function formatGitPath(path, gitRoot) {
|
|
|
1331
1333
|
const relativePath = relative(gitRoot, absolutePath);
|
|
1332
1334
|
return toUnixPath(relativePath);
|
|
1333
1335
|
}
|
|
1336
|
+
var GitStatusError = class _GitStatusError extends Error {
|
|
1337
|
+
static ignoredProps = /* @__PURE__ */ new Set(["current", "tracking"]);
|
|
1338
|
+
static getReducedStatus(status) {
|
|
1339
|
+
return Object.fromEntries(
|
|
1340
|
+
Object.entries(status).filter(([key]) => !this.ignoredProps.has(key)).filter(
|
|
1341
|
+
(entry) => {
|
|
1342
|
+
const value = entry[1];
|
|
1343
|
+
if (value == null) {
|
|
1344
|
+
return false;
|
|
1345
|
+
}
|
|
1346
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
if (typeof value === "number" && value === 0) {
|
|
1350
|
+
return false;
|
|
1351
|
+
}
|
|
1352
|
+
return !(typeof value === "boolean" && !value);
|
|
1353
|
+
}
|
|
1354
|
+
)
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
constructor(status) {
|
|
1358
|
+
super(
|
|
1359
|
+
`Working directory needs to be clean before we you can proceed. Commit your local changes or stash them:
|
|
1360
|
+
${JSON.stringify(
|
|
1361
|
+
_GitStatusError.getReducedStatus(status),
|
|
1362
|
+
null,
|
|
1363
|
+
2
|
|
1364
|
+
)}`
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
async function guardAgainstLocalChanges(git = simpleGit()) {
|
|
1369
|
+
const status = await git.status(["-s"]);
|
|
1370
|
+
if (status.files.length > 0) {
|
|
1371
|
+
throw new GitStatusError(status);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
async function getCurrentBranchOrTag(git = simpleGit()) {
|
|
1375
|
+
return await git.branch().then((r) => r.current) || // If no current branch, try to get the tag
|
|
1376
|
+
// @TODO use simple git
|
|
1377
|
+
await git.raw(["describe", "--tags", "--exact-match"]).then((out) => out.trim());
|
|
1378
|
+
}
|
|
1379
|
+
async function safeCheckout(branchOrHash, forceCleanStatus = false, git = simpleGit()) {
|
|
1380
|
+
if (forceCleanStatus) {
|
|
1381
|
+
await git.raw(["reset", "--hard"]);
|
|
1382
|
+
await git.clean(["f", "d"]);
|
|
1383
|
+
ui().logger.info(`git status cleaned`);
|
|
1384
|
+
}
|
|
1385
|
+
await guardAgainstLocalChanges(git);
|
|
1386
|
+
await git.checkout(branchOrHash);
|
|
1387
|
+
}
|
|
1334
1388
|
|
|
1335
1389
|
// packages/utils/src/lib/group-by-status.ts
|
|
1336
1390
|
function groupByStatus(results) {
|
|
@@ -1644,19 +1698,19 @@ function formatDiffCategoriesSection(diff) {
|
|
|
1644
1698
|
"\u{1F504} Score change"
|
|
1645
1699
|
],
|
|
1646
1700
|
...sortChanges(changed).map((category) => [
|
|
1647
|
-
category
|
|
1701
|
+
formatTitle(category),
|
|
1648
1702
|
formatScoreWithColor(category.scores.after),
|
|
1649
1703
|
formatScoreWithColor(category.scores.before, { skipBold: true }),
|
|
1650
1704
|
formatScoreChange(category.scores.diff)
|
|
1651
1705
|
]),
|
|
1652
1706
|
...added.map((category) => [
|
|
1653
|
-
category
|
|
1707
|
+
formatTitle(category),
|
|
1654
1708
|
formatScoreWithColor(category.score),
|
|
1655
1709
|
style("n/a (\\*)", ["i"]),
|
|
1656
1710
|
style("n/a (\\*)", ["i"])
|
|
1657
1711
|
]),
|
|
1658
1712
|
...unchanged.map((category) => [
|
|
1659
|
-
category
|
|
1713
|
+
formatTitle(category),
|
|
1660
1714
|
formatScoreWithColor(category.score),
|
|
1661
1715
|
formatScoreWithColor(category.score, { skipBold: true }),
|
|
1662
1716
|
"\u2013"
|
|
@@ -1682,8 +1736,8 @@ function formatDiffGroupsSection(diff) {
|
|
|
1682
1736
|
"\u{1F504} Score change"
|
|
1683
1737
|
],
|
|
1684
1738
|
rows: sortChanges(diff.groups.changed).map((group) => [
|
|
1685
|
-
group.plugin
|
|
1686
|
-
group
|
|
1739
|
+
formatTitle(group.plugin),
|
|
1740
|
+
formatTitle(group),
|
|
1687
1741
|
formatScoreWithColor(group.scores.after),
|
|
1688
1742
|
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1689
1743
|
formatScoreChange(group.scores.diff)
|
|
@@ -1704,8 +1758,8 @@ function formatDiffAuditsSection(diff) {
|
|
|
1704
1758
|
"\u{1F504} Value change"
|
|
1705
1759
|
],
|
|
1706
1760
|
rows: sortChanges(diff.audits.changed).map((audit) => [
|
|
1707
|
-
audit.plugin
|
|
1708
|
-
audit
|
|
1761
|
+
formatTitle(audit.plugin),
|
|
1762
|
+
formatTitle(audit),
|
|
1709
1763
|
`${getSquaredScoreMarker(audit.scores.after)} ${style(
|
|
1710
1764
|
audit.displayValues.after || audit.values.after.toString()
|
|
1711
1765
|
)}`,
|
|
@@ -1773,6 +1827,15 @@ function summarizeDiffOutcomes(outcomes, token) {
|
|
|
1773
1827
|
}
|
|
1774
1828
|
}).join(", ");
|
|
1775
1829
|
}
|
|
1830
|
+
function formatTitle({
|
|
1831
|
+
title,
|
|
1832
|
+
docsUrl
|
|
1833
|
+
}) {
|
|
1834
|
+
if (docsUrl) {
|
|
1835
|
+
return link2(docsUrl, title);
|
|
1836
|
+
}
|
|
1837
|
+
return title;
|
|
1838
|
+
}
|
|
1776
1839
|
function sortChanges(changes) {
|
|
1777
1840
|
return [...changes].sort(
|
|
1778
1841
|
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
@@ -2061,7 +2124,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2061
2124
|
|
|
2062
2125
|
// packages/core/package.json
|
|
2063
2126
|
var name = "@code-pushup/core";
|
|
2064
|
-
var version = "0.
|
|
2127
|
+
var version = "0.39.0";
|
|
2065
2128
|
|
|
2066
2129
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2067
2130
|
import chalk5 from "chalk";
|
|
@@ -2344,8 +2407,7 @@ function compareAudits2(reports) {
|
|
|
2344
2407
|
}
|
|
2345
2408
|
function categoryToResult(category) {
|
|
2346
2409
|
return {
|
|
2347
|
-
|
|
2348
|
-
title: category.title,
|
|
2410
|
+
...selectMeta(category),
|
|
2349
2411
|
score: category.score
|
|
2350
2412
|
};
|
|
2351
2413
|
}
|
|
@@ -2354,8 +2416,7 @@ function categoryPairToDiff({
|
|
|
2354
2416
|
after
|
|
2355
2417
|
}) {
|
|
2356
2418
|
return {
|
|
2357
|
-
|
|
2358
|
-
title: after.title,
|
|
2419
|
+
...selectMeta(after),
|
|
2359
2420
|
scores: {
|
|
2360
2421
|
before: before.score,
|
|
2361
2422
|
after: after.score,
|
|
@@ -2365,12 +2426,8 @@ function categoryPairToDiff({
|
|
|
2365
2426
|
}
|
|
2366
2427
|
function pluginGroupToResult({ group, plugin }) {
|
|
2367
2428
|
return {
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
plugin: {
|
|
2371
|
-
slug: plugin.slug,
|
|
2372
|
-
title: plugin.title
|
|
2373
|
-
},
|
|
2429
|
+
...selectMeta(group),
|
|
2430
|
+
plugin: selectMeta(plugin),
|
|
2374
2431
|
score: group.score
|
|
2375
2432
|
};
|
|
2376
2433
|
}
|
|
@@ -2379,12 +2436,8 @@ function pluginGroupPairToDiff({
|
|
|
2379
2436
|
after
|
|
2380
2437
|
}) {
|
|
2381
2438
|
return {
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
plugin: {
|
|
2385
|
-
slug: after.plugin.slug,
|
|
2386
|
-
title: after.plugin.title
|
|
2387
|
-
},
|
|
2439
|
+
...selectMeta(after.group),
|
|
2440
|
+
plugin: selectMeta(after.plugin),
|
|
2388
2441
|
scores: {
|
|
2389
2442
|
before: before.group.score,
|
|
2390
2443
|
after: after.group.score,
|
|
@@ -2394,12 +2447,8 @@ function pluginGroupPairToDiff({
|
|
|
2394
2447
|
}
|
|
2395
2448
|
function pluginAuditToResult({ audit, plugin }) {
|
|
2396
2449
|
return {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
plugin: {
|
|
2400
|
-
slug: plugin.slug,
|
|
2401
|
-
title: plugin.title
|
|
2402
|
-
},
|
|
2450
|
+
...selectMeta(audit),
|
|
2451
|
+
plugin: selectMeta(plugin),
|
|
2403
2452
|
score: audit.score,
|
|
2404
2453
|
value: audit.value,
|
|
2405
2454
|
displayValue: audit.displayValue
|
|
@@ -2410,12 +2459,8 @@ function pluginAuditPairToDiff({
|
|
|
2410
2459
|
after
|
|
2411
2460
|
}) {
|
|
2412
2461
|
return {
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
plugin: {
|
|
2416
|
-
slug: after.plugin.slug,
|
|
2417
|
-
title: after.plugin.title
|
|
2418
|
-
},
|
|
2462
|
+
...selectMeta(after.audit),
|
|
2463
|
+
plugin: selectMeta(after.plugin),
|
|
2419
2464
|
scores: {
|
|
2420
2465
|
before: before.audit.score,
|
|
2421
2466
|
after: after.audit.score,
|
|
@@ -2432,6 +2477,15 @@ function pluginAuditPairToDiff({
|
|
|
2432
2477
|
}
|
|
2433
2478
|
};
|
|
2434
2479
|
}
|
|
2480
|
+
function selectMeta(meta) {
|
|
2481
|
+
return {
|
|
2482
|
+
slug: meta.slug,
|
|
2483
|
+
title: meta.title,
|
|
2484
|
+
...meta.docsUrl && {
|
|
2485
|
+
docsUrl: meta.docsUrl
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2435
2489
|
|
|
2436
2490
|
// packages/core/src/lib/compare.ts
|
|
2437
2491
|
async function compareReportFiles(inputPaths, persistConfig) {
|
|
@@ -2487,45 +2541,8 @@ function reportsDiffToFileContent(reportsDiff, format) {
|
|
|
2487
2541
|
}
|
|
2488
2542
|
}
|
|
2489
2543
|
|
|
2490
|
-
// packages/core/src/lib/
|
|
2491
|
-
import {
|
|
2492
|
-
var ConfigPathError = class extends Error {
|
|
2493
|
-
constructor(configPath) {
|
|
2494
|
-
super(`Provided path '${configPath}' is not valid.`);
|
|
2495
|
-
}
|
|
2496
|
-
};
|
|
2497
|
-
async function readRcByPath(filepath, tsconfig) {
|
|
2498
|
-
if (filepath.length === 0) {
|
|
2499
|
-
throw new Error("The path to the configuration file is empty.");
|
|
2500
|
-
}
|
|
2501
|
-
if (!await fileExists(filepath)) {
|
|
2502
|
-
throw new ConfigPathError(filepath);
|
|
2503
|
-
}
|
|
2504
|
-
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
2505
|
-
return coreConfigSchema.parse(cfg);
|
|
2506
|
-
}
|
|
2507
|
-
async function autoloadRc(tsconfig) {
|
|
2508
|
-
let ext = "";
|
|
2509
|
-
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
2510
|
-
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
2511
|
-
const exists2 = await fileExists(path);
|
|
2512
|
-
if (exists2) {
|
|
2513
|
-
ext = extension;
|
|
2514
|
-
break;
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
if (!ext) {
|
|
2518
|
-
throw new Error(
|
|
2519
|
-
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
2520
|
-
"|"
|
|
2521
|
-
)}) present in ${process.cwd()}`
|
|
2522
|
-
);
|
|
2523
|
-
}
|
|
2524
|
-
return readRcByPath(
|
|
2525
|
-
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2526
|
-
tsconfig
|
|
2527
|
-
);
|
|
2528
|
-
}
|
|
2544
|
+
// packages/core/src/lib/history.ts
|
|
2545
|
+
import { simpleGit as simpleGit2 } from "simple-git";
|
|
2529
2546
|
|
|
2530
2547
|
// packages/core/src/lib/upload.ts
|
|
2531
2548
|
import {
|
|
@@ -2606,6 +2623,7 @@ function categoryToGQL(category) {
|
|
|
2606
2623
|
slug: category.slug,
|
|
2607
2624
|
title: category.title,
|
|
2608
2625
|
description: category.description,
|
|
2626
|
+
isBinary: category.isBinary,
|
|
2609
2627
|
refs: category.refs.map((ref) => ({
|
|
2610
2628
|
plugin: ref.plugin,
|
|
2611
2629
|
type: categoryRefTypeToGQL(ref.type),
|
|
@@ -2655,6 +2673,98 @@ async function upload(options2, uploadFn = uploadToPortal) {
|
|
|
2655
2673
|
return uploadFn({ apiKey, server, data, timeout });
|
|
2656
2674
|
}
|
|
2657
2675
|
|
|
2676
|
+
// packages/core/src/lib/history.ts
|
|
2677
|
+
async function history(config, commits) {
|
|
2678
|
+
const initialBranch = await getCurrentBranchOrTag();
|
|
2679
|
+
const { skipUploads = false, forceCleanStatus, persist } = config;
|
|
2680
|
+
const reports = [];
|
|
2681
|
+
for (const commit of commits) {
|
|
2682
|
+
ui().logger.info(`Collect ${commit}`);
|
|
2683
|
+
await safeCheckout(commit, forceCleanStatus);
|
|
2684
|
+
const currentConfig = {
|
|
2685
|
+
...config,
|
|
2686
|
+
persist: {
|
|
2687
|
+
...persist,
|
|
2688
|
+
format: ["json"],
|
|
2689
|
+
filename: `${commit}-report`
|
|
2690
|
+
}
|
|
2691
|
+
};
|
|
2692
|
+
await collectAndPersistReports(currentConfig);
|
|
2693
|
+
if (skipUploads) {
|
|
2694
|
+
ui().logger.info("Upload is skipped because skipUploads is set to true.");
|
|
2695
|
+
} else {
|
|
2696
|
+
if (currentConfig.upload) {
|
|
2697
|
+
await upload(currentConfig);
|
|
2698
|
+
} else {
|
|
2699
|
+
ui().logger.info(
|
|
2700
|
+
"Upload is skipped because upload config is undefined."
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
reports.push(currentConfig.persist.filename);
|
|
2705
|
+
}
|
|
2706
|
+
await safeCheckout(initialBranch, forceCleanStatus);
|
|
2707
|
+
return reports;
|
|
2708
|
+
}
|
|
2709
|
+
async function getHashes(options2, git = simpleGit2()) {
|
|
2710
|
+
const { from, to } = options2;
|
|
2711
|
+
if (to && !from) {
|
|
2712
|
+
throw new Error(
|
|
2713
|
+
`git log command needs the "from" option defined to accept the "to" option.
|
|
2714
|
+
`
|
|
2715
|
+
);
|
|
2716
|
+
}
|
|
2717
|
+
const logs = await git.log({
|
|
2718
|
+
...options2,
|
|
2719
|
+
from,
|
|
2720
|
+
to
|
|
2721
|
+
});
|
|
2722
|
+
return prepareHashes(logs);
|
|
2723
|
+
}
|
|
2724
|
+
function prepareHashes(logs) {
|
|
2725
|
+
return logs.all.map(({ hash }) => hash).reverse();
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2729
|
+
import { join as join6 } from "node:path";
|
|
2730
|
+
var ConfigPathError = class extends Error {
|
|
2731
|
+
constructor(configPath) {
|
|
2732
|
+
super(`Provided path '${configPath}' is not valid.`);
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
async function readRcByPath(filepath, tsconfig) {
|
|
2736
|
+
if (filepath.length === 0) {
|
|
2737
|
+
throw new Error("The path to the configuration file is empty.");
|
|
2738
|
+
}
|
|
2739
|
+
if (!await fileExists(filepath)) {
|
|
2740
|
+
throw new ConfigPathError(filepath);
|
|
2741
|
+
}
|
|
2742
|
+
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
2743
|
+
return coreConfigSchema.parse(cfg);
|
|
2744
|
+
}
|
|
2745
|
+
async function autoloadRc(tsconfig) {
|
|
2746
|
+
let ext = "";
|
|
2747
|
+
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
2748
|
+
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
2749
|
+
const exists2 = await fileExists(path);
|
|
2750
|
+
if (exists2) {
|
|
2751
|
+
ext = extension;
|
|
2752
|
+
break;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
if (!ext) {
|
|
2756
|
+
throw new Error(
|
|
2757
|
+
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
2758
|
+
"|"
|
|
2759
|
+
)}) present in ${process.cwd()}`
|
|
2760
|
+
);
|
|
2761
|
+
}
|
|
2762
|
+
return readRcByPath(
|
|
2763
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2764
|
+
tsconfig
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2658
2768
|
// packages/cli/src/lib/constants.ts
|
|
2659
2769
|
var CLI_NAME = "Code PushUp CLI";
|
|
2660
2770
|
var CLI_SCRIPT_NAME = "code-pushup";
|
|
@@ -2814,6 +2924,9 @@ function yargsCompareCommandObject() {
|
|
|
2814
2924
|
};
|
|
2815
2925
|
}
|
|
2816
2926
|
|
|
2927
|
+
// packages/cli/src/lib/history/history-command.ts
|
|
2928
|
+
import chalk10 from "chalk";
|
|
2929
|
+
|
|
2817
2930
|
// packages/cli/src/lib/implementation/global.utils.ts
|
|
2818
2931
|
function filterKebabCaseKeys(obj) {
|
|
2819
2932
|
return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
|
|
@@ -2839,6 +2952,104 @@ function coerceArray(param) {
|
|
|
2839
2952
|
return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
|
|
2840
2953
|
}
|
|
2841
2954
|
|
|
2955
|
+
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
2956
|
+
var onlyPluginsOption = {
|
|
2957
|
+
describe: "List of plugins to run. If not set all plugins are run.",
|
|
2958
|
+
type: "array",
|
|
2959
|
+
default: [],
|
|
2960
|
+
coerce: coerceArray
|
|
2961
|
+
};
|
|
2962
|
+
function yargsOnlyPluginsOptionsDefinition() {
|
|
2963
|
+
return {
|
|
2964
|
+
onlyPlugins: onlyPluginsOption
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
// packages/cli/src/lib/history/history.options.ts
|
|
2969
|
+
function yargsHistoryOptionsDefinition() {
|
|
2970
|
+
return {
|
|
2971
|
+
targetBranch: {
|
|
2972
|
+
describe: "Branch to crawl history",
|
|
2973
|
+
type: "string",
|
|
2974
|
+
default: "main"
|
|
2975
|
+
},
|
|
2976
|
+
forceCleanStatus: {
|
|
2977
|
+
describe: "If we reset the status to a clean git history forcefully or not.",
|
|
2978
|
+
type: "boolean",
|
|
2979
|
+
default: false
|
|
2980
|
+
},
|
|
2981
|
+
skipUploads: {
|
|
2982
|
+
describe: "Upload created reports",
|
|
2983
|
+
type: "boolean",
|
|
2984
|
+
default: false
|
|
2985
|
+
},
|
|
2986
|
+
maxCount: {
|
|
2987
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---max-countltnumbergt
|
|
2988
|
+
describe: "Number of steps in history",
|
|
2989
|
+
type: "number",
|
|
2990
|
+
// eslint-disable-next-line no-magic-numbers
|
|
2991
|
+
default: 5
|
|
2992
|
+
},
|
|
2993
|
+
from: {
|
|
2994
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
|
|
2995
|
+
describe: "hash to first commit in history",
|
|
2996
|
+
type: "string"
|
|
2997
|
+
},
|
|
2998
|
+
to: {
|
|
2999
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
|
|
3000
|
+
describe: "hash to last commit in history",
|
|
3001
|
+
type: "string"
|
|
3002
|
+
}
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
// packages/cli/src/lib/history/history-command.ts
|
|
3007
|
+
function yargsHistoryCommandObject() {
|
|
3008
|
+
const command = "history";
|
|
3009
|
+
return {
|
|
3010
|
+
command,
|
|
3011
|
+
describe: "Collect reports for commit history",
|
|
3012
|
+
builder: (yargs2) => {
|
|
3013
|
+
yargs2.options({
|
|
3014
|
+
...yargsHistoryOptionsDefinition(),
|
|
3015
|
+
...yargsOnlyPluginsOptionsDefinition()
|
|
3016
|
+
});
|
|
3017
|
+
yargs2.group(
|
|
3018
|
+
Object.keys(yargsHistoryOptionsDefinition()),
|
|
3019
|
+
"History Options:"
|
|
3020
|
+
);
|
|
3021
|
+
return yargs2;
|
|
3022
|
+
},
|
|
3023
|
+
handler: async (args) => {
|
|
3024
|
+
ui().logger.info(chalk10.bold(CLI_NAME));
|
|
3025
|
+
ui().logger.info(chalk10.gray(`Run ${command}`));
|
|
3026
|
+
const currentBranch = await getCurrentBranchOrTag();
|
|
3027
|
+
const {
|
|
3028
|
+
targetBranch = currentBranch,
|
|
3029
|
+
forceCleanStatus,
|
|
3030
|
+
maxCount,
|
|
3031
|
+
from,
|
|
3032
|
+
to,
|
|
3033
|
+
...restOptions
|
|
3034
|
+
} = args;
|
|
3035
|
+
const commits = await getHashes({ maxCount, from, to });
|
|
3036
|
+
try {
|
|
3037
|
+
const reports = await history(
|
|
3038
|
+
{
|
|
3039
|
+
...restOptions,
|
|
3040
|
+
targetBranch,
|
|
3041
|
+
forceCleanStatus
|
|
3042
|
+
},
|
|
3043
|
+
commits
|
|
3044
|
+
);
|
|
3045
|
+
ui().logger.log(`Reports: ${reports.length}`);
|
|
3046
|
+
} finally {
|
|
3047
|
+
await safeCheckout(currentBranch);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
|
|
2842
3053
|
// packages/cli/src/lib/print-config/print-config-command.ts
|
|
2843
3054
|
function yargsConfigCommandObject() {
|
|
2844
3055
|
const command = "print-config";
|
|
@@ -2854,15 +3065,15 @@ function yargsConfigCommandObject() {
|
|
|
2854
3065
|
}
|
|
2855
3066
|
|
|
2856
3067
|
// packages/cli/src/lib/upload/upload-command.ts
|
|
2857
|
-
import
|
|
3068
|
+
import chalk11 from "chalk";
|
|
2858
3069
|
function yargsUploadCommandObject() {
|
|
2859
3070
|
const command = "upload";
|
|
2860
3071
|
return {
|
|
2861
3072
|
command,
|
|
2862
3073
|
describe: "Upload report results to the portal",
|
|
2863
3074
|
handler: async (args) => {
|
|
2864
|
-
ui().logger.log(
|
|
2865
|
-
ui().logger.info(
|
|
3075
|
+
ui().logger.log(chalk11.bold(CLI_NAME));
|
|
3076
|
+
ui().logger.info(chalk11.gray(`Run ${command}...`));
|
|
2866
3077
|
const options2 = args;
|
|
2867
3078
|
if (options2.upload == null) {
|
|
2868
3079
|
renderIntegratePortalHint();
|
|
@@ -2883,6 +3094,7 @@ var commands = [
|
|
|
2883
3094
|
yargsAutorunCommandObject(),
|
|
2884
3095
|
yargsCollectCommandObject(),
|
|
2885
3096
|
yargsUploadCommandObject(),
|
|
3097
|
+
yargsHistoryCommandObject(),
|
|
2886
3098
|
yargsCompareCommandObject(),
|
|
2887
3099
|
yargsConfigCommandObject()
|
|
2888
3100
|
];
|
|
@@ -2922,7 +3134,7 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2922
3134
|
}
|
|
2923
3135
|
|
|
2924
3136
|
// packages/cli/src/lib/implementation/only-plugins.utils.ts
|
|
2925
|
-
import
|
|
3137
|
+
import chalk12 from "chalk";
|
|
2926
3138
|
function validateOnlyPluginsOption({
|
|
2927
3139
|
plugins,
|
|
2928
3140
|
categories
|
|
@@ -2936,7 +3148,7 @@ function validateOnlyPluginsOption({
|
|
|
2936
3148
|
);
|
|
2937
3149
|
if (missingPlugins.length > 0 && verbose) {
|
|
2938
3150
|
ui().logger.info(
|
|
2939
|
-
`${
|
|
3151
|
+
`${chalk12.yellow(
|
|
2940
3152
|
"\u26A0"
|
|
2941
3153
|
)} The --onlyPlugin argument references plugins with "${missingPlugins.join(
|
|
2942
3154
|
'", "'
|
|
@@ -3063,19 +3275,6 @@ function yargsGlobalOptionsDefinition() {
|
|
|
3063
3275
|
};
|
|
3064
3276
|
}
|
|
3065
3277
|
|
|
3066
|
-
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
3067
|
-
var onlyPluginsOption = {
|
|
3068
|
-
describe: "List of plugins to run. If not set all plugins are run.",
|
|
3069
|
-
type: "array",
|
|
3070
|
-
default: [],
|
|
3071
|
-
coerce: coerceArray
|
|
3072
|
-
};
|
|
3073
|
-
function yargsOnlyPluginsOptionsDefinition() {
|
|
3074
|
-
return {
|
|
3075
|
-
onlyPlugins: onlyPluginsOption
|
|
3076
|
-
};
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
3278
|
// packages/cli/src/lib/options.ts
|
|
3080
3279
|
var options = {
|
|
3081
3280
|
...yargsGlobalOptionsDefinition(),
|
|
@@ -3092,7 +3291,7 @@ var groups = {
|
|
|
3092
3291
|
};
|
|
3093
3292
|
|
|
3094
3293
|
// packages/cli/src/lib/yargs-cli.ts
|
|
3095
|
-
import
|
|
3294
|
+
import chalk13 from "chalk";
|
|
3096
3295
|
import yargs from "yargs";
|
|
3097
3296
|
function yargsCli(argv, cfg) {
|
|
3098
3297
|
const { usageMessage, scriptName, noExitProcess } = cfg;
|
|
@@ -3112,7 +3311,7 @@ function yargsCli(argv, cfg) {
|
|
|
3112
3311
|
(config) => Array.isArray(config) ? config.at(-1) : config
|
|
3113
3312
|
).options(options2).wrap(TERMINAL_WIDTH);
|
|
3114
3313
|
if (usageMessage) {
|
|
3115
|
-
cli2.usage(
|
|
3314
|
+
cli2.usage(chalk13.bold(usageMessage));
|
|
3116
3315
|
}
|
|
3117
3316
|
if (scriptName) {
|
|
3118
3317
|
cli2.scriptName(scriptName);
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-pushup/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"code-pushup": "index.js"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@code-pushup/models": "0.
|
|
10
|
-
"@code-pushup/core": "0.
|
|
9
|
+
"@code-pushup/models": "0.39.0",
|
|
10
|
+
"@code-pushup/core": "0.39.0",
|
|
11
|
+
"@code-pushup/utils": "0.39.0",
|
|
11
12
|
"yargs": "^17.7.2",
|
|
12
13
|
"chalk": "^5.3.0",
|
|
13
|
-
"
|
|
14
|
+
"simple-git": "^3.20.0"
|
|
14
15
|
},
|
|
15
16
|
"homepage": "https://github.com/code-pushup/cli#readme",
|
|
16
17
|
"bugs": {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ArgumentsCamelCase } from 'yargs';
|
|
2
|
+
export declare function yargsHistoryCommandObject(): {
|
|
3
|
+
command: string;
|
|
4
|
+
describe: string;
|
|
5
|
+
builder: (yargs: import("yargs").Argv<{}>) => import("yargs").Argv<{}>;
|
|
6
|
+
handler: <T>(args: ArgumentsCamelCase<T>) => Promise<void>;
|
|
7
|
+
};
|