@code-pushup/ci 0.53.1 → 0.55.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
@@ -14,10 +14,10 @@
14
14
 
15
15
  This package exports **provider-agnostic core logic for running Code PushUp in CI pipelines**. It serves as the base for the following provider integrations:
16
16
 
17
- | | |
18
- | :------------- | :-------------------------------------------------------------------------------- |
19
- | GitHub Actions | [`code-pushup/github-action`](https://github.com/marketplace/actions/code-pushup) |
20
- | GitLab CI/CD | _coming soon_ |
17
+ | | |
18
+ | :------------- | :-------------------------------------------------------------------------------------------------- |
19
+ | GitHub Actions | [`code-pushup/github-action`](https://github.com/marketplace/actions/code-pushup) |
20
+ | GitLab CI/CD | [`code-pushup/gitlab-pipelines-template`](https://gitlab.com/code-pushup/gitlab-pipelines-template) |
21
21
 
22
22
  ## Setup
23
23
 
@@ -74,13 +74,13 @@ This will additionally compare reports from both source and target branches and
74
74
  The PR flow requires interacting with the Git provider's API to post a comparison comment.
75
75
  Wrap these requests in functions and pass them in as an object which configures the provider.
76
76
 
77
- | Property | Required | Type | Description |
78
- | :----------------------- | :------: | :----------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- |
79
- | `createComment` | yes | `(body: string) => Promise<Comment>` | Posts a new comment to PR |
80
- | `updateComment` | yes | `(id: number, body: string) => Promise<Comment>` | Updates existing PR comment |
81
- | `listComments` | yes | `() => Promise<Comment[]>` | Fetches all comments from PR |
82
- | `maxCommentChars` | yes | `number` | Character limit for comment body |
83
- | `downloadReportArtifact` | no | `() => Promise<string \| null>` | Fetches previous report for base branch (returns path to downloaded `report.json`), used as cache to speed up comparison |
77
+ | Property | Required | Type | Description |
78
+ | :----------------------- | :------: | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- |
79
+ | `createComment` | yes | `(body: string) => Promise<Comment>` | Posts a new comment to PR |
80
+ | `updateComment` | yes | `(id: number, body: string) => Promise<Comment>` | Updates existing PR comment |
81
+ | `listComments` | yes | `() => Promise<Comment[]>` | Fetches all comments from PR |
82
+ | `maxCommentChars` | yes | `number` | Character limit for comment body |
83
+ | `downloadReportArtifact` | no | `(project?: string) => Promise<string \| null>` | Fetches previous (root/project) `report.json` for base branch and returns path, used as cache to speed up comparison |
84
84
 
85
85
  A `Comment` object has the following required properties:
86
86
 
@@ -94,20 +94,23 @@ A `Comment` object has the following required properties:
94
94
 
95
95
  Optionally, you can override default options for further customization:
96
96
 
97
- | Property | Type | Default | Description |
98
- | :---------------- | :------------------------ | :------------------------------- | :-------------------------------------------------------------------------------- |
99
- | `monorepo` | `boolean \| MonorepoTool` | `false` | Enables [monorepo mode](#monorepo-mode) |
100
- | `projects` | `string[] \| null` | `null` | Custom projects configuration for [monorepo mode](#monorepo-mode) |
101
- | `task` | `string` | `'code-pushup'` | Name of command to run Code PushUp per project in [monorepo mode](#monorepo-mode) |
102
- | `directory` | `string` | `process.cwd()` | Directory in which Code PushUp CLI should run |
103
- | `config` | `string \| null` | `null` [^1] | Path to config file (`--config` option) |
104
- | `silent` | `boolean` | `false` | Toggles if logs from CLI commands are printed |
105
- | `bin` | `string` | `'npx --no-install code-pushup'` | Command for executing Code PushUp CLI |
106
- | `detectNewIssues` | `boolean` | `true` | Toggles if new issues should be detected and returned in `newIssues` property |
107
- | `logger` | `Logger` | `console` | Logger for reporting progress and encountered problems |
97
+ | Property | Type | Default | Description |
98
+ | :---------------- | :------------------------ | :------------------------------- | :----------------------------------------------------------------------------------- |
99
+ | `monorepo` | `boolean \| MonorepoTool` | `false` | Enables [monorepo mode](#monorepo-mode) |
100
+ | `projects` | `string[] \| null` | `null` | Custom projects configuration for [monorepo mode](#monorepo-mode) |
101
+ | `task` | `string` | `'code-pushup'` | Name of command to run Code PushUp per project in [monorepo mode](#monorepo-mode) |
102
+ | `directory` | `string` | `process.cwd()` | Directory in which Code PushUp CLI should run |
103
+ | `config` | `string \| null` | `null` [^1] | Path to config file (`--config` option) |
104
+ | `silent` | `boolean` | `false` | Toggles if logs from CLI commands are printed |
105
+ | `bin` | `string` | `'npx --no-install code-pushup'` | Command for executing Code PushUp CLI |
106
+ | `detectNewIssues` | `boolean` | `true` | Toggles if new issues should be detected and returned in `newIssues` property |
107
+ | `logger` | `Logger` | `console` | Logger for reporting progress and encountered problems |
108
+ | `output` | `string` | `'.code-pushup'` | Directory where Code PushUp reports will be created (interpolates project name [^2]) |
108
109
 
109
110
  [^1]: By default, the `code-pushup.config` file is autodetected as described in [`@code-pushup/cli` docs](../cli/README.md#configuration).
110
111
 
112
+ [^2]: In monorepo mode, any occurrence of `{project}` in the `output` path will be replaced with a project name. This separation of folders per project (e.g. `output: '.code-pushup/{project}'`) may be useful for caching purposes.
113
+
111
114
  The `Logger` object has the following required properties:
112
115
 
113
116
  | Property | Type | Description |
package/index.js CHANGED
@@ -36,7 +36,7 @@ function exists(value) {
36
36
  return value != null;
37
37
  }
38
38
  function getMissingRefsForCategories(categories, plugins) {
39
- if (categories.length === 0) {
39
+ if (!categories || categories.length === 0) {
40
40
  return false;
41
41
  }
42
42
  const auditRefsFromCategory = categories.flatMap(
@@ -113,7 +113,6 @@ var fileNameSchema = z.string().trim().regex(filenameRegex, {
113
113
  message: `The filename has to be valid`
114
114
  }).min(1, { message: "file name is invalid" });
115
115
  var positiveIntSchema = z.number().int().positive();
116
- var nonnegativeIntSchema = z.number().int().nonnegative();
117
116
  var nonnegativeNumberSchema = z.number().nonnegative();
118
117
  function packageVersionSchema(options) {
119
118
  const { versionDescription = "NPM version of the package", required } = options ?? {};
@@ -536,12 +535,9 @@ var unrefinedCoreConfigSchema = z14.object({
536
535
  var coreConfigSchema = refineCoreConfig(unrefinedCoreConfigSchema);
537
536
  function refineCoreConfig(schema) {
538
537
  return schema.refine(
539
- (coreCfg) => !getMissingRefsForCategories(coreCfg.categories ?? [], coreCfg.plugins),
540
- (coreCfg) => ({
541
- message: missingRefsForCategoriesErrorMsg(
542
- coreCfg.categories ?? [],
543
- coreCfg.plugins
544
- )
538
+ ({ categories, plugins }) => !getMissingRefsForCategories(categories, plugins),
539
+ ({ categories, plugins }) => ({
540
+ message: missingRefsForCategoriesErrorMsg(categories, plugins)
545
541
  })
546
542
  );
547
543
  }
@@ -598,19 +594,16 @@ var reportSchema = packageVersionSchema({
598
594
  ).merge(
599
595
  z15.object(
600
596
  {
601
- categories: z15.array(categoryConfigSchema),
602
597
  plugins: z15.array(pluginReportSchema).min(1),
598
+ categories: z15.array(categoryConfigSchema).optional(),
603
599
  commit: commitSchema.describe("Git commit for which report was collected").nullable()
604
600
  },
605
601
  { description: "Collect output data" }
606
602
  )
607
603
  ).refine(
608
- (report) => !getMissingRefsForCategories(report.categories, report.plugins),
609
- (report) => ({
610
- message: missingRefsForCategoriesErrorMsg(
611
- report.categories,
612
- report.plugins
613
- )
604
+ ({ categories, plugins }) => !getMissingRefsForCategories(categories, plugins),
605
+ ({ categories, plugins }) => ({
606
+ message: missingRefsForCategoriesErrorMsg(categories, plugins)
614
607
  })
615
608
  );
616
609
 
@@ -662,7 +655,7 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
662
655
  z16.object({
663
656
  values: makeComparisonSchema(auditValueSchema).merge(
664
657
  z16.object({
665
- diff: z16.number().int().describe("Value change (`values.after - values.before`)")
658
+ diff: z16.number().describe("Value change (`values.after - values.before`)")
666
659
  })
667
660
  ).describe("Audit `value` comparison"),
668
661
  displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
@@ -756,6 +749,7 @@ function executeProcess(cfg) {
756
749
  return new Promise((resolve, reject) => {
757
750
  const spawnedProcess = spawn(command, args ?? [], {
758
751
  shell: true,
752
+ windowsHide: true,
759
753
  ...options
760
754
  });
761
755
  let stdout = "";
@@ -1171,10 +1165,11 @@ import { simpleGit as simpleGit4 } from "simple-git";
1171
1165
  import path from "node:path";
1172
1166
  function persistCliOptions({
1173
1167
  directory,
1174
- project
1168
+ project,
1169
+ output
1175
1170
  }) {
1176
1171
  return [
1177
- `--persist.outputDir=${path.join(directory, DEFAULT_PERSIST_OUTPUT_DIR)}`,
1172
+ `--persist.outputDir=${path.join(directory, output)}`,
1178
1173
  `--persist.filename=${createFilename(project)}`,
1179
1174
  ...DEFAULT_PERSIST_FORMAT.map((format) => `--persist.format=${format}`)
1180
1175
  ];
@@ -1183,9 +1178,10 @@ function persistedCliFiles({
1183
1178
  directory,
1184
1179
  isDiff,
1185
1180
  project,
1186
- formats
1181
+ formats,
1182
+ output
1187
1183
  }) {
1188
- const rootDir = path.join(directory, DEFAULT_PERSIST_OUTPUT_DIR);
1184
+ const rootDir = path.join(directory, output);
1189
1185
  const filename = isDiff ? `${createFilename(project)}-diff` : createFilename(project);
1190
1186
  const filePaths = (formats ?? DEFAULT_PERSIST_FORMAT).reduce(
1191
1187
  (acc, format) => ({
@@ -1218,24 +1214,25 @@ async function runCollect({
1218
1214
  config,
1219
1215
  directory,
1220
1216
  silent,
1221
- project
1217
+ project,
1218
+ output
1222
1219
  }) {
1223
1220
  const { stdout } = await executeProcess({
1224
1221
  command: bin,
1225
1222
  args: [
1226
1223
  ...config ? [`--config=${config}`] : [],
1227
- ...persistCliOptions({ directory, project })
1224
+ ...persistCliOptions({ directory, project, output })
1228
1225
  ],
1229
1226
  cwd: directory
1230
1227
  });
1231
1228
  if (!silent) {
1232
1229
  console.info(stdout);
1233
1230
  }
1234
- return persistedCliFiles({ directory, project });
1231
+ return persistedCliFiles({ directory, project, output });
1235
1232
  }
1236
1233
 
1237
1234
  // packages/ci/src/lib/cli/commands/compare.ts
1238
- async function runCompare({ before, after, label }, { bin, config, directory, silent, project }) {
1235
+ async function runCompare({ before, after, label }, { bin, config, directory, silent, project, output }) {
1239
1236
  const { stdout } = await executeProcess({
1240
1237
  command: bin,
1241
1238
  args: [
@@ -1244,32 +1241,37 @@ async function runCompare({ before, after, label }, { bin, config, directory, si
1244
1241
  `--after=${after}`,
1245
1242
  ...label ? [`--label=${label}`] : [],
1246
1243
  ...config ? [`--config=${config}`] : [],
1247
- ...persistCliOptions({ directory, project })
1244
+ ...persistCliOptions({ directory, project, output })
1248
1245
  ],
1249
1246
  cwd: directory
1250
1247
  });
1251
1248
  if (!silent) {
1252
1249
  console.info(stdout);
1253
1250
  }
1254
- return persistedCliFiles({ directory, isDiff: true, project });
1251
+ return persistedCliFiles({ directory, isDiff: true, project, output });
1255
1252
  }
1256
1253
 
1257
1254
  // packages/ci/src/lib/cli/commands/merge-diffs.ts
1258
- async function runMergeDiffs(files, { bin, config, directory, silent }) {
1255
+ async function runMergeDiffs(files, { bin, config, directory, silent, output }) {
1259
1256
  const { stdout } = await executeProcess({
1260
1257
  command: bin,
1261
1258
  args: [
1262
1259
  "merge-diffs",
1263
1260
  ...files.map((file) => `--files=${file}`),
1264
1261
  ...config ? [`--config=${config}`] : [],
1265
- ...persistCliOptions({ directory })
1262
+ ...persistCliOptions({ directory, output })
1266
1263
  ],
1267
1264
  cwd: directory
1268
1265
  });
1269
1266
  if (!silent) {
1270
1267
  console.info(stdout);
1271
1268
  }
1272
- return persistedCliFiles({ directory, isDiff: true, formats: ["md"] });
1269
+ return persistedCliFiles({
1270
+ directory,
1271
+ isDiff: true,
1272
+ formats: ["md"],
1273
+ output
1274
+ });
1273
1275
  }
1274
1276
 
1275
1277
  // packages/ci/src/lib/cli/commands/print-config.ts
@@ -1296,7 +1298,8 @@ function createCommandContext(settings, project) {
1296
1298
  bin: project?.bin ?? settings.bin,
1297
1299
  directory: project?.directory ?? settings.directory,
1298
1300
  config: settings.config,
1299
- silent: settings.silent
1301
+ silent: settings.silent,
1302
+ output: settings.output.replaceAll("{project}", project?.name ?? "")
1300
1303
  };
1301
1304
  }
1302
1305
 
@@ -1350,7 +1353,8 @@ var DEFAULT_SETTINGS = {
1350
1353
  silent: false,
1351
1354
  debug: false,
1352
1355
  detectNewIssues: true,
1353
- logger: console
1356
+ logger: console,
1357
+ output: DEFAULT_PERSIST_OUTPUT_DIR
1354
1358
  };
1355
1359
 
1356
1360
  // packages/ci/src/lib/git.ts
@@ -1510,6 +1514,9 @@ function createIssuesSortCompareFn(report) {
1510
1514
  return (a, b) => getAuditImpactValue(b, report) - getAuditImpactValue(a, report);
1511
1515
  }
1512
1516
  function getAuditImpactValue({ audit, plugin }, report) {
1517
+ if (!report.categories) {
1518
+ return 0;
1519
+ }
1513
1520
  return report.categories.map((category) => {
1514
1521
  const weights = category.refs.map((ref) => {
1515
1522
  if (ref.plugin !== plugin.slug) {
@@ -1519,17 +1526,20 @@ function getAuditImpactValue({ audit, plugin }, report) {
1519
1526
  case "audit":
1520
1527
  return ref.slug === audit.slug ? ref.weight : 0;
1521
1528
  case "group":
1522
- const group = report.plugins.find(({ slug }) => slug === ref.plugin)?.groups?.find(({ slug }) => slug === ref.slug);
1523
- if (!group?.refs.length) {
1524
- return 0;
1525
- }
1526
- const groupRatio = (group.refs.find(({ slug }) => slug === audit.slug)?.weight ?? 0) / group.refs.reduce((acc, { weight }) => acc + weight, 0);
1527
- return ref.weight * groupRatio;
1529
+ return calculateGroupImpact(ref, audit, report);
1528
1530
  }
1529
1531
  });
1530
1532
  return weights.reduce((acc, weight) => acc + weight, 0) / category.refs.reduce((acc, { weight }) => acc + weight, 0);
1531
1533
  }).reduce((acc, value) => acc + value, 0);
1532
1534
  }
1535
+ function calculateGroupImpact(ref, audit, report) {
1536
+ const group = report.plugins.find(({ slug }) => slug === ref.plugin)?.groups?.find(({ slug }) => slug === ref.slug);
1537
+ if (!group?.refs.length) {
1538
+ return 0;
1539
+ }
1540
+ const groupRatio = (group.refs.find(({ slug }) => slug === audit.slug)?.weight ?? 0) / group.refs.reduce((acc, { weight }) => acc + weight, 0);
1541
+ return ref.weight * groupRatio;
1542
+ }
1533
1543
 
1534
1544
  // packages/ci/src/lib/run.ts
1535
1545
  async function runInCI(refs, api, options, git = simpleGit4()) {
@@ -1611,7 +1621,7 @@ async function runOnProject(args) {
1611
1621
  return noDiffOutput;
1612
1622
  }
1613
1623
  logger.info(
1614
- `PR detected, preparing to compare base branch ${base.ref} to head ${head.ref}`
1624
+ `PR/MR detected, preparing to compare base branch ${base.ref} to head ${head.ref}`
1615
1625
  );
1616
1626
  const prevReport = await collectPreviousReport({ ...args, base, ctx });
1617
1627
  if (!prevReport) {
@@ -1652,9 +1662,13 @@ async function runOnProject(args) {
1652
1662
  return { ...diffOutput, newIssues };
1653
1663
  }
1654
1664
  async function collectPreviousReport(args) {
1655
- const { base, api, settings, ctx, git } = args;
1665
+ const { project, base, api, settings, ctx, git } = args;
1656
1666
  const logger = settings.logger;
1657
- const cachedBaseReport = await api.downloadReportArtifact?.();
1667
+ const cachedBaseReport = await api.downloadReportArtifact?.(project?.name).catch((error) => {
1668
+ logger.warn(
1669
+ `Error when downloading previous report artifact, skipping - ${stringifyError(error)}`
1670
+ );
1671
+ });
1658
1672
  if (api.downloadReportArtifact != null) {
1659
1673
  logger.info(
1660
1674
  `Previous report artifact ${cachedBaseReport ? "found" : "not found"}`
@@ -1687,7 +1701,7 @@ async function collectPreviousReport(args) {
1687
1701
  const prevReport = await fs.readFile(prevReportPath, "utf8");
1688
1702
  logger.debug(`Collected previous report at ${prevReportPath}`);
1689
1703
  await git.checkout(["-f", "-"]);
1690
- logger.info("Switched back to PR branch");
1704
+ logger.info("Switched back to PR/MR branch");
1691
1705
  return prevReport;
1692
1706
  }
1693
1707
  }
@@ -1706,7 +1720,7 @@ async function findNewIssues(args) {
1706
1720
  changedFiles
1707
1721
  });
1708
1722
  logger.debug(
1709
- `Found ${issues.length} relevant issues for ${Object.keys(changedFiles).length} changed files and created GitHub annotations`
1723
+ `Found ${issues.length} relevant issues for ${Object.keys(changedFiles).length} changed files`
1710
1724
  );
1711
1725
  return issues;
1712
1726
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-pushup/ci",
3
- "version": "0.53.1",
3
+ "version": "0.55.0",
4
4
  "description": "CI automation logic for Code PushUp (provider-agnostic)",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/code-pushup/cli/tree/main/packages/ci#readme",
@@ -28,8 +28,8 @@
28
28
  "main": "./index.js",
29
29
  "types": "./src/index.d.ts",
30
30
  "dependencies": {
31
- "@code-pushup/models": "0.53.1",
32
- "@code-pushup/utils": "0.53.1",
31
+ "@code-pushup/models": "0.55.0",
32
+ "@code-pushup/utils": "0.55.0",
33
33
  "glob": "^10.4.5",
34
34
  "simple-git": "^3.20.0",
35
35
  "yaml": "^2.5.1"
@@ -1,3 +1,3 @@
1
1
  import type { CommandContext } from '../context';
2
2
  import { type PersistedCliFiles } from '../persist';
3
- export declare function runCollect({ bin, config, directory, silent, project, }: CommandContext): Promise<PersistedCliFiles>;
3
+ export declare function runCollect({ bin, config, directory, silent, project, output, }: CommandContext): Promise<PersistedCliFiles>;
@@ -5,5 +5,5 @@ type CompareOptions = {
5
5
  after: string;
6
6
  label?: string;
7
7
  };
8
- export declare function runCompare({ before, after, label }: CompareOptions, { bin, config, directory, silent, project }: CommandContext): Promise<PersistedCliFiles>;
8
+ export declare function runCompare({ before, after, label }: CompareOptions, { bin, config, directory, silent, project, output }: CommandContext): Promise<PersistedCliFiles>;
9
9
  export {};
@@ -1,3 +1,3 @@
1
1
  import type { CommandContext } from '../context';
2
2
  import { type PersistedCliFiles } from '../persist';
3
- export declare function runMergeDiffs(files: string[], { bin, config, directory, silent }: CommandContext): Promise<PersistedCliFiles<'md'>>;
3
+ export declare function runMergeDiffs(files: string[], { bin, config, directory, silent, output }: CommandContext): Promise<PersistedCliFiles<'md'>>;
@@ -1,6 +1,6 @@
1
1
  import type { Settings } from '../models';
2
2
  import type { ProjectConfig } from '../monorepo';
3
- export type CommandContext = Pick<Settings, 'bin' | 'config' | 'directory' | 'silent'> & {
3
+ export type CommandContext = Pick<Settings, 'bin' | 'config' | 'directory' | 'silent' | 'output'> & {
4
4
  project?: string;
5
5
  };
6
6
  export declare function createCommandContext(settings: Settings, project: ProjectConfig | null | undefined): CommandContext;
@@ -8,14 +8,20 @@ export type PersistedCliFiles<T extends Format = Format> = PersistedCliFilesForm
8
8
  export type PersistedCliFilesFormats<T extends Format = Format> = {
9
9
  [F in T as `${F}FilePath`]: string;
10
10
  };
11
- export declare function persistCliOptions({ directory, project, }: {
11
+ export declare function persistCliOptions({ directory, project, output, }: {
12
12
  directory: string;
13
13
  project?: string;
14
+ output: string;
14
15
  }): string[];
15
- export declare function persistedCliFiles<TFormat extends Format = Format>({ directory, isDiff, project, formats, }: {
16
+ export declare function persistedCliFiles<TFormat extends Format = Format>({ directory, isDiff, project, formats, output, }: {
16
17
  directory: string;
17
18
  isDiff?: boolean;
18
19
  project?: string;
19
20
  formats?: TFormat[];
21
+ output: string;
20
22
  }): PersistedCliFiles<TFormat>;
21
- export declare function findPersistedFiles(rootDir: string, files: string[], project?: string): PersistedCliFiles;
23
+ export declare function findPersistedFiles({ rootDir, files, project, }: {
24
+ rootDir: string;
25
+ files: string[];
26
+ project?: string;
27
+ }): PersistedCliFiles;
@@ -1,4 +1,4 @@
1
- import type { Audit, Issue, PluginMeta, Report, ReportsDiff } from '@code-pushup/models';
1
+ import type { Audit, CategoryRef, Issue, PluginMeta, Report, ReportsDiff } from '@code-pushup/models';
2
2
  import { type ChangedFiles } from './git';
3
3
  export type SourceFileIssue = Required<Issue> & IssueContext;
4
4
  type IssueContext = {
@@ -13,4 +13,5 @@ export declare function filterRelevantIssues({ currReport, prevReport, reportsDi
13
13
  }): SourceFileIssue[];
14
14
  export declare function issuesMatch(prev: SourceFileIssue, curr: SourceFileIssue, changedFiles: ChangedFiles): boolean;
15
15
  export declare function getAuditImpactValue({ audit, plugin }: IssueContext, report: Report): number;
16
+ export declare function calculateGroupImpact(ref: CategoryRef, audit: Audit, report: Report): number;
16
17
  export {};
@@ -15,6 +15,7 @@ export type Options = {
15
15
  debug?: boolean;
16
16
  detectNewIssues?: boolean;
17
17
  logger?: Logger;
18
+ output?: string;
18
19
  };
19
20
  /**
20
21
  * {@link Options} with filled-in defaults.
@@ -33,13 +34,13 @@ export type GitRefs = {
33
34
  */
34
35
  export type ProviderAPIClient = {
35
36
  maxCommentChars: number;
36
- downloadReportArtifact?: () => Promise<string | null>;
37
+ downloadReportArtifact?: (project?: string) => Promise<string | null>;
37
38
  listComments: () => Promise<Comment[]>;
38
39
  updateComment: (id: number, body: string) => Promise<Comment>;
39
40
  createComment: (body: string) => Promise<Comment>;
40
41
  };
41
42
  /**
42
- * PR comment from {@link ProviderAPIClient}
43
+ * PR/MR comment from {@link ProviderAPIClient}
43
44
  */
44
45
  export type Comment = {
45
46
  id: number;