@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 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({ slug: slugSchema, title: titleSchema });
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.error(error.message);
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.title,
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.title,
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.title,
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.title,
1686
- group.title,
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.title,
1708
- audit.title,
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.35.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
- slug: category.slug,
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
- slug: after.slug,
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
- slug: group.slug,
2369
- title: group.title,
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
- slug: after.group.slug,
2383
- title: after.group.title,
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
- slug: audit.slug,
2398
- title: audit.title,
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
- slug: after.audit.slug,
2414
- title: after.audit.title,
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/implementation/read-rc-file.ts
2491
- import { join as join6 } from "node:path";
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 chalk10 from "chalk";
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(chalk10.bold(CLI_NAME));
2865
- ui().logger.info(chalk10.gray(`Run ${command}...`));
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 chalk11 from "chalk";
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
- `${chalk11.yellow(
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 chalk12 from "chalk";
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(chalk12.bold(usageMessage));
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.35.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.35.0",
10
- "@code-pushup/core": "0.35.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
- "@code-pushup/utils": "0.35.0"
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
+ };
@@ -0,0 +1,5 @@
1
+ import { type LogOptions } from 'simple-git';
2
+ import { HistoryOnlyOptions } from '@code-pushup/core';
3
+ export type HistoryCliOptions = {
4
+ targetBranch?: string;
5
+ } & Pick<LogOptions, 'maxCount' | 'from' | 'to'> & HistoryOnlyOptions;
@@ -0,0 +1,3 @@
1
+ import { Options } from 'yargs';
2
+ import { HistoryCliOptions } from './history.model';
3
+ export declare function yargsHistoryOptionsDefinition(): Record<keyof HistoryCliOptions, Options>;