Skip to content

Commit f9deac7

Browse files
committed
perf(ci): use bulk command to compare reports for all projects
1 parent 2adcf9d commit f9deac7

File tree

4 files changed

+168
-104
lines changed

4 files changed

+168
-104
lines changed

packages/ci/src/lib/cli/commands/compare.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,19 @@ import { DEFAULT_PERSIST_FORMAT } from '@code-pushup/models';
22
import { executeProcess, isVerbose } from '@code-pushup/utils';
33
import type { CommandContext } from '../context.js';
44

5-
type CompareOptions = {
6-
before: string;
7-
after: string;
8-
label?: string;
9-
};
10-
115
export async function runCompare(
12-
{ before, after, label }: CompareOptions,
136
{ bin, config, directory, observer }: CommandContext,
7+
{ hasFormats }: { hasFormats: boolean },
148
): Promise<void> {
159
await executeProcess({
1610
command: bin,
1711
args: [
1812
'compare',
1913
...(isVerbose() ? ['--verbose'] : []),
20-
`--before=${before}`,
21-
`--after=${after}`,
22-
...(label ? [`--label=${label}`] : []),
2314
...(config ? [`--config=${config}`] : []),
24-
...DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`),
15+
...(hasFormats
16+
? []
17+
: DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`)),
2518
],
2619
cwd: directory,
2720
observer,

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

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
createCommandContext,
1212
persistedFilesFromConfig,
1313
runCollect,
14+
runCompare,
1415
runMergeDiffs,
1516
} from './cli/index.js';
1617
import { commentOnPR } from './comment.js';
@@ -29,16 +30,18 @@ import {
2930
import { saveOutputFiles } from './output-files.js';
3031
import {
3132
type BaseReportArgs,
33+
type CompareReportsArgs,
3234
type ReportData,
3335
type RunEnv,
3436
checkPrintConfig,
35-
compareReports,
3637
configFromPatterns,
3738
hasDefaultPersistFormats,
3839
loadCachedBaseReport,
40+
prepareReportFilesToCompare,
3941
printPersistConfig,
4042
runInBaseBranch,
4143
runOnProject,
44+
saveDiffFiles,
4245
saveReportFiles,
4346
} from './run-utils.js';
4447

@@ -226,18 +229,38 @@ async function compareProjectsInBulk(
226229
...args,
227230
prevReport: args.prevReport || collectedPrevReports[args.project.name],
228231
}))
229-
.filter(hasNoNullableProps);
232+
.filter(hasNoNullableProps) satisfies CompareReportsArgs[];
230233

231-
const projectComparisons = Object.fromEntries(
232-
await asyncSequential(projectsToCompare, async args => [
233-
args.project.name,
234-
await compareReports(args),
235-
]),
234+
const projectComparisons = await compareManyProjects(
235+
projectsToCompare,
236+
runManyCommand,
237+
env,
236238
);
237239

238240
return finalizeProjectReports(currProjectReports, projectComparisons);
239241
}
240242

243+
async function compareManyProjects(
244+
projectsToCompare: ExcludeNullableProps<CompareReportsArgs>[],
245+
runManyCommand: RunManyCommand,
246+
env: RunEnv,
247+
): Promise<Record<string, ProjectRunResult>> {
248+
await Promise.all(projectsToCompare.map(prepareReportFilesToCompare));
249+
250+
await compareMany(runManyCommand, env, {
251+
hasFormats: allProjectsHaveDefaultPersistFormats(projectsToCompare),
252+
});
253+
254+
return Object.fromEntries(
255+
await Promise.all(
256+
projectsToCompare.map(async args => [
257+
args.project.name,
258+
await saveDiffFiles(args),
259+
]),
260+
),
261+
);
262+
}
263+
241264
function finalizeProjectReports(
242265
projectReports: ProjectReport[],
243266
projectComparisons?: Record<string, ProjectRunResult>,
@@ -352,6 +375,26 @@ async function collectMany(
352375
);
353376
}
354377

