diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..47b928e0f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,16 @@ +# Testing +*.test.ts @Tlacenka +/e2e/ @Tlacenka +/testing/ @Tlacenka +vite.config*.ts @Tlacenka + +# Mocks +mocks/ @Tlacenka +/examples/react-todos-app/ @matejchalk +/testing/test-utils/src/lib/utils/dynamic-mocks/ @matejchalk + +# Plugins +/packages/plugin-eslint/ @matejchalk +/packages/plugin-coverage/ @Tlacenka +/packages/plugin-lighthouse/ @BioPhoton +/examples/plugins/ @BioPhoton diff --git a/.github/labeler.yml b/.github/labeler.yml index 8637933ac..910d00f7b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -37,7 +37,7 @@ - '**/mocks/**' - e2e/** - testing/** - - '**/vitest.config*.ts' + - '**/vite.config*.ts' - 'vitest.workspace.ts' 🦾 CI/CD: diff --git a/.github/workflows/pr-title-commitlint-check.yml b/.github/workflows/pr-commitlint.yml similarity index 88% rename from .github/workflows/pr-title-commitlint-check.yml rename to .github/workflows/pr-commitlint.yml index 8178f829e..c29b1c310 100644 --- a/.github/workflows/pr-title-commitlint-check.yml +++ b/.github/workflows/pr-commitlint.yml @@ -1,4 +1,4 @@ -name: PR Title Commitlint Check +name: PR Commitlint on: pull_request: @@ -6,6 +6,7 @@ on: jobs: check: + name: Check PR title runs-on: ubuntu-latest steps: - name: Checkout the repository diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index e71dd0c21..d2eb58b9a 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -6,6 +6,7 @@ on: jobs: label: + name: Add PR labels runs-on: ubuntu-latest permissions: pull-requests: write diff --git a/CHANGELOG.md b/CHANGELOG.md index b9248b133..8e2b00b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +## [0.26.1](https://github.com/code-pushup/cli/compare/v0.26.0...v0.26.1) (2024-03-07) + +# [0.26.0](https://github.com/code-pushup/cli/compare/v0.25.7...v0.26.0) (2024-03-06) + +### Features + +- **cli:** include commit info in report.json ([5965900](https://github.com/code-pushup/cli/commit/596590082c76b0b7915b2b339fa27baee9eaa678)) + +## [0.25.7](https://github.com/code-pushup/cli/compare/v0.25.6...v0.25.7) (2024-03-06) + ## [0.25.6](https://github.com/code-pushup/cli/compare/v0.25.5...v0.25.6) (2024-03-05) ### Bug Fixes diff --git a/e2e/cli-e2e/tests/collect.e2e.test.ts b/e2e/cli-e2e/tests/collect.e2e.test.ts index de65cbba0..9cf95068b 100644 --- a/e2e/cli-e2e/tests/collect.e2e.test.ts +++ b/e2e/cli-e2e/tests/collect.e2e.test.ts @@ -14,14 +14,13 @@ describe('CLI collect', () => { duration, version, ...report - }: Report | PluginReport) => report; - /* eslint-enable @typescript-eslint/no-unused-vars */ - - const omitVariableReportData = (report: Report) => + }: Omit | PluginReport) => report; + const omitVariableReportData = ({ commit, ...report }: Report) => omitVariableData({ ...report, plugins: report.plugins.map(omitVariableData) as PluginReport[], }); + /* eslint-enable @typescript-eslint/no-unused-vars */ beforeEach(async () => { await cleanTestFolder('tmp/e2e'); @@ -37,7 +36,7 @@ describe('CLI collect', () => { expect(code).toBe(0); expect(stderr).toBe(''); - const report = await readJsonFile('tmp/react-todos-app/report.json'); + const report = await readJsonFile('tmp/e2e/react-todos-app/report.json'); expect(() => reportSchema.parse(report)).not.toThrow(); expect(omitVariableReportData(report as Report)).toMatchSnapshot(); @@ -90,7 +89,7 @@ describe('CLI collect', () => { expect(code).toBe(0); expect(stderr).toBe(''); - const report = await readJsonFile('tmp/react-todos-app/report.json'); + const report = await readJsonFile('tmp/e2e/react-todos-app/report.json'); expect(() => reportSchema.parse(report)).not.toThrow(); expect(omitVariableReportData(report as Report)).toMatchSnapshot(); @@ -106,7 +105,7 @@ describe('CLI collect', () => { expect(code).toBe(0); expect(stderr).toBe(''); - const md = await readTextFile('tmp/react-todos-app/report.md'); + const md = await readTextFile('tmp/e2e/react-todos-app/report.md'); expect(md).toContain('# Code PushUp Report'); expect(md).toContain(exampleCategoryTitle); diff --git a/examples/react-todos-app/code-pushup.config.js b/examples/react-todos-app/code-pushup.config.js index e8d9fa266..a1fb2d2b4 100644 --- a/examples/react-todos-app/code-pushup.config.js +++ b/examples/react-todos-app/code-pushup.config.js @@ -10,7 +10,7 @@ const eslintAuditRef = (slug, weight) => ({ export default { persist: { - outputDir: '../../tmp/react-todos-app', + outputDir: '../../tmp/e2e/react-todos-app', }, plugins: [ await coveragePlugin({ diff --git a/global-setup.e2e.ts b/global-setup.e2e.ts index db63a9a89..99e61b3f5 100644 --- a/global-setup.e2e.ts +++ b/global-setup.e2e.ts @@ -1,9 +1,6 @@ import { execSync } from 'child_process'; -import { - setup as globalSetup, - teardown as globalTeardown, -} from './global-setup'; -import { setupTestFolder } from './testing/test-setup/src'; +import { setup as globalSetup } from './global-setup'; +import { setupTestFolder, teardownTestFolder } from './testing/test-setup/src'; import startLocalRegistry from './tools/scripts/start-local-registry'; import stopLocalRegistry from './tools/scripts/stop-local-registry'; @@ -21,5 +18,6 @@ export async function teardown() { execSync('npm uninstall @code-pushup/cli'); execSync('npm uninstall @code-pushup/eslint-plugin'); execSync('npm uninstall @code-pushup/coverage-plugin'); - await globalTeardown(); + await teardownTestFolder('tmp/e2e'); + await teardownTestFolder('tmp/local-registry'); } diff --git a/global-setup.ts b/global-setup.ts index da497a9fa..65522d48f 100644 --- a/global-setup.ts +++ b/global-setup.ts @@ -1,9 +1,3 @@ -import { teardownTestFolder } from './testing/test-setup/src'; - export async function setup() { process.env.TZ = 'UTC'; } - -export async function teardown() { - await teardownTestFolder('tmp'); -} diff --git a/package.json b/package.json index 013169822..2e7933f7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/cli-source", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "homepage": "https://github.com/code-pushup/cli#readme", "bugs": { diff --git a/packages/cli/package.json b/packages/cli/package.json index e47f972ec..9f4b998e5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/cli", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "bin": { "code-pushup": "index.js" diff --git a/packages/cli/src/lib/autorun/autorun-command.ts b/packages/cli/src/lib/autorun/autorun-command.ts index 9204a560e..d0dc4f139 100644 --- a/packages/cli/src/lib/autorun/autorun-command.ts +++ b/packages/cli/src/lib/autorun/autorun-command.ts @@ -6,7 +6,6 @@ import { collectAndPersistReports, upload, } from '@code-pushup/core'; -import { getLatestCommit, validateCommitData } from '@code-pushup/utils'; import { CLI_NAME } from '../constants'; import { collectSuccessfulLog, @@ -50,10 +49,7 @@ export function yargsAutorunCommandObject() { if (options.upload) { const { url } = await upload(options); - const commitData = await getLatestCommit(); - if (validateCommitData(commitData, { throwError: true })) { - uploadSuccessfulLog(url); - } + uploadSuccessfulLog(url); } else { ui().logger.warning('Upload skipped because configuration is not set.'); renderIntegratePortalHint(); diff --git a/packages/cli/src/lib/upload/upload-command.ts b/packages/cli/src/lib/upload/upload-command.ts index 6382c9f14..79d73b431 100644 --- a/packages/cli/src/lib/upload/upload-command.ts +++ b/packages/cli/src/lib/upload/upload-command.ts @@ -1,7 +1,6 @@ import chalk from 'chalk'; import { ArgumentsCamelCase, CommandModule } from 'yargs'; import { UploadOptions, upload } from '@code-pushup/core'; -import { getLatestCommit, validateCommitData } from '@code-pushup/utils'; import { CLI_NAME } from '../constants'; import { renderIntegratePortalHint, @@ -24,11 +23,7 @@ export function yargsUploadCommandObject() { throw new Error('Upload configuration not set'); } const { url } = await upload(options); - - const commitData = await getLatestCommit(); - if (validateCommitData(commitData, { throwError: true })) { - uploadSuccessfulLog(url); - } + uploadSuccessfulLog(url); }, } satisfies CommandModule; } diff --git a/packages/core/package.json b/packages/core/package.json index 01bc8abfc..2a228c939 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/core", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "dependencies": { "@code-pushup/models": "*", diff --git a/packages/core/src/lib/collect-and-persist.unit.test.ts b/packages/core/src/lib/collect-and-persist.unit.test.ts index cb664ee8c..93ffb41e2 100644 --- a/packages/core/src/lib/collect-and-persist.unit.test.ts +++ b/packages/core/src/lib/collect-and-persist.unit.test.ts @@ -4,7 +4,10 @@ import { MINIMAL_CONFIG_MOCK, MINIMAL_REPORT_MOCK, } from '@code-pushup/test-utils'; -import { collectAndPersistReports } from './collect-and-persist'; +import { + CollectAndPersistReportsOptions, + collectAndPersistReports, +} from './collect-and-persist'; import { collect } from './implementation/collect'; import { logPersistedResults, persistReport } from './implementation/persist'; @@ -19,7 +22,8 @@ vi.mock('./implementation/persist', () => ({ describe('collectAndPersistReports', () => { it('should call collect and persistReport with correct parameters in non-verbose mode', async () => { - const nonVerboseConfig = { + const nonVerboseConfig: CollectAndPersistReportsOptions = { + categories: [], ...MINIMAL_CONFIG_MOCK, persist: { outputDir: 'output', @@ -33,12 +37,15 @@ describe('collectAndPersistReports', () => { expect(collect).toHaveBeenCalledWith(nonVerboseConfig); - expect(persistReport).toHaveBeenCalledWith( + expect(persistReport).toHaveBeenCalledWith< + Parameters + >( { packageName: '@code-pushup/core', version: '0.0.1', date: expect.stringMatching(ISO_STRING_REGEXP), duration: 666, + commit: expect.any(Object), categories: expect.any(Array), plugins: expect.any(Array), }, @@ -53,7 +60,8 @@ describe('collectAndPersistReports', () => { }); it('should call collect and persistReport with correct parameters in verbose mode', async () => { - const verboseConfig = { + const verboseConfig: CollectAndPersistReportsOptions = { + categories: [], ...MINIMAL_CONFIG_MOCK, persist: { outputDir: 'output', diff --git a/packages/core/src/lib/implementation/collect.integration.test.ts b/packages/core/src/lib/implementation/collect.integration.test.ts index 4bf90ec82..a0437f16f 100644 --- a/packages/core/src/lib/implementation/collect.integration.test.ts +++ b/packages/core/src/lib/implementation/collect.integration.test.ts @@ -1,5 +1,6 @@ import { vol } from 'memfs'; import { describe, expect, it } from 'vitest'; +import { commitSchema } from '@code-pushup/models'; import { MEMFS_VOLUME, MINIMAL_CONFIG_MOCK } from '@code-pushup/test-utils'; import { collect } from './collect'; @@ -7,6 +8,7 @@ describe('collect', () => { it('should execute with valid options', async () => { vol.fromJSON({}, MEMFS_VOLUME); const report = await collect({ + categories: [], ...MINIMAL_CONFIG_MOCK, verbose: true, progress: false, @@ -27,5 +29,7 @@ describe('collect', () => { }, }), ); + + expect(() => commitSchema.parse(report.commit)).not.toThrow(); }); }); diff --git a/packages/core/src/lib/implementation/collect.ts b/packages/core/src/lib/implementation/collect.ts index e249cc3bb..067fd9b06 100644 --- a/packages/core/src/lib/implementation/collect.ts +++ b/packages/core/src/lib/implementation/collect.ts @@ -1,5 +1,5 @@ import { CoreConfig, Report } from '@code-pushup/models'; -import { calcDuration } from '@code-pushup/utils'; +import { calcDuration, getLatestCommit } from '@code-pushup/utils'; import { name, version } from '../../../package.json'; import { GlobalOptions } from '../types'; import { executePlugins } from './execute-plugin'; @@ -17,8 +17,10 @@ export async function collect(options: CollectOptions): Promise { const { plugins, categories } = options; const date = new Date().toISOString(); const start = performance.now(); + const commit = await getLatestCommit(); const pluginOutputs = await executePlugins(plugins, options); return { + commit, packageName: name, version, date, diff --git a/packages/core/src/lib/implementation/persist.ts b/packages/core/src/lib/implementation/persist.ts index 710605186..cc6f3361f 100644 --- a/packages/core/src/lib/implementation/persist.ts +++ b/packages/core/src/lib/implementation/persist.ts @@ -6,11 +6,9 @@ import { directoryExists, generateMdReport, generateStdoutSummary, - getLatestCommit, logMultipleFileResults, scoreReport, sortReport, - validateCommitData, } from '@code-pushup/utils'; export class PersistDirError extends Error { @@ -35,24 +33,20 @@ export async function persistReport( console.warn(generateStdoutSummary(sortedScoredReport)); // collect physical format outputs - const results = await Promise.all( - format.map(async reportType => { - switch (reportType) { - case 'json': - return { - format: 'json', - content: JSON.stringify(report, null, 2), - }; - case 'md': - const commitData = await getLatestCommit(); - validateCommitData(commitData); - return { - format: 'md', - content: generateMdReport(sortedScoredReport, commitData), - }; - } - }), - ); + const results = format.map(reportType => { + switch (reportType) { + case 'json': + return { + format: 'json', + content: JSON.stringify(report, null, 2), + }; + case 'md': + return { + format: 'md', + content: generateMdReport(sortedScoredReport), + }; + } + }); if (!(await directoryExists(outputDir))) { try { diff --git a/packages/core/src/lib/upload.ts b/packages/core/src/lib/upload.ts index f889c9564..2144e5335 100644 --- a/packages/core/src/lib/upload.ts +++ b/packages/core/src/lib/upload.ts @@ -3,7 +3,7 @@ import { uploadToPortal, } from '@code-pushup/portal-client'; import { PersistConfig, Report, UploadConfig } from '@code-pushup/models'; -import { getLatestCommit, loadReport } from '@code-pushup/utils'; +import { loadReport } from '@code-pushup/utils'; import { reportToGQL } from './implementation/report-to-gql'; import { GlobalOptions } from './types'; @@ -27,15 +27,14 @@ export async function upload( ...options.persist, format: 'json', }); - const commitData = await getLatestCommit(); - if (!commitData) { - throw new Error('no commit data available'); + if (!report.commit) { + throw new Error('Commit must be linked in order to upload report'); } const data: SaveReportMutationVariables = { organization, project, - commit: commitData.hash, + commit: report.commit.hash, ...reportToGQL(report), }; diff --git a/packages/models/docs/models-reference.md b/packages/models/docs/models-reference.md index 5f8177843..f2683f7ce 100644 --- a/packages/models/docs/models-reference.md +++ b/packages/models/docs/models-reference.md @@ -90,6 +90,21 @@ _Object containing the following properties:_ _(\*) Required._ +## Commit + +Git commit + +_Object containing the following properties:_ + +| Property | Description | Type | +| :----------------- | :------------------------------------- | :------------------------------------ | +| **`hash`** (\*) | Commit SHA (full) | `string` (_regex: `/^[\da-f]{40}$/`_) | +| **`message`** (\*) | Commit message | `string` | +| **`date`** (\*) | Date and time when commit was authored | `Date` (_nullable_) | +| **`author`** (\*) | Commit author name | `string` | + +_(\*) Required._ + ## CoreConfig _Object containing the following properties:_ @@ -1095,14 +1110,15 @@ _(\*) Required._ _Object containing the following properties:_ -| Property | Description | Type | -| :--------------------- | :------------------------------------- | :-------------------------------------------------------- | -| **`packageName`** (\*) | NPM package name | `string` | -| **`version`** (\*) | NPM version of the CLI | `string` | -| **`date`** (\*) | Start date and time of the collect run | `string` | -| **`duration`** (\*) | Duration of the collect run in ms | `number` | -| **`categories`** (\*) | | _Array of [CategoryConfig](#categoryconfig) items_ | -| **`plugins`** (\*) | | _Array of at least 1 [PluginReport](#pluginreport) items_ | +| Property | Description | Type | +| :--------------------- | :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`packageName`** (\*) | NPM package name | `string` | +| **`version`** (\*) | NPM version of the CLI | `string` | +| **`date`** (\*) | Start date and time of the collect run | `string` | +| **`duration`** (\*) | Duration of the collect run in ms | `number` | +| **`categories`** (\*) | | _Array of [CategoryConfig](#categoryconfig) items_ | +| **`plugins`** (\*) | | _Array of at least 1 [PluginReport](#pluginreport) items_ | +| **`commit`** (\*) | Git commit for which report was collected | _Object with properties:_
  • `hash`: `string` (_regex: `/^[\da-f]{40}$/`_) - Commit SHA (full)
  • `message`: `string` - Commit message
  • `date`: `Date` (_nullable_) - Date and time when commit was authored
  • `author`: `string` - Commit author name
