Skip to content

Commit 5965900

Browse files
committed
feat(cli): include commit info in report.json
1 parent 66949c2 commit 5965900

25 files changed

+235
-173
lines changed

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ describe('CLI collect', () => {
1414
duration,
1515
version,
1616
...report
17-
}: Report | PluginReport) => report;
18-
/* eslint-enable @typescript-eslint/no-unused-vars */
19-
20-
const omitVariableReportData = (report: Report) =>
17+
}: Omit<Report, 'commit'> | PluginReport) => report;
18+
const omitVariableReportData = ({ commit, ...report }: Report) =>
2119
omitVariableData({
2220
...report,
2321
plugins: report.plugins.map(omitVariableData) as PluginReport[],
2422
});
23+
/* eslint-enable @typescript-eslint/no-unused-vars */
2524

2625
beforeEach(async () => {
2726
await cleanTestFolder('tmp/e2e');

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
collectAndPersistReports,
77
upload,
88
} from '@code-pushup/core';
9-
import { getLatestCommit, validateCommitData } from '@code-pushup/utils';
109
import { CLI_NAME } from '../constants';
1110
import {
1211
collectSuccessfulLog,
@@ -50,10 +49,7 @@ export function yargsAutorunCommandObject() {
5049

5150
if (options.upload) {
5251
const { url } = await upload(options);
53-
const commitData = await getLatestCommit();
54-
if (validateCommitData(commitData, { throwError: true })) {
55-
uploadSuccessfulLog(url);
56-
}
52+
uploadSuccessfulLog(url);
5753
} else {
5854
ui().logger.warning('Upload skipped because configuration is not set.');
5955
renderIntegratePortalHint();

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

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import chalk from 'chalk';
22
import { ArgumentsCamelCase, CommandModule } from 'yargs';
33
import { UploadOptions, upload } from '@code-pushup/core';
4-
import { getLatestCommit, validateCommitData } from '@code-pushup/utils';
54
import { CLI_NAME } from '../constants';
65
import {
76
renderIntegratePortalHint,
@@ -24,11 +23,7 @@ export function yargsUploadCommandObject() {
2423
throw new Error('Upload configuration not set');
2524
}
2625
const { url } = await upload(options);
27-
28-
const commitData = await getLatestCommit();
29-
if (validateCommitData(commitData, { throwError: true })) {
30-
uploadSuccessfulLog(url);
31-
}
26+
uploadSuccessfulLog(url);
3227
},
3328
} satisfies CommandModule;
3429
}

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

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
MINIMAL_CONFIG_MOCK,
55
MINIMAL_REPORT_MOCK,
66
} from '@code-pushup/test-utils';
7-
import { collectAndPersistReports } from './collect-and-persist';
7+
import {
8+
CollectAndPersistReportsOptions,
9+
collectAndPersistReports,
10+
} from './collect-and-persist';
811
import { collect } from './implementation/collect';
912
import { logPersistedResults, persistReport } from './implementation/persist';
1013

@@ -19,7 +22,8 @@ vi.mock('./implementation/persist', () => ({
1922

2023
describe('collectAndPersistReports', () => {
2124
it('should call collect and persistReport with correct parameters in non-verbose mode', async () => {
22-
const nonVerboseConfig = {
25+
const nonVerboseConfig: CollectAndPersistReportsOptions = {
26+
categories: [],
2327
...MINIMAL_CONFIG_MOCK,
2428
persist: {
2529
outputDir: 'output',
@@ -33,12 +37,15 @@ describe('collectAndPersistReports', () => {
3337

3438
expect(collect).toHaveBeenCalledWith(nonVerboseConfig);
3539

36-
expect(persistReport).toHaveBeenCalledWith(
40+
expect(persistReport).toHaveBeenCalledWith<
41+
Parameters<typeof persistReport>
42+
>(
3743
{
3844
packageName: '@code-pushup/core',
3945
version: '0.0.1',
4046
date: expect.stringMatching(ISO_STRING_REGEXP),
4147
duration: 666,
48+
commit: expect.any(Object),
4249
categories: expect.any(Array),
4350
plugins: expect.any(Array),
4451
},
@@ -53,7 +60,8 @@ describe('collectAndPersistReports', () => {
5360
});
5461

5562
it('should call collect and persistReport with correct parameters in verbose mode', async () => {
56-
const verboseConfig = {
63+
const verboseConfig: CollectAndPersistReportsOptions = {
64+
categories: [],
5765
...MINIMAL_CONFIG_MOCK,
5866
persist: {
5967
outputDir: 'output',

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

+4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { vol } from 'memfs';
22
import { describe, expect, it } from 'vitest';
3+
import { commitSchema } from '@code-pushup/models';
34
import { MEMFS_VOLUME, MINIMAL_CONFIG_MOCK } from '@code-pushup/test-utils';
45
import { collect } from './collect';
56

67
describe('collect', () => {
78
it('should execute with valid options', async () => {
89
vol.fromJSON({}, MEMFS_VOLUME);
910
const report = await collect({
11+
categories: [],
1012
...MINIMAL_CONFIG_MOCK,
1113
verbose: true,
1214
progress: false,
@@ -27,5 +29,7 @@ describe('collect', () => {
2729
},
2830
}),
2931
);
32+
33+
expect(() => commitSchema.parse(report.commit)).not.toThrow();
3034
});
3135
});

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CoreConfig, Report } from '@code-pushup/models';
2-
import { calcDuration } from '@code-pushup/utils';
2+
import { calcDuration, getLatestCommit } from '@code-pushup/utils';
33
import { name, version } from '../../../package.json';
44
import { GlobalOptions } from '../types';
55
import { executePlugins } from './execute-plugin';
@@ -17,8 +17,10 @@ export async function collect(options: CollectOptions): Promise<Report> {
1717
const { plugins, categories } = options;
1818
const date = new Date().toISOString();
1919
const start = performance.now();
20+
const commit = await getLatestCommit();
2021
const pluginOutputs = await executePlugins(plugins, options);
2122
return {
23+
commit,
2224
packageName: name,
2325
version,
2426
date,

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

+14-20
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import {
66
directoryExists,
77
generateMdReport,
88
generateStdoutSummary,
9-
getLatestCommit,
109
logMultipleFileResults,
1110
scoreReport,
1211
sortReport,
13-
validateCommitData,
1412
} from '@code-pushup/utils';
1513

1614
export class PersistDirError extends Error {
@@ -35,24 +33,20 @@ export async function persistReport(
3533
console.info(generateStdoutSummary(sortedScoredReport));
3634

3735
// collect physical format outputs
38-
const results = await Promise.all(
39-
format.map(async reportType => {
40-
switch (reportType) {
41-
case 'json':
42-
return {
43-
format: 'json',
44-
content: JSON.stringify(report, null, 2),
45-
};
46-
case 'md':
47-
const commitData = await getLatestCommit();
48-
validateCommitData(commitData);
49-
return {
50-
format: 'md',
51-
content: generateMdReport(sortedScoredReport, commitData),
52-
};
53-
}
54-
}),
55-
);
36+
const results = format.map(reportType => {
37+
switch (reportType) {
38+
case 'json':
39+
return {
40+
format: 'json',
41+
content: JSON.stringify(report, null, 2),
42+
};
43+
case 'md':
44+
return {
45+
format: 'md',
46+
content: generateMdReport(sortedScoredReport),
47+
};
48+
}
49+
});
5650

5751
if (!(await directoryExists(outputDir))) {
5852
try {

packages/core/src/lib/upload.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
uploadToPortal,
44
} from '@code-pushup/portal-client';
55
import { PersistConfig, Report, UploadConfig } from '@code-pushup/models';
6-
import { getLatestCommit, loadReport } from '@code-pushup/utils';
6+
import { loadReport } from '@code-pushup/utils';
77
import { reportToGQL } from './implementation/report-to-gql';
88
import { GlobalOptions } from './types';
99

@@ -27,15 +27,14 @@ export async function upload(
2727
...options.persist,
2828
format: 'json',
2929
});
30-
const commitData = await getLatestCommit();
31-
if (!commitData) {
32-
throw new Error('no commit data available');
30+
if (!report.commit) {
31+
throw new Error('Commit must be linked in order to upload report');
3332
}
3433

3534
const data: SaveReportMutationVariables = {
3635
organization,
3736
project,
38-
commit: commitData.hash,
37+
commit: report.commit.hash,
3938
...reportToGQL(report),
4039
};
4140

packages/models/docs/models-reference.md

+24-8
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ _Object containing the following properties:_
9090

9191
_(\*) Required._
9292

93+
## Commit
94+
95+
Git commit
96+
97+
_Object containing the following properties:_
98+
99+
| Property | Description | Type |
100+
| :----------------- | :------------------------------------- | :------------------------------------ |
101+
| **`hash`** (\*) | Commit SHA (full) | `string` (_regex: `/^[\da-f]{40}$/`_) |
102+
| **`message`** (\*) | Commit message | `string` |
103+
| **`date`** (\*) | Date and time when commit was authored | `Date` (_nullable_) |
104+
| **`author`** (\*) | Commit author name | `string` |
105+
106+
_(\*) Required._
107+
93108
## CoreConfig
94109

95110
_Object containing the following properties:_
@@ -1095,14 +1110,15 @@ _(\*) Required._
10951110

10961111
_Object containing the following properties:_
10971112

1098-
| Property | Description | Type |
1099-
| :--------------------- | :------------------------------------- | :-------------------------------------------------------- |
1100-
| **`packageName`** (\*) | NPM package name | `string` |
1101-
| **`version`** (\*) | NPM version of the CLI | `string` |
1102-
| **`date`** (\*) | Start date and time of the collect run | `string` |
1103-
| **`duration`** (\*) | Duration of the collect run in ms | `number` |
1104-
| **`categories`** (\*) | | _Array of [CategoryConfig](#categoryconfig) items_ |
1105-
| **`plugins`** (\*) | | _Array of at least 1 [PluginReport](#pluginreport) items_ |
1113+
| Property | Description | Type |
1114+
| :--------------------- | :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1115+
| **`packageName`** (\*) | NPM package name | `string` |
1116+
| **`version`** (\*) | NPM version of the CLI | `string` |
1117+
| **`date`** (\*) | Start date and time of the collect run | `string` |
1118+
| **`duration`** (\*) | Duration of the collect run in ms | `number` |
1119+
| **`categories`** (\*) | | _Array of [CategoryConfig](#categoryconfig) items_ |
1120+
| **`plugins`** (\*) | | _Array of at least 1 [PluginReport](#pluginreport) items_ |
1121+
| **`commit`** (\*) | Git commit for which report was collected | _Object with properties:_<ul><li>`hash`: `string` (_regex: `/^[\da-f]{40}$/`_) - Commit SHA (full)</li><li>`message`: `string` - Commit message</li><li>`date`: `Date` (_nullable_) - Date and time when commit was authored</li><li>`author`: `string` - Commit author name</li></ul> (_nullable_) |
11061122

11071123
_(\*) Required._
11081124

packages/models/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export {
1313
categoryConfigSchema,
1414
categoryRefSchema,
1515
} from './lib/category-config';
16+
export { Commit, commitSchema } from './lib/commit';
1617
export { CoreConfig, coreConfigSchema } from './lib/core-config';
1718
export { Group, GroupRef, groupRefSchema, groupSchema } from './lib/group';
1819
export {

packages/models/src/lib/commit.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { z } from 'zod';
2+
3+
export const commitSchema = z.object(
4+
{
5+
hash: z
6+
.string({ description: 'Commit SHA (full)' })
7+
.regex(
8+
/^[\da-f]{40}$/,
9+
'Commit SHA should be a 40-character hexadecimal string',
10+
),
11+
message: z.string({ description: 'Commit message' }),
12+
date: z.coerce.date({
13+
description: 'Date and time when commit was authored',
14+
}),
15+
author: z
16+
.string({
17+
description: 'Commit author name',
18+
})
19+
.trim(),
20+
},
21+
{ description: 'Git commit' },
22+
);
23+
24+
export type Commit = z.infer<typeof commitSchema>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { type Commit, commitSchema } from './commit';
2+
3+
describe('commitSchema', () => {
4+
it('should accept valid git commit data', () => {
5+
expect(() =>
6+
commitSchema.parse({
7+
hash: 'abcdef0123456789abcdef0123456789abcdef01',
8+
message: 'Minor fixes',
9+
author: 'John Doe',
10+
date: new Date(),
11+
} satisfies Commit),
12+
).not.toThrow();
13+
});
14+
15+
it('should coerce date string into Date object', () => {
16+
expect(
17+
commitSchema.parse({
18+
hash: 'abcdef0123456789abcdef0123456789abcdef01',
19+
message: 'Minor fixes',
20+
author: 'John Doe',
21+
date: '2024-03-06T17:30:12+01:00',
22+
}),
23+
).toEqual<Commit>({
24+
hash: 'abcdef0123456789abcdef0123456789abcdef01',
25+
message: 'Minor fixes',
26+
author: 'John Doe',
27+
date: new Date('2024-03-06T17:30:12+01:00'),
28+
});
29+
});
30+
31+
it('should throw for invalid hash', () => {
32+
expect(() =>
33+
commitSchema.parse({
34+
hash: '12345678', // too short
35+
message: 'Minor fixes',
36+
author: 'John Doe',
37+
date: new Date(),
38+
} satisfies Commit),
39+
).toThrow('Commit SHA should be a 40-character hexadecimal string');
40+
});
41+
});

packages/models/src/lib/report.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { z } from 'zod';
22
import { auditSchema } from './audit';
33
import { auditOutputSchema } from './audit-output';
44
import { categoryConfigSchema } from './category-config';
5+
import { commitSchema } from './commit';
56
import { Group, groupSchema } from './group';
67
import {
78
executionMetaSchema,
@@ -76,6 +77,9 @@ export const reportSchema = packageVersionSchema({
7677
{
7778
categories: z.array(categoryConfigSchema),
7879
plugins: z.array(pluginReportSchema).min(1),
80+
commit: commitSchema
81+
.describe('Git commit for which report was collected')
82+
.nullable(),
7983
},
8084
{ description: 'Collect output data' },
8185
),

packages/models/src/lib/report.unit.test.ts

+7
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ describe('reportSchema', () => {
183183
title: 'Bug prevention',
184184
},
185185
],
186+
commit: {
187+
hash: 'abcdef0123456789abcdef0123456789abcdef01',
188+
message: 'Minor fixes',
189+
author: 'John Doe',
190+
date: new Date('2024-01-07T09:15:00.000Z'),
191+
},
186192
date: '2024-01-07T09:30:00.000Z',
187193
duration: 600,
188194
plugins: [
@@ -235,6 +241,7 @@ describe('reportSchema', () => {
235241
title: 'Bug prevention',
236242
},
237243
],
244+
commit: null,
238245
date: '2024-01-07T09:30:00.000Z',
239246
duration: 600,
240247
plugins: [

packages/utils/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export {
3939
getGitRoot,
4040
getLatestCommit,
4141
toGitPath,
42-
validateCommitData,
4342
} from './lib/git';
4443
export { groupByStatus } from './lib/group-by-status';
4544
export {

0 commit comments

Comments
 (0)