Skip to content

Commit 5973d5a

Browse files
committed
feat(ci): style top-level logs
1 parent a83baae commit 5973d5a

File tree

10 files changed

+135
-56
lines changed

10 files changed

+135
-56
lines changed

packages/ci/src/lib/comment.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { readFile } from 'node:fs/promises';
2-
import { logger } from '@code-pushup/utils';
2+
import { log } from './log.js';
33
import type { ProviderAPIClient, Settings } from './models.js';
44

55
export async function commentOnPR(
@@ -17,32 +17,33 @@ export async function commentOnPR(
1717
);
1818

1919
const comments = await api.listComments();
20-
logger.debug(`Fetched ${comments.length} comments for pull request`);
20+
log('debug', `Fetched ${comments.length} comments for pull request`);
2121

2222
const prevComment = comments.find(comment =>
2323
comment.body.includes(identifier),
2424
);
25-
logger.debug(
25+
log(
26+
'debug',
2627
prevComment
2728
? `Found previous comment ${prevComment.id} from Code PushUp`
2829
: 'Previous Code PushUp comment not found',
2930
);
3031

3132
if (prevComment) {
3233
const updatedComment = await api.updateComment(prevComment.id, body);
33-
logger.info(`Updated body of comment ${updatedComment.url}`);
34+
log('info', `Updated body of comment ${updatedComment.url}`);
3435
return updatedComment.id;
3536
}
3637

3738
const createdComment = await api.createComment(body);
38-
logger.info(`Created new comment ${createdComment.url}`);
39+
log('info', `Created new comment ${createdComment.url}`);
3940
return createdComment.id;
4041
}
4142

4243
function truncateBody(body: string, max: number): string {
4344
const truncateWarning = '...*[Comment body truncated]*';
4445
if (body.length > max) {
45-
logger.warn(`Comment body is too long. Truncating to ${max} characters.`);
46+
log('warn', `Comment body is too long. Truncating to ${max} characters.`);
4647
return body.slice(0, max - truncateWarning.length) + truncateWarning;
4748
}
4849
return body;

packages/ci/src/lib/comment.unit.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ansis from 'ansis';
12
import { vol } from 'memfs';
23
import { writeFile } from 'node:fs/promises';
34
import path from 'node:path';
@@ -112,7 +113,7 @@ describe('commentOnPR', () => {
112113
expect.stringContaining('...*[Comment body truncated]*'),
113114
);
114115
expect(logger.warn).toHaveBeenCalledWith(
115-
'Comment body is too long. Truncating to 1000000 characters.',
116+
`${ansis.bold.blue('<✓>')} Comment body is too long. Truncating to 1000000 characters.\n`,
116117
);
117118
});
118119
});