(_nullable_) | _(\*) Required._ diff --git a/packages/models/package.json b/packages/models/package.json index 302940f75..ac44784ed 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/models", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "dependencies": { "zod": "^3.22.1", diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 3ea344dde..798ecadf4 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -13,6 +13,7 @@ export { categoryConfigSchema, categoryRefSchema, } from './lib/category-config'; +export { Commit, commitSchema } from './lib/commit'; export { CoreConfig, coreConfigSchema } from './lib/core-config'; export { Group, GroupRef, groupRefSchema, groupSchema } from './lib/group'; export { diff --git a/packages/models/src/lib/commit.ts b/packages/models/src/lib/commit.ts new file mode 100644 index 000000000..8442b8030 --- /dev/null +++ b/packages/models/src/lib/commit.ts @@ -0,0 +1,24 @@ +import { z } from 'zod'; + +export const commitSchema = z.object( + { + hash: z + .string({ description: 'Commit SHA (full)' }) + .regex( + /^[\da-f]{40}$/, + 'Commit SHA should be a 40-character hexadecimal string', + ), + message: z.string({ description: 'Commit message' }), + date: z.coerce.date({ + description: 'Date and time when commit was authored', + }), + author: z + .string({ + description: 'Commit author name', + }) + .trim(), + }, + { description: 'Git commit' }, +); + +export type Commit = z.infer; diff --git a/packages/models/src/lib/commit.unit.test.ts b/packages/models/src/lib/commit.unit.test.ts new file mode 100644 index 000000000..2d146caf7 --- /dev/null +++ b/packages/models/src/lib/commit.unit.test.ts @@ -0,0 +1,41 @@ +import { type Commit, commitSchema } from './commit'; + +describe('commitSchema', () => { + it('should accept valid git commit data', () => { + expect(() => + commitSchema.parse({ + hash: 'abcdef0123456789abcdef0123456789abcdef01', + message: 'Minor fixes', + author: 'John Doe', + date: new Date(), + } satisfies Commit), + ).not.toThrow(); + }); + + it('should coerce date string into Date object', () => { + expect( + commitSchema.parse({ + hash: 'abcdef0123456789abcdef0123456789abcdef01', + message: 'Minor fixes', + author: 'John Doe', + date: '2024-03-06T17:30:12+01:00', + }), + ).toEqual({ + hash: 'abcdef0123456789abcdef0123456789abcdef01', + message: 'Minor fixes', + author: 'John Doe', + date: new Date('2024-03-06T17:30:12+01:00'), + }); + }); + + it('should throw for invalid hash', () => { + expect(() => + commitSchema.parse({ + hash: '12345678', // too short + message: 'Minor fixes', + author: 'John Doe', + date: new Date(), + } satisfies Commit), + ).toThrow('Commit SHA should be a 40-character hexadecimal string'); + }); +}); diff --git a/packages/models/src/lib/report.ts b/packages/models/src/lib/report.ts index 0e2c05473..248ef37b9 100644 --- a/packages/models/src/lib/report.ts +++ b/packages/models/src/lib/report.ts @@ -2,6 +2,7 @@ import { z } from 'zod'; import { auditSchema } from './audit'; import { auditOutputSchema } from './audit-output'; import { categoryConfigSchema } from './category-config'; +import { commitSchema } from './commit'; import { Group, groupSchema } from './group'; import { executionMetaSchema, @@ -76,6 +77,9 @@ export const reportSchema = packageVersionSchema({ { categories: z.array(categoryConfigSchema), plugins: z.array(pluginReportSchema).min(1), + commit: commitSchema + .describe('Git commit for which report was collected') + .nullable(), }, { description: 'Collect output data' }, ), diff --git a/packages/models/src/lib/report.unit.test.ts b/packages/models/src/lib/report.unit.test.ts index 458935147..f3205910a 100644 --- a/packages/models/src/lib/report.unit.test.ts +++ b/packages/models/src/lib/report.unit.test.ts @@ -183,6 +183,12 @@ describe('reportSchema', () => { title: 'Bug prevention', }, ], + commit: { + hash: 'abcdef0123456789abcdef0123456789abcdef01', + message: 'Minor fixes', + author: 'John Doe', + date: new Date('2024-01-07T09:15:00.000Z'), + }, date: '2024-01-07T09:30:00.000Z', duration: 600, plugins: [ @@ -235,6 +241,7 @@ describe('reportSchema', () => { title: 'Bug prevention', }, ], + commit: null, date: '2024-01-07T09:30:00.000Z', duration: 600, plugins: [ diff --git a/packages/nx-plugin/package.json b/packages/nx-plugin/package.json index 3f9ecb744..776517aab 100644 --- a/packages/nx-plugin/package.json +++ b/packages/nx-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/nx-plugin", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "dependencies": { "@nx/devkit": "^17.1.3", diff --git a/packages/plugin-coverage/package.json b/packages/plugin-coverage/package.json index e8ebe1496..8b3da3576 100644 --- a/packages/plugin-coverage/package.json +++ b/packages/plugin-coverage/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/coverage-plugin", - "version": "0.25.6", + "version": "0.26.1", "dependencies": { "@code-pushup/models": "*", "@code-pushup/utils": "*", diff --git a/packages/plugin-eslint/package.json b/packages/plugin-eslint/package.json index 7fb867fd6..f192f48a8 100644 --- a/packages/plugin-eslint/package.json +++ b/packages/plugin-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/eslint-plugin", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "dependencies": { "@code-pushup/utils": "*", diff --git a/packages/plugin-eslint/src/lib/runner.integration.test.ts b/packages/plugin-eslint/src/lib/runner.integration.test.ts index 8a75822bd..029cc6ba2 100644 --- a/packages/plugin-eslint/src/lib/runner.integration.test.ts +++ b/packages/plugin-eslint/src/lib/runner.integration.test.ts @@ -17,9 +17,7 @@ import { } from './runner'; import { setupESLint } from './setup'; -// FIXME: tests fail when run in same thread as other integration tests -// eslint-disable-next-line vitest/no-disabled-tests -describe.skip('executeRunner', () => { +describe('executeRunner', () => { let cwdSpy: MockInstance<[], string>; let platformSpy: MockInstance<[], NodeJS.Platform>; diff --git a/packages/plugin-lighthouse/package.json b/packages/plugin-lighthouse/package.json index 71895feca..28c023350 100644 --- a/packages/plugin-lighthouse/package.json +++ b/packages/plugin-lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/lighthouse-plugin", - "version": "0.25.6", + "version": "0.26.1", "license": "MIT", "dependencies": { "@code-pushup/models": "*", diff --git a/packages/utils/package.json b/packages/utils/package.json index 1a2280096..ed9d964d0 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@code-pushup/utils", - "version": "0.25.6", + "version": "0.26.1", "dependencies": { "@code-pushup/models": "*", "bundle-require": "^4.0.1", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 33a48ef34..1ca9dab8e 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -39,7 +39,6 @@ export { getGitRoot, getLatestCommit, toGitPath, - validateCommitData, getCurrentBranchOrTag, guardAgainstLocalChanges, safeCheckout, diff --git a/packages/utils/src/lib/git.integration.test.ts b/packages/utils/src/lib/git.integration.test.ts index 53fd727ce..99e6abb8d 100644 --- a/packages/utils/src/lib/git.integration.test.ts +++ b/packages/utils/src/lib/git.integration.test.ts @@ -12,10 +12,8 @@ import { } from './git'; import { toUnixPath } from './transform'; -// we need a separate folder that is not cleaned in `global-setup.ts`, otherwise the tests can't execute in parallel -const gitTestFolder = 'git-test'; describe('git utils in a git repo', () => { - const baseDir = join(process.cwd(), gitTestFolder); + const baseDir = join(process.cwd(), 'tmp', 'git-tests'); let emptyGit: SimpleGit; beforeAll(async () => { @@ -54,14 +52,11 @@ describe('git utils in a git repo', () => { }); it('should log latest commit', async () => { - const gitCommitDateRegex = - /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d{2}:\d{2}:\d{2} \d{4} [+|-]\d{4}$/; - await expect(getLatestCommit(emptyGit)).resolves.toEqual({ hash: expect.stringMatching(/^[\da-f]{40}$/), message: 'Create README', author: 'John Doe', - date: expect.stringMatching(gitCommitDateRegex), + date: expect.any(Date), }); }); @@ -78,12 +73,12 @@ describe('git utils in a git repo', () => { it('should convert relative Windows path to relative Git path', async () => { await expect( toGitPath('Backend\\API\\Startup.cs', emptyGit), - ).resolves.toBe('../Backend/API/Startup.cs'); + ).resolves.toBe('../../Backend/API/Startup.cs'); }); it('should keep relative Unix path as is (already a Git path)', async () => { await expect(toGitPath('Backend/API/Startup.cs', emptyGit)).resolves.toBe( - '../Backend/API/Startup.cs', + '../../Backend/API/Startup.cs', ); }); diff --git a/packages/utils/src/lib/git.ts b/packages/utils/src/lib/git.ts index 777228d3d..330a2203f 100644 --- a/packages/utils/src/lib/git.ts +++ b/packages/utils/src/lib/git.ts @@ -1,21 +1,21 @@ import { isAbsolute, join, relative } from 'node:path'; import { simpleGit } from 'simple-git'; +import { Commit, commitSchema } from '@code-pushup/models'; import { toUnixPath } from './transform'; -export type CommitData = { - hash: string; - message: string; - author: string; - date: string; -}; - -export async function getLatestCommit(git = simpleGit()) { - // git log -1 --pretty=format:"%H %s %an %ad" // logs hash, message, author, date +export async function getLatestCommit( + git = simpleGit(), +): Promise { + // git log -1 --pretty=format:"%H %s %an %aI" + // https://git-scm.com/docs/pretty-formats const log = await git.log({ maxCount: 1, - format: { hash: '%H', message: '%s', author: '%an', date: '%ad' }, + format: { hash: '%H', message: '%s', author: '%an', date: '%aI' }, }); - return log.latest satisfies CommitData | null; + if (!log.latest) { + return null; + } + return commitSchema.parse(log.latest); } export function getGitRoot(git = simpleGit()): Promise { @@ -36,23 +36,6 @@ export async function toGitPath( return formatGitPath(path, gitRoot); } -export function validateCommitData( - commitData: CommitData | null, - options: { throwError?: true } = {}, -): commitData is CommitData { - if (!commitData) { - const msg = 'no commit data available'; - if (options.throwError) { - throw new Error(msg); - } else { - // @TODO replace with ui().logger.warning - console.warn(msg); - return false; - } - } - return true; -} - export async function guardAgainstLocalChanges( git = simpleGit(), ): Promise { diff --git a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.integration.test.ts.snap b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.integration.test.ts.snap index 0d2bb2a15..ad9ee8814 100644 --- a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.integration.test.ts.snap +++ b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.integration.test.ts.snap @@ -429,7 +429,7 @@ Report was created by [Code PushUp](https://github.com/flowup/quality-metrics-cl |Commit|Version|Duration|Plugins|Categories|Audits| |:--|:--:|:--:|:--:|:--:|:--:| -|refactor(cli): fix exec target (41682a2)|\`0.0.1\`|1.65 s|2|3|52| +|Minor fixes (abcdef0)|\`0.0.1\`|1.65 s|2|3|52| The following plugins were run: @@ -806,7 +806,7 @@ Report was created by [Code PushUp](https://github.com/flowup/quality-metrics-cl |Commit|Version|Duration|Plugins|Categories|Audits| |:--|:--:|:--:|:--:|:--:|:--:| -|refactor(cli): fix exec target (41682a2)|\`0.0.1\`|1.65 s|2|0|52| +|Minor fixes (abcdef0)|\`0.0.1\`|1.65 s|2|0|52| The following plugins were run: diff --git a/packages/utils/src/lib/reports/__snapshots__/sorting.integration.test.ts.snap b/packages/utils/src/lib/reports/__snapshots__/sorting.integration.test.ts.snap index 16da86b52..c5a8eb07e 100644 --- a/packages/utils/src/lib/reports/__snapshots__/sorting.integration.test.ts.snap +++ b/packages/utils/src/lib/reports/__snapshots__/sorting.integration.test.ts.snap @@ -60,6 +60,12 @@ exports[`sortReport > should sort the audits and audit groups in categories, plu "title": "Bug prevention", }, ], + "commit": { + "author": "John Doe", + "date": 2023-08-16T08:30:00.000Z, + "hash": "abcdef0123456789abcdef0123456789abcdef01", + "message": "Minor fixes", + }, "date": "2023-08-16T09:00:00.000Z", "duration": 666, "packageName": "@code-pushup/core", diff --git a/packages/utils/src/lib/reports/generate-md-report.integration.test.ts b/packages/utils/src/lib/reports/generate-md-report.integration.test.ts index b615288a8..b4d5c9cdc 100644 --- a/packages/utils/src/lib/reports/generate-md-report.integration.test.ts +++ b/packages/utils/src/lib/reports/generate-md-report.integration.test.ts @@ -15,19 +15,7 @@ describe('generateMdReport', () => { }); it('should contain all sections when using the fixture report', () => { - const commit = { - hash: '41682a2fec1d4ece81c696a26c08984baeb4bcf3', - message: 'refactor(cli): fix exec target', - author: 'BioPhoton', - date: 'Sat Sep 10 12:00:00 2021 +0200', - }; - const mdReport = generateMdReport( - sortReport(scoreReport(reportMock())), - commit, - ); - expect(mdReport).toContain( - `${commit.message} (${commit.hash.slice(0, 7)})`, - ); + const mdReport = generateMdReport(sortReport(scoreReport(reportMock()))); expect(mdReport).toContain('🏷 Category'); expect(mdReport).toMatchSnapshot(); }); @@ -35,12 +23,6 @@ describe('generateMdReport', () => { it('should not contain category sections when categories are empty', () => { const mdReport = generateMdReport( sortReport(scoreReport({ ...reportMock(), categories: [] })), - { - hash: '41682a2fec1d4ece81c696a26c08984baeb4bcf3', - message: 'refactor(cli): fix exec target', - author: 'BioPhoton', - date: 'Sat Sep 10 12:00:00 2021 +0200', - }, ); expect(mdReport).not.toContain('🏷 Category'); expect(mdReport).toMatchSnapshot(); diff --git a/packages/utils/src/lib/reports/generate-md-report.ts b/packages/utils/src/lib/reports/generate-md-report.ts index 744cd87ed..a5807af34 100644 --- a/packages/utils/src/lib/reports/generate-md-report.ts +++ b/packages/utils/src/lib/reports/generate-md-report.ts @@ -1,6 +1,5 @@ import { AuditReport, CategoryConfig, Issue } from '@code-pushup/models'; import { formatDate, formatDuration, slugify } from '../formatting'; -import { CommitData } from '../git'; import { FOOTER_PREFIX, NEW_LINE, @@ -34,10 +33,7 @@ import { getSquaredScoreMarker, } from './utils'; -export function generateMdReport( - report: ScoredReport, - commitData: CommitData | null, -): string { +export function generateMdReport(report: ScoredReport): string { const printCategories = report.categories.length > 0; return ( @@ -58,7 +54,7 @@ export function generateMdReport( NEW_LINE + NEW_LINE + // about section - reportToAboutSection(report, commitData) + + reportToAboutSection(report) + NEW_LINE + NEW_LINE + // footer section @@ -235,15 +231,12 @@ function reportToDetailsSection(audit: AuditReport) { return details(detailsTitle, detailsTable); } -function reportToAboutSection( - report: ScoredReport, - commitData: CommitData | null, -): string { +function reportToAboutSection(report: ScoredReport): string { const date = formatDate(new Date()); - const { duration, version, plugins, categories } = report; - const commitInfo = commitData - ? `${commitData.message} (${commitData.hash.slice(0, 7)})` + const { duration, version, commit, plugins, categories } = report; + const commitInfo = commit + ? `${commit.message} (${commit.hash.slice(0, 7)})` : 'N/A'; const reportMetaTable: string[][] = [ reportMetaTableHeaders, diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/commit.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/commit.mock.ts new file mode 100644 index 000000000..13b950076 --- /dev/null +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/commit.mock.ts @@ -0,0 +1,8 @@ +import type { Commit } from '@code-pushup/models'; + +export const COMMIT_MOCK: Commit = { + hash: 'abcdef0123456789abcdef0123456789abcdef01', + message: 'Minor fixes', + author: 'John Doe', + date: new Date('2023-08-16T08:30:00.000Z'), +}; diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/config.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/config.mock.ts index bce3967c3..c1199b1c4 100644 --- a/testing/test-utils/src/lib/utils/dynamic-mocks/config.mock.ts +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/config.mock.ts @@ -1,10 +1,4 @@ -import { - CoreConfig, - PluginConfig, - PluginReport, - Report, - coreConfigSchema, -} from '@code-pushup/models'; +import { CoreConfig, coreConfigSchema } from '@code-pushup/models'; import { categoryConfigsMock } from './categories.mock'; import { eslintPluginConfigMock } from './eslint-plugin.mock'; import { lighthousePluginConfigMock } from './lighthouse-plugin.mock'; @@ -68,47 +62,3 @@ export function minimalConfigMock( return JSON.parse(JSON.stringify(cfg)); } - -export function minimalReportMock(outputDir = 'tmp'): Report { - const PLUGIN_1_SLUG = 'plugin-1'; - const AUDIT_1_SLUG = 'audit-1'; - - const plg1: PluginConfig = pluginConfigMock([], { - slug: PLUGIN_1_SLUG, - outputDir, - }); - - const { runner: _, ...rest } = plg1; - const pluginReport: PluginReport = { - ...rest, - duration: 0, - date: 'dummy-data-string', - version: '', - packageName: '', - audits: [auditReportMock({ slug: AUDIT_1_SLUG })], - }; - - return JSON.parse( - JSON.stringify({ - packageName: '@code-pushup/core', - version: '0.1.0', - date: 'today', - duration: 42, - categories: [ - { - slug: 'category-1', - title: 'Category 1', - refs: [ - { - type: 'audit', - plugin: PLUGIN_1_SLUG, - slug: AUDIT_1_SLUG, - weight: 1, - }, - ], - }, - ], - plugins: [pluginReport], - } satisfies Report), - ); -} diff --git a/testing/test-utils/src/lib/utils/dynamic-mocks/report.mock.ts b/testing/test-utils/src/lib/utils/dynamic-mocks/report.mock.ts index 6d3df6521..c1aee8c4b 100644 --- a/testing/test-utils/src/lib/utils/dynamic-mocks/report.mock.ts +++ b/testing/test-utils/src/lib/utils/dynamic-mocks/report.mock.ts @@ -1,5 +1,6 @@ import { Report, reportSchema } from '@code-pushup/models'; import { categoryConfigsMock } from './categories.mock'; +import { COMMIT_MOCK } from './commit.mock'; import { eslintPluginReportMock } from './eslint-plugin.mock'; import { lighthousePluginReportMock } from './lighthouse-plugin.mock'; @@ -12,7 +13,8 @@ export function reportMock(): Report { eslintPluginReportMock().duration + lighthousePluginReportMock().duration + 50, + commit: COMMIT_MOCK, categories: categoryConfigsMock(), plugins: [eslintPluginReportMock(), lighthousePluginReportMock()], - }); + } satisfies Report); } diff --git a/testing/test-utils/src/lib/utils/report.mock.ts b/testing/test-utils/src/lib/utils/report.mock.ts index 55383bdc2..bd84e99b1 100644 --- a/testing/test-utils/src/lib/utils/report.mock.ts +++ b/testing/test-utils/src/lib/utils/report.mock.ts @@ -1,10 +1,16 @@ -import type { Report } from '@code-pushup/models'; +import type { PluginConfig, PluginReport, Report } from '@code-pushup/models'; +import { COMMIT_MOCK } from './dynamic-mocks/commit.mock'; +import { + auditReportMock, + pluginConfigMock, +} from './dynamic-mocks/plugin-config.mock'; export const MINIMAL_REPORT_MOCK: Report = { packageName: '@code-pushup/core', version: '0.0.1', date: '2023-08-16T09:00:00.000Z', duration: 666, + commit: COMMIT_MOCK, categories: [], plugins: [ { @@ -30,6 +36,7 @@ export const REPORT_MOCK: Report = { version: '1.0.0', date: '2023-08-16T09:00:00.000Z', duration: 666, + commit: COMMIT_MOCK, categories: [ { slug: 'test-results', @@ -246,3 +253,48 @@ export const REPORT_MOCK: Report = { }, ], }; + +export function minimalReportMock(outputDir = 'tmp'): Report { + const PLUGIN_1_SLUG = 'plugin-1'; + const AUDIT_1_SLUG = 'audit-1'; + + const plg1: PluginConfig = pluginConfigMock([], { + slug: PLUGIN_1_SLUG, + outputDir, + }); + + const { runner: _, ...rest } = plg1; + const pluginReport: PluginReport = { + ...rest, + duration: 0, + date: 'dummy-data-string', + version: '', + packageName: '', + audits: [auditReportMock({ slug: AUDIT_1_SLUG })], + }; + + return JSON.parse( + JSON.stringify({ + packageName: '@code-pushup/core', + version: '0.1.0', + date: 'today', + commit: null, + duration: 42, + categories: [ + { + slug: 'category-1', + title: 'Category 1', + refs: [ + { + type: 'audit', + plugin: PLUGIN_1_SLUG, + slug: AUDIT_1_SLUG, + weight: 1, + }, + ], + }, + ], + plugins: [pluginReport], + } satisfies Report), + ); +}