Skip to content

Commit d8d799c

Browse files
committed
feat(cli): improve collect and upload logs
1 parent 28367ed commit d8d799c

22 files changed

+165
-385
lines changed

packages/cli/src/lib/autorun/autorun-command.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import {
77
} from '@code-pushup/core';
88
import { logger } from '@code-pushup/utils';
99
import {
10-
collectSuccessfulLog,
1110
printCliCommand,
1211
renderCategoriesHint,
1312
renderPortalHint,
14-
uploadSuccessfulLog,
1513
} from '../implementation/logging.js';
1614

1715
type AutorunOptions = CollectOptions & UploadOptions;
@@ -38,19 +36,15 @@ export function yargsAutorunCommandObject() {
3836
};
3937

4038
await collectAndPersistReports(optionsWithFormat);
41-
collectSuccessfulLog();
39+
logger.newline();
4240

4341
if (!options.categories?.length) {
44-
logger.newline();
4542
renderCategoriesHint();
4643
logger.newline();
4744
}
4845

4946
if (options.upload) {
50-
const report = await upload(options);
51-
if (report?.url) {
52-
uploadSuccessfulLog(report.url);
53-
}
47+
await upload(options);
5448
} else {
5549
logger.warn('Upload skipped because Portal is not configured.');
5650
logger.newline();

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
} from '@code-pushup/core';
66
import { logger } from '@code-pushup/utils';
77
import {
8-
collectSuccessfulLog,
98
printCliCommand,
109
renderCategoriesHint,
1110
renderPortalHint,
@@ -23,7 +22,6 @@ export function yargsCollectCommandObject(): CommandModule {
2322
const options = args as unknown as CollectAndPersistReportsOptions;
2423

2524
await collectAndPersistReports(options);
26-
collectSuccessfulLog();
2725

2826
if (!options.categories?.length) {
2927
logger.newline();

packages/cli/src/lib/implementation/logging.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@ export function printCliCommand(command: string): void {
99
logger.debug(`Running ${ansis.bold(command)} command`);
1010
}
1111

12-
export function uploadSuccessfulLog(url: string): void {
13-
logger.info(ansis.green('Upload successful!'));
14-
logger.info(formatAsciiLink(url));
15-
}
16-
17-
export function collectSuccessfulLog(): void {
18-
logger.info(ansis.green('Collecting report successful!'));
19-
}
20-
2112
export function renderCategoriesHint(): void {
2213
logger.info(
2314
formatAsciiSticker([

packages/cli/src/lib/middlewares.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { setVerboseMiddleware } from './implementation/set-verbose.middleware.js
66

77
export const middlewares = [
88
{
9-
middlewareFunction: logIntroMiddleware,
9+
middlewareFunction: logIntroMiddleware as unknown as MiddlewareFunction,
1010
applyBeforeValidation: true,
1111
},
1212
{

packages/cli/src/lib/upload/upload-command.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { logger } from '@code-pushup/utils';
44
import {
55
printCliCommand,
66
renderPortalHint,
7-
uploadSuccessfulLog,
87
} from '../implementation/logging.js';
98

109
export function yargsUploadCommandObject() {
@@ -22,10 +21,7 @@ export function yargsUploadCommandObject() {
2221
logger.newline();
2322
throw new Error('Upload to Portal is missing configuration');
2423
}
25-
const report = await upload(options);
26-
if (report?.url) {
27-
uploadSuccessfulLog(report.url);
28-
}
24+
await upload(options);
2925
},
3026
} satisfies CommandModule;
3127
}
Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import {
2-
type CacheConfigObject,
3-
type CoreConfig,
4-
type PersistConfig,
5-
pluginReportSchema,
6-
validate,
1+
import type {
2+
CacheConfigObject,
3+
CoreConfig,
4+
PersistConfig,
75
} from '@code-pushup/models';
86
import {
97
logStdoutSummary,
@@ -12,10 +10,7 @@ import {
1210
sortReport,
1311
} from '@code-pushup/utils';
1412
import { collect } from './implementation/collect.js';
15-
import {
16-
logPersistedResults,
17-
persistReport,
18-
} from './implementation/persist.js';
13+
import { logPersistedReport, persistReport } from './implementation/persist.js';
1914

2015
export type CollectAndPersistReportsOptions = Pick<
2116
CoreConfig,
@@ -36,22 +31,17 @@ export async function collectAndPersistReports(
3631
const { skipReports = false, ...persistOptions } = persist ?? {};
3732

3833
if (skipReports) {
39-
logger.info('Skipping saving reports as `persist.skipReports` is true');
34+
logger.info('Skipped saving report as persist.skipReports flag is set');
4035
} else {
41-
const persistResults = await persistReport(
36+
const reportFiles = await persistReport(
4237
reportResult,
4338
sortedScoredReport,
4439
persistOptions,
4540
);
46-
logPersistedResults(persistResults);
41+
logPersistedReport(reportFiles);
4742
}
4843

4944
// terminal output
45+
logger.newline();
5046
logStdoutSummary(sortedScoredReport);
51-
52-
// validate report and throw if invalid
53-
reportResult.plugins.forEach(plugin => {
54-
// Running checks after persisting helps while debugging as you can check the invalid output after the error is thrown
55-
validate(pluginReportSchema, plugin);
56-
});
5747
}

packages/core/src/lib/collect-and-persist.unit.test.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,15 @@ import {
1515
collectAndPersistReports,
1616
} from './collect-and-persist.js';
1717
import { collect } from './implementation/collect.js';
18-
import {
19-
logPersistedResults,
20-
persistReport,
21-
} from './implementation/persist.js';
18+
import { logPersistedReport, persistReport } from './implementation/persist.js';
2219

2320
vi.mock('./implementation/collect', () => ({
2421
collect: vi.fn().mockResolvedValue(MINIMAL_REPORT_MOCK),
2522
}));
2623

2724
vi.mock('./implementation/persist', () => ({
2825
persistReport: vi.fn(),
29-
logPersistedResults: vi.fn(),
26+
logPersistedReport: vi.fn(),
3027
}));
3128

3229
describe('collectAndPersistReports', () => {
@@ -60,7 +57,7 @@ describe('collectAndPersistReports', () => {
6057
>(MINIMAL_REPORT_MOCK, sortedScoredReport, config.persist);
6158

6259
expect(logStdoutSummary).toHaveBeenCalledWith(sortedScoredReport);
63-
expect(logPersistedResults).toHaveBeenCalled();
60+
expect(logPersistedReport).toHaveBeenCalled();
6461
});
6562

6663
it('should call collect and not persistReport if skipReports options is true', async () => {
@@ -81,7 +78,7 @@ describe('collectAndPersistReports', () => {
8178
expect(collect).toHaveBeenCalledWith(verboseConfig);
8279

8380
expect(persistReport).not.toHaveBeenCalled();
84-
expect(logPersistedResults).not.toHaveBeenCalled();
81+
expect(logPersistedReport).not.toHaveBeenCalled();
8582

8683
expect(logStdoutSummary).toHaveBeenCalledWith(sortedScoredReport);
8784
});

packages/core/src/lib/history.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
UploadConfig,
66
} from '@code-pushup/models';
77
import {
8+
type WithRequired,
89
getCurrentBranchOrTag,
910
logger,
1011
safeCheckout,
@@ -55,7 +56,7 @@ export async function history(
5556
if (skipUploads) {
5657
logger.info('Upload is skipped because skipUploads is set to true.');
5758
} else {
58-
if (currentConfig.upload) {
59+
if (hasUpload(currentConfig)) {
5960
await upload(currentConfig);
6061
} else {
6162
logger.info('Upload is skipped because upload config is undefined.');
@@ -70,3 +71,9 @@ export async function history(
7071

7172
return reports;
7273
}
74+
75+
function hasUpload(
76+
config: HistoryOptions,
77+
): config is WithRequired<HistoryOptions, 'upload'> {
78+
return config.upload != null;
79+
}

packages/core/src/lib/implementation/collect.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import ansis from 'ansis';
12
import { createRequire } from 'node:module';
23
import type {
34
CacheConfigObject,
45
CoreConfig,
56
PersistConfig,
67
Report,
78
} from '@code-pushup/models';
8-
import { calcDuration, getLatestCommit } from '@code-pushup/utils';
9+
import {
10+
calcDuration,
11+
getLatestCommit,
12+
logger,
13+
pluralizeToken,
14+
} from '@code-pushup/utils';
915
import { executePlugins } from './execute-plugin.js';
1016

1117
export type CollectOptions = Pick<CoreConfig, 'plugins' | 'categories'> & {
@@ -19,13 +25,26 @@ export type CollectOptions = Pick<CoreConfig, 'plugins' | 'categories'> & {
1925
*/
2026
export async function collect(options: CollectOptions): Promise<Report> {
2127
const { plugins, categories, persist = {}, cache } = options;
28+
2229
const date = new Date().toISOString();
2330
const start = performance.now();
24-
const commit = await getLatestCommit();
25-
const pluginOutputs = await executePlugins({ plugins, persist, cache });
2631
const packageJson = createRequire(import.meta.url)(
2732
'../../../package.json',
2833
) as typeof import('../../../package.json');
34+
35+
const commit = await getLatestCommit();
36+
logger.debug(
37+
commit
38+
? `Found latest commit ${commit.hash} ("${commit.message}" by ${commit.author})`
39+
: 'Latest commit not found',
40+
);
41+
42+
logger.info(
43+
`Collecting report from ${pluralizeToken('plugin', plugins.length)} ...`,
44+
);
45+
const pluginOutputs = await executePlugins({ plugins, persist, cache });
46+
logger.info(ansis.green('Collected report ✓'));
47+
2948
return {
3049
commit,
3150
packageName: packageJson.name,

packages/core/src/lib/implementation/persist.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
import ansis from 'ansis';
22
import { mkdir, stat, writeFile } from 'node:fs/promises';
3+
import path from 'node:path';
34
import type { Format, PersistConfig, Report } from '@code-pushup/models';
45
import {
5-
type MultipleFileResults,
66
type ScoredReport,
77
createReportPath,
88
directoryExists,
9+
formatBytes,
910
generateMdReport,
10-
logMultipleFileResults,
11+
logger,
1112
stringifyError,
1213
} from '@code-pushup/utils';
1314

15+
type FileSize = {
16+
file: string;
17+
size: number;
18+
};
19+
1420
export async function persistReport(
1521
report: Report,
1622
sortedScoredReport: ScoredReport,
1723
options: Required<Omit<PersistConfig, 'skipReports'>>,
18-
): Promise<MultipleFileResults> {
24+
): Promise<FileSize[]> {
1925
const { outputDir, filename, format } = options;
2026

21-
// collect physical format outputs
27+
// format report
2228
const results = format.map(
2329
(reportType): { format: Format; content: string } => {
2430
switch (reportType) {
@@ -47,7 +53,7 @@ export async function persistReport(
4753
}
4854

4955
// write relevant format outputs to file system
50-
return Promise.allSettled(
56+
return Promise.all(
5157
results.map(result =>
5258
persistResult(
5359
createReportPath({ outputDir, filename, format: result.format }),
@@ -57,20 +63,25 @@ export async function persistReport(
5763
);
5864
}
5965

60-
function persistResult(reportPath: string, content: string) {
66+
function persistResult(reportPath: string, content: string): Promise<FileSize> {
6167
return (
6268
writeFile(reportPath, content)
6369
// return reportPath instead of void
6470
.then(() => stat(reportPath))
65-
.then(stats => [reportPath, stats.size] as const)
71+
.then((stats): FileSize => ({ file: reportPath, size: stats.size }))
6672
.catch((error: unknown) => {
6773
throw new Error(
68-
`Failed to persist report in ${ansis.bold(reportPath)} - ${stringifyError(error)}`,
74+
`Failed to save report in ${ansis.bold(reportPath)} - ${stringifyError(error)}`,
6975
);
7076
})
7177
);
7278
}
7379

74-
export function logPersistedResults(persistResults: MultipleFileResults) {
75-
logMultipleFileResults(persistResults, 'Generated reports');
80+
export function logPersistedReport(reportFiles: FileSize[]) {
81+
logger.info(`Persisted report to file system:`);
82+
reportFiles.forEach(({ file, size }) => {
83+
const name = ansis.bold(path.relative(process.cwd(), file));
84+
const suffix = ansis.gray(`(${formatBytes(size)})`);
85+
logger.info(`• ${name} ${suffix}`);
86+
});
7687
}

0 commit comments

Comments
 (0)