packages/ci/src/lib/log.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import ansis from 'ansis';
2+
import {
3+
CODE_PUSHUP_UNICODE_LOGO,
4+
logger,
5+
transformLines,
6+
} from '@code-pushup/utils';
7+
8+
const LOG_PREFIX = ansis.bold.blue(CODE_PUSHUP_UNICODE_LOGO);
9+
10+
/**
11+
* Prefixes CI logs with logo and ensures each CI log is followed by an empty line.
12+
* This is to make top-level CI logs more visually distinct from printed process logs.
13+
* @param level Log level
14+
* @param message Log message
15+
*/
16+
export function log(
17+
level: 'error' | 'warn' | 'info' | 'debug',
18+
message: string,
19+
): void {
20+
const prefixedLines = transformLines(
21+
message.trim(),
22+
line => `${LOG_PREFIX} ${line}`,
23+
);
24+
const styledMessage = `${prefixedLines}\n`;
25+
26+
logger[level](styledMessage);
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import ansis from 'ansis';
2+
import { logger } from '@code-pushup/utils';
3+
import { log } from './log';
4+
5+
describe('log', () => {
6+
it('should add logo prefix and ending line-break to message', () => {
7+
log('info', 'Running Code PushUp in standalone mode');
8+
expect(logger.info).toHaveBeenCalledWith(
9+
`${ansis.bold.blue('<✓>')} Running Code PushUp in standalone mode\n`,
10+
);
11+
});
12+
13+
it('should add logo prefix to each line', () => {
14+
log('debug', 'Found 3 Nx projects:\n- api\n- backoffice\n- frontoffice');
15+
expect(logger.debug).toHaveBeenCalledWith(
16+
`
17+
${ansis.bold.blue('<✓>')} Found 3 Nx projects:
18+
${ansis.bold.blue('<✓>')} - api
19+
${ansis.bold.blue('<✓>')} - backoffice
20+
${ansis.bold.blue('<✓>')} - frontoffice
21+
`.trimStart(),
22+
);
23+
});
24+
25+
it('should create multiple line-breaks', () => {
26+
log('warn', 'Comment body is too long, truncating to 1000 characters\n');
27+
expect(logger.warn).toHaveBeenCalledWith(
28+
`${ansis.bold.blue('<✓>')} Comment body is too long, truncating to 1000 characters\n`,
29+
);
30+
});
31+
});

packages/ci/src/lib/monorepo/list-projects.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { glob } from 'glob';
22
import path from 'node:path';
3-
import { logger } from '@code-pushup/utils';
3+
import { log } from '../log.js';
44
import type { Settings } from '../models.js';
55
import { detectMonorepoTool } from './detect-tool.js';
66
import { getToolHandler } from './handlers/index.js';
@@ -31,8 +31,8 @@ export async function listMonorepoProjects(
3131
if (tool) {
3232
const handler = getToolHandler(tool);
3333
const projects = await handler.listProjects(options);
34-
logger.info(`Found ${projects.length} projects in ${tool} monorepo`);
35-
logger.debug(`Projects: ${projects.map(({ name }) => name).join(', ')}`);
34+
log('info', `Found ${projects.length} projects in ${tool} monorepo`);
35+
log('debug', `Projects: ${projects.map(({ name }) => name).join(', ')}`);
3636
return {
3737
tool,
3838
projects,
@@ -70,15 +70,15 @@ async function resolveMonorepoTool(
7070
}
7171

7272
if (typeof settings.monorepo === 'string') {
73-
logger.info(`Using monorepo tool "${settings.monorepo}" from inputs`);
73+
log('info', `Using monorepo tool "${settings.monorepo}" from inputs`);
7474
return settings.monorepo;
7575
}
7676

7777
const tool = await detectMonorepoTool(options);
7878
if (tool) {
79-
logger.info(`Auto-detected monorepo tool ${tool}`);
79+
log('info', `Auto-detected monorepo tool ${tool}`);
8080
} else {
81-
logger.info("Couldn't auto-detect any supported monorepo tool");
81+
log('info', "Couldn't auto-detect any supported monorepo tool");
8282
}
8383

8484
return tool;
@@ -110,12 +110,13 @@ async function listProjectsByGlobs(args: {
110110
{ cwd },
111111
);
112112

113-
logger.info(
113+
log(
114+
'info',
114115
`Found ${directories.length} project folders matching "${patterns.join(
115116
', ',
116117
)}" from configuration`,
117118
);
118-
logger.debug(`Projects: ${directories.join(', ')}`);
119+
log('debug', `Projects: ${directories.join(', ')}`);
119120

120121
return directories.toSorted().map(directory => ({
121122
name: directory,
@@ -132,8 +133,8 @@ async function listProjectsByNpmPackages(args: {
132133

133134
const packages = await listPackages(cwd);
134135

135-
logger.info(`Found ${packages.length} NPM packages in repository`);
136-
logger.debug(`Projects: ${packages.map(({ name }) => name).join(', ')}`);
136+
log('info', `Found ${packages.length} NPM packages in repository`);
137+
log('debug', `Projects: ${packages.map(({ name }) => name).join(', ')}`);
137138

138139
return packages.map(({ name, directory }) => ({
139140
name,

packages/ci/src/lib/output-files.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { copyFile, mkdir } from 'node:fs/promises';
22
import path from 'node:path';
33
import { DEFAULT_PERSIST_FILENAME, type Format } from '@code-pushup/models';
4-
import { logger, objectFromEntries, objectToKeys } from '@code-pushup/utils';
4+
import { objectFromEntries, objectToKeys } from '@code-pushup/utils';
5+
import { log } from './log.js';
56
import type { OutputFiles, Settings } from './models.js';
67
import type { ProjectConfig } from './monorepo/tools.js';
78

@@ -45,7 +46,7 @@ export async function saveOutputFiles<T extends Partial<OutputFiles>>({
4546
const src = files[format]!;
4647
const dest = outputs[format];
4748
await copyFile(src, dest);
48-
logger.debug(`Copied ${type} report from ${src} to ${dest}`);
49+
log('debug', `Copied ${type} report from ${src} to ${dest}`);
4950
}),
5051
);
5152

packages/ci/src/lib/output-files.unit.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ansis from 'ansis';
12
import { vol } from 'memfs';
23
import { readFile } from 'node:fs/promises';
34
import path from 'node:path';
@@ -120,10 +121,10 @@ describe('saveOutputFiles', () => {
120121
});
121122

122123
expect(logger.debug).toHaveBeenCalledWith(
123-
`Copied current report from ${path.join(MEMFS_VOLUME, 'report.json')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.json')}`,
124+
`${ansis.bold.blue('<✓>')} Copied current report from ${path.join(MEMFS_VOLUME, 'report.json')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.json')}\n`,
124125
);
125126
expect(logger.debug).toHaveBeenCalledWith(
126-
`Copied current report from ${path.join(MEMFS_VOLUME, 'report.md')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.md')}`,
127+
`${ansis.bold.blue('<✓>')} Copied current report from ${path.join(MEMFS_VOLUME, 'report.md')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.md')}\n`,
127128
);
128129
});
129130

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
type ExcludeNullableProps,
55
asyncSequential,
66
hasNoNullableProps,
7-
logger,
87
} from '@code-pushup/utils';
98
import {
109
type CommandContext,
@@ -16,6 +15,7 @@ import {
1615
runMergeDiffs,
1716
} from './cli/index.js';
1817
import { commentOnPR } from './comment.js';
18+
import { log } from './log.js';
1919
import type {
2020
GitBranch,
2121
MonorepoRunResult,
@@ -52,7 +52,7 @@ export async function runInMonorepoMode(
5252
): Promise<MonorepoRunResult> {
5353
const { api, settings } = env;
5454

55-
logger.info('Running Code PushUp in monorepo mode');
55+
log('info', 'Running Code PushUp in monorepo mode');
5656

5757
const { projects, runManyCommand } = await listMonorepoProjects(settings);
5858
const projectResults = runManyCommand
@@ -71,7 +71,7 @@ export async function runInMonorepoMode(
7171
diffJsonPaths,
7272
createCommandContext(settings, projects[0]),
7373
);
74-
logger.debug(`Merged ${diffJsonPaths.length} diffs into ${tmpDiffPath}`);
74+
log('debug', `Merged ${diffJsonPaths.length} diffs into ${tmpDiffPath}`);
7575
const { md: diffPath } = await saveOutputFiles({
7676
project: null,
7777
type: 'comparison',
@@ -105,7 +105,7 @@ function runProjectsIndividually(
105105
projects: ProjectConfig[],
106106
env: RunEnv,
107107
): Promise<ProjectRunResult[]> {
108-
logger.info(`Running on ${projects.length} projects individually`);
108+
log('info', `Running on ${projects.length} projects individually`);
109109
return asyncSequential(projects, project => runOnProject(project, env));
110110
}
111111

@@ -119,7 +119,8 @@ async function runProjectsInBulk(
119119
settings,
120120
} = env;
121121

122-
logger.info(
122+
log(
123+
'info',
123124
`Running on ${projects.length} projects in bulk (parallel: ${settings.parallel})`,
124125
);
125126

@@ -173,7 +174,8 @@ async function loadProjectEnvs(
173174

174175
const hasFormats = allProjectsHaveDefaultPersistFormats(projectEnvs);
175176

176-
logger.debug(
177+
log(
178+
'debug',
177179
[
178180
configPatterns
179181
? `Parsed ${projectEnvs.length} persist and upload configs by interpolating configPatterns option for each project.`
@@ -211,7 +213,8 @@ async function compareProjectsInBulk(
211213
const uncachedProjectReports = projectReportsWithCache.filter(
212214
({ prevReport }) => !prevReport,
213215
);
214-
logger.info(
216+
log(
217+
'info',
215218
`${currProjectReports.length - uncachedProjectReports.length} out of ${currProjectReports.length} projects loaded previous report from artifact cache`,
216219
);
217220

@@ -314,18 +317,21 @@ async function collectPreviousReports(
314317
.map(({ project }) => project.name)
315318
.filter(name => !onlyProjects.includes(name));
316319
if (invalidProjects.length > 0) {
317-
logger.debug(
320+
log(
321+
'debug',
318322
`Printing config failed for ${invalidProjects.length} projects - ${invalidProjects.join(', ')}`,
319323
);
320-
logger.info(
324+
log(
325+
'info',
321326
`Skipping ${invalidProjects.length} projects which aren't configured in base branch ${base.ref}`,
322327
);
323328
}
324329

325330
if (onlyProjects.length > 0) {
326331
const hasFormats =
327332
allProjectsHaveDefaultPersistFormats(validProjectConfigs);
328-
logger.info(
333+
log(
334+
'info',
329335
`Collecting previous reports for ${onlyProjects.length} projects`,
330336
);
331337
await collectMany(runManyCommand, env, { hasFormats, onlyProjects });
@@ -377,7 +383,7 @@ async function collectMany(
377383
const countText = onlyProjects
378384
? `${onlyProjects.length} previous`
379385
: 'all current';
380-
logger.debug(`Collected ${countText} reports using command \`${command}\``);
386+
log('debug', `Collected ${countText} reports using command \`${command}\``);
381387
}
382388

383389
async function compareMany(
@@ -397,7 +403,7 @@ async function compareMany(
397403

398404
await runCompare(ctx, { hasFormats });
399405

400-
logger.debug('Compared all project reports');
406+
log('debug', 'Compared all project reports');
401407
}
402408

403409
export function allProjectsHaveDefaultPersistFormats(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { logger } from '@code-pushup/utils';
21
import { commentOnPR } from './comment.js';
2+
import { log } from './log.js';
33
import type { StandaloneRunResult } from './models.js';
44
import { type RunEnv, runOnProject } from './run-utils.js';
55

@@ -8,7 +8,7 @@ export async function runInStandaloneMode(
88
): Promise<StandaloneRunResult> {
99
const { api, settings } = env;
1010

11-
logger.info('Running Code PushUp in standalone project mode');
11+
log('info', 'Running Code PushUp in standalone project mode');
1212

1313
const { files, newIssues } = await runOnProject(null, env);
1414

0 commit comments

Comments
 (0)