diff --git a/e2e/ci-e2e/global-setup.ts b/e2e/ci-e2e/global-setup.ts new file mode 100644 index 000000000..92893c11c --- /dev/null +++ b/e2e/ci-e2e/global-setup.ts @@ -0,0 +1,16 @@ +/* eslint-disable functional/immutable-data */ + +const originalCI = process.env['CI']; + +export function setup() { + // package is expected to run in CI environment + process.env['CI'] = 'true'; +} + +export function teardown() { + if (originalCI === undefined) { + delete process.env['CI']; + } else { + process.env['CI'] = originalCI; + } +} diff --git a/e2e/ci-e2e/tsconfig.test.json b/e2e/ci-e2e/tsconfig.test.json index 307a55e79..5248dd739 100644 --- a/e2e/ci-e2e/tsconfig.test.json +++ b/e2e/ci-e2e/tsconfig.test.json @@ -8,6 +8,7 @@ "vitest.e2e.config.ts", "tests/**/*.e2e.test.ts", "tests/**/*.d.ts", - "mocks/**/*.ts" + "mocks/**/*.ts", + "global-setup.ts" ] } diff --git a/e2e/ci-e2e/vitest.e2e.config.ts b/e2e/ci-e2e/vitest.e2e.config.ts index b44113aba..188911141 100644 --- a/e2e/ci-e2e/vitest.e2e.config.ts +++ b/e2e/ci-e2e/vitest.e2e.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ }, environment: 'node', include: ['tests/**/*.e2e.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + globalSetup: './global-setup.ts', setupFiles: ['../../testing/test-setup/src/lib/reset.mocks.ts'], }, }); diff --git a/packages/ci/src/lib/cli/commands/collect.ts b/packages/ci/src/lib/cli/commands/collect.ts index d98aacf4a..9423c4202 100644 --- a/packages/ci/src/lib/cli/commands/collect.ts +++ b/packages/ci/src/lib/cli/commands/collect.ts @@ -2,18 +2,18 @@ import { DEFAULT_PERSIST_FORMAT } from '@code-pushup/models'; import { executeProcess, isVerbose } from '@code-pushup/utils'; import type { CommandContext } from '../context.js'; -export async function runCollect({ - bin, - config, - directory, - observer, -}: CommandContext): Promise { +export async function runCollect( + { bin, config, directory, observer }: CommandContext, + { hasFormats }: { hasFormats: boolean }, +): Promise { await executeProcess({ command: bin, args: [ ...(isVerbose() ? ['--verbose'] : []), ...(config ? [`--config=${config}`] : []), - ...DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`), + ...(hasFormats + ? [] + : DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`)), ], cwd: directory, observer, diff --git a/packages/ci/src/lib/run-monorepo.ts b/packages/ci/src/lib/run-monorepo.ts index 333824ea3..cc3207905 100644 --- a/packages/ci/src/lib/run-monorepo.ts +++ b/packages/ci/src/lib/run-monorepo.ts @@ -32,6 +32,7 @@ import { type RunEnv, checkPrintConfig, compareReports, + hasDefaultPersistFormats, loadCachedBaseReport, printPersistConfig, runInBaseBranch, @@ -116,24 +117,35 @@ async function runProjectsInBulk( `Running on ${projects.length} projects in bulk (parallel: ${settings.parallel})`, ); - await collectMany(runManyCommand, env); - - const currProjectReports = await asyncSequential( - projects, - async (project): Promise => { - const ctx = createCommandContext(settings, project); - const config = await printPersistConfig(ctx); - const reports = await saveOutputFiles({ - project, - type: 'current', - files: persistedFilesFromConfig(config, ctx), - settings, - }); - return { project, reports, config, ctx }; - }, - ); + const currProjectConfigs = await asyncSequential(projects, async project => { + const ctx = createCommandContext(settings, project); + const config = await printPersistConfig(ctx); + return { project, config, ctx }; + }); + const hasFormats = allProjectsHaveDefaultPersistFormats(currProjectConfigs); logger.debug( - `Loaded ${currProjectReports.length} persist configs by running print-config command for each project`, + [ + `Loaded ${currProjectConfigs.length} persist configs by running print-config command for each project.`, + hasFormats + ? 'Every project has default persist formats.' + : 'Not all projects have default persist formats.', + ].join(' '), + ); + + await collectMany(runManyCommand, env, { hasFormats }); + + const currProjectReports = await Promise.all( + currProjectConfigs.map( + async ({ project, config, ctx }): Promise => { + const reports = await saveOutputFiles({ + project, + type: 'current', + files: persistedFilesFromConfig(config, ctx), + settings, + }); + return { project, reports, config, ctx }; + }, + ), ); if (base == null) { @@ -248,10 +260,12 @@ async function collectPreviousReports( } if (onlyProjects.length > 0) { + const hasFormats = + allProjectsHaveDefaultPersistFormats(validProjectConfigs); logger.info( `Collecting previous reports for ${onlyProjects.length} projects`, ); - await collectMany(runManyCommand, env, onlyProjects); + await collectMany(runManyCommand, env, { hasFormats, onlyProjects }); } const projectFiles = validProjectConfigs.map(args => @@ -281,16 +295,21 @@ async function savePreviousProjectReport(args: { async function collectMany( runManyCommand: RunManyCommand, env: RunEnv, - onlyProjects?: string[], + options: { + hasFormats: boolean; + onlyProjects?: string[]; + }, ): Promise { const { settings } = env; + const { hasFormats, onlyProjects } = options; + const command = await runManyCommand(onlyProjects); const ctx: CommandContext = { ...createCommandContext(settings, null), bin: command, }; - await runCollect(ctx); + await runCollect(ctx, { hasFormats }); const countText = onlyProjects ? `${onlyProjects.length} previous` @@ -299,3 +318,9 @@ async function collectMany( `Collected ${countText} reports using command \`${command}\``, ); } + +export function allProjectsHaveDefaultPersistFormats( + projects: { config: Pick }[], +): boolean { + return projects.every(({ config }) => hasDefaultPersistFormats(config)); +} diff --git a/packages/ci/src/lib/run-utils.ts b/packages/ci/src/lib/run-utils.ts index 84364e6b7..f11db536a 100644 --- a/packages/ci/src/lib/run-utils.ts +++ b/packages/ci/src/lib/run-utils.ts @@ -1,7 +1,12 @@ /* eslint-disable max-lines */ import { readFile } from 'node:fs/promises'; import type { SimpleGit } from 'simple-git'; -import type { CoreConfig, Report, ReportsDiff } from '@code-pushup/models'; +import { + type CoreConfig, + DEFAULT_PERSIST_FORMAT, + type Report, + type ReportsDiff, +} from '@code-pushup/models'; import { removeUndefinedAndEmptyProps, stringifyError, @@ -112,7 +117,7 @@ export async function runOnProject( `Loaded persist config from print-config command - ${JSON.stringify(config.persist)}`, ); - await runCollect(ctx); + await runCollect(ctx, { hasFormats: hasDefaultPersistFormats(config) }); const currReport = await saveReportFiles({ project, type: 'current', @@ -221,7 +226,7 @@ export async function collectPreviousReport( return null; } - await runCollect(ctx); + await runCollect(ctx, { hasFormats: hasDefaultPersistFormats(config) }); const report = await saveReportFiles({ project, type: 'previous', @@ -329,6 +334,16 @@ export async function printPersistConfig( return parsePersistConfig(json); } +export function hasDefaultPersistFormats( + config: Pick, +): boolean { + const formats = config.persist?.format; + return ( + formats == null || + DEFAULT_PERSIST_FORMAT.every(format => formats.includes(format)) + ); +} + export async function findNewIssues( args: CompareReportsArgs & { diffFiles: OutputFiles }, ): Promise { diff --git a/packages/ci/src/lib/run.int.test.ts b/packages/ci/src/lib/run.int.test.ts index 9c973c4cd..acd8b8ace 100644 --- a/packages/ci/src/lib/run.int.test.ts +++ b/packages/ci/src/lib/run.int.test.ts @@ -256,7 +256,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenNthCalledWith(2, { command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: workDir, observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -334,7 +334,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenNthCalledWith(2, { command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: workDir, observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -346,7 +346,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenNthCalledWith(4, { command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: workDir, observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -418,7 +418,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenNthCalledWith(2, { command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: workDir, observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -605,7 +605,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenCalledWith({ command: runMany, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: expect.stringContaining(workDir), observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -763,7 +763,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenCalledWith({ command: runMany, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: expect.stringContaining(workDir), observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -951,7 +951,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenCalledWith({ command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: expect.stringContaining(workDir), observer: expectedObserver, } satisfies utils.ProcessConfig); @@ -1120,7 +1120,7 @@ describe('runInCI', () => { } satisfies utils.ProcessConfig); expect(utils.executeProcess).toHaveBeenCalledWith({ command: options.bin, - args: ['--persist.format=json', '--persist.format=md'], + args: [], cwd: expect.stringContaining(workDir), observer: expectedObserver, } satisfies utils.ProcessConfig);