diff --git a/packages/ci/README.md b/packages/ci/README.md index 7b38e18dc..b570c39a2 100644 --- a/packages/ci/README.md +++ b/packages/ci/README.md @@ -107,6 +107,7 @@ Optionally, you can override default options for further customization: | `bin` | `string` | `'npx --no-install code-pushup'` | Command for executing Code PushUp CLI | | `detectNewIssues` | `boolean` | `true` | Toggles if new issues should be detected and returned in `newIssues` property | | `logger` | `Logger` | `console` | Logger for reporting progress and encountered problems | +| `skipComment` | `boolean` | `false` | Toggles if comparison comment is posted to PR | [^1]: By default, the `code-pushup.config` file is autodetected as described in [`@code-pushup/cli` docs](../cli/README.md#configuration). diff --git a/packages/ci/src/lib/constants.ts b/packages/ci/src/lib/constants.ts index 50d1320b7..dc3390e38 100644 --- a/packages/ci/src/lib/constants.ts +++ b/packages/ci/src/lib/constants.ts @@ -13,4 +13,5 @@ export const DEFAULT_SETTINGS: Settings = { detectNewIssues: true, logger: console, nxProjectsFilter: '--with-target={task}', + skipComment: false, }; diff --git a/packages/ci/src/lib/models.ts b/packages/ci/src/lib/models.ts index 7bb5cf54b..3c91e43a9 100644 --- a/packages/ci/src/lib/models.ts +++ b/packages/ci/src/lib/models.ts @@ -19,6 +19,7 @@ export type Options = { debug?: boolean; detectNewIssues?: boolean; logger?: Logger; + skipComment?: boolean; }; /** diff --git a/packages/ci/src/lib/run-monorepo.ts b/packages/ci/src/lib/run-monorepo.ts index 6b339dc75..7fcc91245 100644 --- a/packages/ci/src/lib/run-monorepo.ts +++ b/packages/ci/src/lib/run-monorepo.ts @@ -33,6 +33,7 @@ import { type RunEnv, checkPrintConfig, compareReports, + ensureHeadBranch, loadCachedBaseReport, printPersistConfig, runInBaseBranch, @@ -47,6 +48,8 @@ export async function runInMonorepoMode( logger.info('Running Code PushUp in monorepo mode'); + await ensureHeadBranch(env); + const { projects, runManyCommand } = await listMonorepoProjects(settings); const projectResults = runManyCommand ? await runProjectsInBulk(projects, runManyCommand, env) @@ -55,6 +58,7 @@ export async function runInMonorepoMode( const diffJsonPaths = projectResults .map(({ files }) => files.diff?.json) .filter((file): file is string => file != null); + if (diffJsonPaths.length > 0) { const tmpDiffPath = await runMergeDiffs( diffJsonPaths, @@ -70,12 +74,14 @@ export async function runInMonorepoMode( await copyFile(tmpDiffPath, diffPath); logger.debug(`Copied ${tmpDiffPath} to ${diffPath}`); } - const commentId = await commentOnPR(tmpDiffPath, api, logger); + const commentId = settings.skipComment + ? null + : await commentOnPR(tmpDiffPath, api, logger); return { mode: 'monorepo', projects: projectResults, - commentId, diffPath, + ...(commentId != null && { commentId }), }; } diff --git a/packages/ci/src/lib/run-standalone.ts b/packages/ci/src/lib/run-standalone.ts index 7d6a96a99..2f06497f3 100644 --- a/packages/ci/src/lib/run-standalone.ts +++ b/packages/ci/src/lib/run-standalone.ts @@ -1,21 +1,21 @@ import { commentOnPR } from './comment.js'; import type { StandaloneRunResult } from './models.js'; -import { type RunEnv, runOnProject } from './run-utils.js'; +import { type RunEnv, ensureHeadBranch, runOnProject } from './run-utils.js'; export async function runInStandaloneMode( env: RunEnv, ): Promise { - const { - api, - settings: { logger }, - } = env; + const { api, settings } = env; + const { logger } = settings; logger.info('Running Code PushUp in standalone project mode'); + await ensureHeadBranch(env); + const { files, newIssues } = await runOnProject(null, env); const commentMdPath = files.diff?.md; - if (commentMdPath) { + if (!settings.skipComment && commentMdPath) { const commentId = await commentOnPR(commentMdPath, api, logger); return { mode: 'standalone', diff --git a/packages/ci/src/lib/run-utils.ts b/packages/ci/src/lib/run-utils.ts index c7f65f4dd..a6540bc2e 100644 --- a/packages/ci/src/lib/run-utils.ts +++ b/packages/ci/src/lib/run-utils.ts @@ -223,6 +223,13 @@ export async function loadCachedBaseReport( return null; } +export async function ensureHeadBranch({ refs, git }: RunEnv): Promise { + const { head } = refs; + if ((await git.revparse('HEAD')) !== (await git.revparse(head.ref))) { + await git.checkout(['-f', head.ref]); + } +} + export async function runInBaseBranch( base: GitBranch, env: RunEnv, diff --git a/packages/ci/src/lib/run.integration.test.ts b/packages/ci/src/lib/run.integration.test.ts index bd362834d..e775fee9c 100644 --- a/packages/ci/src/lib/run.integration.test.ts +++ b/packages/ci/src/lib/run.integration.test.ts @@ -404,6 +404,43 @@ describe('runInCI', () => { expect(logger.info).toHaveBeenCalled(); expect(logger.debug).toHaveBeenCalled(); }); + + it('should skip comment if disabled', async () => { + const api: ProviderAPIClient = { + maxCommentChars: 1_000_000, + createComment: vi.fn(), + updateComment: vi.fn(), + listComments: vi.fn().mockResolvedValue([]), + }; + + await expect( + runInCI(refs, api, { ...options, skipComment: true }, git), + ).resolves.toEqual({ + mode: 'standalone', + commentId: undefined, + newIssues: [], + files: { + report: { + json: path.join(outputDir, 'report.json'), + md: path.join(outputDir, 'report.md'), + }, + diff: { + json: path.join(outputDir, 'report-diff.json'), + md: path.join(outputDir, 'report-diff.md'), + }, + }, + } satisfies RunResult); + + expect(api.listComments).not.toHaveBeenCalled(); + expect(api.createComment).not.toHaveBeenCalled(); + expect(api.updateComment).not.toHaveBeenCalled(); + + expect(utils.executeProcess).toHaveBeenCalledWith({ + command: options.bin, + args: expect.arrayContaining(['compare']), + cwd: workDir, + } satisfies utils.ProcessConfig); + }); }); }); @@ -743,6 +780,60 @@ describe('runInCI', () => { expect(logger.info).toHaveBeenCalled(); expect(logger.debug).toHaveBeenCalled(); }); + + it('should skip comment if disabled', async () => { + const api: ProviderAPIClient = { + maxCommentChars: 1_000_000, + createComment: vi.fn(), + updateComment: vi.fn(), + listComments: vi.fn(), + }; + + await expect( + runInCI( + refs, + api, + { ...options, monorepo: tool, skipComment: true }, + git, + ), + ).resolves.toEqual({ + mode: 'monorepo', + commentId: undefined, + diffPath: path.join(workDir, '.code-pushup/merged-report-diff.md'), + projects: [ + expect.objectContaining({ name: 'cli' }), + expect.objectContaining({ name: 'core' }), + expect.objectContaining({ name: 'utils' }), + ], + } satisfies RunResult); + + await expect( + readFile( + path.join(workDir, '.code-pushup/merged-report-diff.md'), + 'utf8', + ), + ).resolves.toBe(diffMdString); + + expect(api.listComments).not.toHaveBeenCalled(); + expect(api.createComment).not.toHaveBeenCalled(); + expect(api.updateComment).not.toHaveBeenCalled(); + + expect(utils.executeProcess).toHaveBeenCalledWith({ + command: runMany, + args: expect.any(Array), + cwd: expect.stringContaining(workDir), + } satisfies utils.ProcessConfig); + expect(utils.executeProcess).toHaveBeenCalledWith({ + command: run, + args: expect.arrayContaining(['compare']), + cwd: expect.stringContaining(workDir), + } satisfies utils.ProcessConfig); + expect(utils.executeProcess).toHaveBeenCalledWith({ + command: run, + args: expect.arrayContaining(['merge-diffs']), + cwd: expect.stringContaining(workDir), + } satisfies utils.ProcessConfig); + }); }); });