378+
async function compareMany(
379+
runManyCommand: RunManyCommand,
380+
env: RunEnv,
381+
options: {
382+
hasFormats: boolean;
383+
},
384+
): Promise<void> {
385+
const { settings } = env;
386+
const { hasFormats } = options;
387+
388+
const ctx: CommandContext = {
389+
...createCommandContext(settings, null),
390+
bin: await runManyCommand(),
391+
};
392+
393+
await runCompare(ctx, { hasFormats });
394+
395+
settings.logger.debug('Compared all project reports');
396+
}
397+
355398
export function allProjectsHaveDefaultPersistFormats(
356399
projects: { config: EnhancedPersistConfig }[],
357400
): boolean {

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

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
/* eslint-disable max-lines */
2-
import { readFile } from 'node:fs/promises';
2+
import { readFile, writeFile } from 'node:fs/promises';
3+
import path from 'node:path';
34
import type { SimpleGit } from 'simple-git';
45
import {
6+
DEFAULT_PERSIST_FILENAME,
57
DEFAULT_PERSIST_FORMAT,
8+
DEFAULT_PERSIST_OUTPUT_DIR,
69
type Report,
710
type ReportsDiff,
811
} from '@code-pushup/models';
912
import {
13+
type Diff,
14+
createReportPath,
1015
interpolate,
16+
objectFromEntries,
17+
readJsonFile,
1118
removeUndefinedAndEmptyProps,
1219
stringifyError,
1320
} from '@code-pushup/utils';
@@ -53,6 +60,7 @@ type NormalizedGitRefs = {
5360
export type CompareReportsArgs = {
5461
project: ProjectConfig | null;
5562
env: RunEnv;
63+
ctx: CommandContext;
5664
base: GitBranch;
5765
currReport: ReportData<'current'>;
5866
prevReport: ReportData<'previous'>;
@@ -152,38 +160,91 @@ export async function runOnProject(
152160
return noDiffOutput;
153161
}
154162

155-
const compareArgs = { project, env, base, config, currReport, prevReport };
163+
const compareArgs = { ...baseArgs, currReport, prevReport };
156164
return compareReports(compareArgs);
157165
}
158166

159167
export async function compareReports(
160168
args: CompareReportsArgs,
161169
): Promise<ProjectRunResult> {
170+
const { ctx, env, config } = args;
171+
const { logger } = env.settings;
172+
173+
await prepareReportFilesToCompare(args);
174+
await runCompare(ctx, { hasFormats: hasDefaultPersistFormats(config) });
175+
176+
logger.info('Compared reports and generated diff files');
177+
178+
return saveDiffFiles(args);
179+
}
180+
181+
export async function prepareReportFilesToCompare(
182+
args: CompareReportsArgs,
183+
): Promise<Diff<string>> {
184+
const { config, project, env, ctx } = args;
185+
const {
186+
outputDir = DEFAULT_PERSIST_OUTPUT_DIR,
187+
filename = DEFAULT_PERSIST_FILENAME,
188+
} = config.persist ?? {};
189+
const label = project?.name;
190+
const { logger } = env.settings;
191+
192+
const originalReports = await Promise.all(
193+
[args.currReport, args.prevReport].map(({ files }) =>
194+
readJsonFile<Report>(files.json),
195+
),
196+
);
197+
const labeledReports = label
198+
? originalReports.map(report => ({ ...report, label }))
199+
: originalReports;
200+
201+
const reportPaths = labeledReports.map((report, idx) => {
202+
const key: keyof Diff<string> = idx === 0 ? 'after' : 'before';
203+
const filePath = createReportPath({
204+
outputDir: path.resolve(ctx.directory, outputDir),
205+
filename,
206+
format: 'json',
207+
suffix: key,
208+
});
209+
return { key, report, filePath };
210+
});
211+
212+
await Promise.all(
213+
reportPaths.map(({ filePath, report }) =>
214+
writeFile(filePath, JSON.stringify(report, null, 2)),
215+
),
216+
);
217+
218+
logger.debug(
219+
[
220+
'Prepared',
221+
project && `"${project.name}" project's`,
222+
'report files for comparison',
223+
`at ${reportPaths.map(({ filePath }) => filePath).join(' and ')}`,
224+
]
225+
.filter(Boolean)
226+
.join(' '),
227+
);
228+
229+
return objectFromEntries(
230+
reportPaths.map(({ key, filePath }) => [key, filePath]),
231+
);
232+
}
233+
234+
export async function saveDiffFiles(args: CompareReportsArgs) {
162235
const {
163236
project,
237+
ctx,
164238
env: { settings },
165239
currReport,
166240
prevReport,
167241
config,
168242
} = args;
169-
const logger = settings.logger;
170-
171-
const ctx = createCommandContext(settings, project);
172243

173-
await runCompare(
174-
{
175-
before: prevReport.files.json,
176-
after: currReport.files.json,
177-
label: project?.name,
178-
},
179-
ctx,
180-
);
181244
const diffFiles = persistedFilesFromConfig(config, {
182245
directory: ctx.directory,
183246
isDiff: true,
184247
});
185-
logger.info('Compared reports and generated diff files');
186-
logger.debug(`Generated diff files at ${diffFiles.json} and ${diffFiles.md}`);
187248

188249
return {
189250
name: projectToName(project),

0 commit comments

Comments
 (0)