Skip to content

Commit 49adbf3

Browse files
authored
feat: add report option to cli (#1058)
1 parent 7daf201 commit 49adbf3

File tree

17 files changed

+158
-31
lines changed

17 files changed

+158
-31
lines changed

e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ Global Options:
3737
[array] [default: []]
3838
3939
Persist Options:
40-
--persist.outputDir Directory for the produced reports
40+
--persist.outputDir Directory for the produced reports
4141
[string]
42-
--persist.filename Filename for the produced reports.
42+
--persist.filename Filename for the produced reports.
4343
[string]
44-
--persist.format Format of the report output. e.g. \`md\`, \`json\`
44+
--persist.format Format of the report output. e.g. \`md\`, \`json\`
4545
[array]
46+
--persist.skipReports Skip generating report files. (useful in combinatio
47+
n with caching) [boolean]
4648
4749
Upload Options:
4850
--upload.organization Organization slug from portal

e2e/cli-e2e/tests/collect.e2e.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
TEST_OUTPUT_DIR,
88
teardownTestFolder,
99
} from '@code-pushup/test-utils';
10-
import { executeProcess, readTextFile } from '@code-pushup/utils';
10+
import { executeProcess, fileExists, readTextFile } from '@code-pushup/utils';
1111

1212
describe('CLI collect', () => {
1313
const dummyPluginTitle = 'Dummy Plugin';
@@ -61,6 +61,28 @@ describe('CLI collect', () => {
6161
expect(md).toContain(dummyAuditTitle);
6262
});
6363

64+
it('should not create reports if --persist.skipReports is given', async () => {
65+
const { code } = await executeProcess({
66+
command: 'npx',
67+
args: [
68+
'@code-pushup/cli',
69+
'--no-progress',
70+
'collect',
71+
'--persist.skipReports',
72+
],
73+
cwd: dummyDir,
74+
});
75+
76+
expect(code).toBe(0);
77+
78+
await expect(
79+
fileExists(path.join(dummyOutputDir, 'report.md')),
80+
).resolves.toBeFalsy();
81+
await expect(
82+
fileExists(path.join(dummyOutputDir, 'report.json')),
83+
).resolves.toBeFalsy();
84+
});
85+
6486
it('should print report summary to stdout', async () => {
6587
const { code, stdout } = await executeProcess({
6688
command: 'npx',

packages/ci/src/lib/run-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ export function configFromPatterns(
521521
outputDir: interpolate(persist.outputDir, variables),
522522
filename: interpolate(persist.filename, variables),
523523
format: persist.format,
524+
skipReports: persist.skipReports,
524525
},
525526
...(upload && {
526527
upload: {

packages/cli/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ Each example is fully tested to demonstrate best practices for plugin testing as
212212
| **`--persist.outputDir`** | `string` | n/a | Directory for the produced reports. |
213213
| **`--persist.filename`** | `string` | `report` | Filename for the produced reports without extension. |
214214
| **`--persist.format`** | `('json' \| 'md')[]` | `json` | Format(s) of the report file. |
215+
| **`--persist.skipReports`** | `boolean` | `false` | Skip generating report files. (useful in combination with caching) |
215216
| **`--upload.organization`** | `string` | n/a | Organization slug from portal. |
216217
| **`--upload.project`** | `string` | n/a | Project slug from portal. |
217218
| **`--upload.server`** | `string` | n/a | URL to your portal server. |

packages/cli/src/lib/collect/collect-command.unit.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('collect-command', () => {
3737
expect(collectAndPersistReports).toHaveBeenCalledWith(
3838
expect.objectContaining({
3939
config: '/test/code-pushup.config.ts',
40-
persist: expect.objectContaining<Required<PersistConfig>>({
40+
persist: expect.objectContaining<PersistConfig>({
4141
filename: DEFAULT_PERSIST_FILENAME,
4242
outputDir: DEFAULT_PERSIST_OUTPUT_DIR,
4343
format: DEFAULT_PERSIST_FORMAT,

packages/cli/src/lib/compare/compare-command.unit.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('compare-command', () => {
4141
outputDir: DEFAULT_PERSIST_OUTPUT_DIR,
4242
filename: DEFAULT_PERSIST_FILENAME,
4343
format: DEFAULT_PERSIST_FORMAT,
44+
skipReports: false,
4445
},
4546
upload: expect.any(Object),
4647
},

packages/cli/src/lib/implementation/core-config.int.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('parsing values from CLI and middleware', () => {
6464
filename: DEFAULT_PERSIST_FILENAME,
6565
format: DEFAULT_PERSIST_FORMAT,
6666
outputDir: DEFAULT_PERSIST_OUTPUT_DIR,
67+
skipReports: false,
6768
});
6869
});
6970

@@ -85,6 +86,7 @@ describe('parsing values from CLI and middleware', () => {
8586
filename: 'cli-filename',
8687
format: ['md'],
8788
outputDir: 'cli-outputDir',
89+
skipReports: false,
8890
});
8991
});
9092

@@ -101,6 +103,7 @@ describe('parsing values from CLI and middleware', () => {
101103
filename: 'rc-filename',
102104
format: ['json', 'md'],
103105
outputDir: 'rc-outputDir',
106+
skipReports: false,
104107
});
105108
});
106109

@@ -122,6 +125,7 @@ describe('parsing values from CLI and middleware', () => {
122125
filename: 'cli-filename',
123126
format: ['md'],
124127
outputDir: 'cli-outputDir',
128+
skipReports: false,
125129
});
126130
});
127131

@@ -141,6 +145,7 @@ describe('parsing values from CLI and middleware', () => {
141145
filename: 'rc-filename',
142146
format: DEFAULT_PERSIST_FORMAT,
143147
outputDir: 'cli-outputdir',
148+
skipReports: false,
144149
});
145150
});
146151

packages/cli/src/lib/implementation/core-config.middleware.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ export type CoreConfigMiddlewareOptions = GeneralCliOptions &
1515
CoreConfigCliOptions &
1616
FilterOptions;
1717

18+
function buildPersistConfig(
19+
cliPersist: CoreConfigCliOptions['persist'],
20+
rcPersist: CoreConfig['persist'],
21+
): Required<CoreConfig['persist']> {
22+
return {
23+
outputDir:
24+
cliPersist?.outputDir ??
25+
rcPersist?.outputDir ??
26+
DEFAULT_PERSIST_OUTPUT_DIR,
27+
filename:
28+
cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
29+
format: normalizeFormats(
30+
cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT,
31+
),
32+
skipReports: cliPersist?.skipReports ?? rcPersist?.skipReports ?? false,
33+
};
34+
}
35+
1836
export async function coreConfigMiddleware<
1937
T extends CoreConfigMiddlewareOptions,
2038
>(processArgs: T): Promise<GeneralCliOptions & CoreConfig & FilterOptions> {
@@ -43,22 +61,23 @@ export async function coreConfigMiddleware<
4361
});
4462
return {
4563
...(config != null && { config }),
46-
persist: {
47-
outputDir:
48-
cliPersist?.outputDir ??
49-
rcPersist?.outputDir ??
50-
DEFAULT_PERSIST_OUTPUT_DIR,
51-
filename:
52-
cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
53-
format: normalizeFormats(
54-
cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT,
55-
),
56-
},
64+
persist: buildPersistConfig(cliPersist, rcPersist),
5765
...(upload != null && { upload }),
5866
...remainingRcConfig,
5967
...remainingCliOptions,
6068
};
6169
}
6270

71+
export const normalizeBooleanWithNegation = <T extends string>(
72+
propertyName: T,
73+
cliOptions?: Record<T, unknown>,
74+
rcOptions?: Record<T, unknown>,
75+
): boolean =>
76+
propertyName in (cliOptions ?? {})
77+
? (cliOptions?.[propertyName] as boolean)
78+
: `no-${propertyName}` in (cliOptions ?? {})
79+
? false
80+
: ((rcOptions?.[propertyName] as boolean) ?? true);
81+
6382
export const normalizeFormats = (formats?: string[]): Format[] =>
6483
(formats ?? []).flatMap(format => format.split(',') as Format[]);

packages/cli/src/lib/implementation/core-config.middleware.unit.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, vi } from 'vitest';
22
import { autoloadRc, readRcByPath } from '@code-pushup/core';
33
import {
44
coreConfigMiddleware,
5+
normalizeBooleanWithNegation,
56
normalizeFormats,
67
} from './core-config.middleware.js';
78
import type { CoreConfigCliOptions } from './core-config.model.js';
@@ -19,6 +20,36 @@ vi.mock('@code-pushup/core', async () => {
1920
};
2021
});
2122

23+
describe('normalizeBooleanWithNegation', () => {
24+
it('should return true when CLI property is true', () => {
25+
expect(normalizeBooleanWithNegation('report', { report: true }, {})).toBe(
26+
true,
27+
);
28+
});
29+
30+
it('should return false when CLI property is false', () => {
31+
expect(normalizeBooleanWithNegation('report', { report: false }, {})).toBe(
32+
false,
33+
);
34+
});
35+
36+
it('should return false when no-property exists in CLI persist', () => {
37+
expect(
38+
normalizeBooleanWithNegation('report', { 'no-report': true }, {}),
39+
).toBe(false);
40+
});
41+
42+
it('should fallback to RC persist when no CLI property', () => {
43+
expect(normalizeBooleanWithNegation('report', {}, { report: false })).toBe(
44+
false,
45+
);
46+
});
47+
48+
it('should return default true when no property anywhere', () => {
49+
expect(normalizeBooleanWithNegation('report', {}, {})).toBe(true);
50+
});
51+
});
52+
2253
describe('normalizeFormats', () => {
2354
it('should forward valid formats', () => {
2455
expect(normalizeFormats(['json', 'md'])).toEqual(['json', 'md']);

packages/cli/src/lib/implementation/core-config.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type PersistConfigCliOptions = {
44
'persist.outputDir'?: string;
55
'persist.filename'?: string;
66
'persist.format'?: Format;
7+
'persist.skipReports'?: boolean;
78
};
89

910
export type UploadConfigCliOptions = {

0 commit comments

Comments
 (0)