Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
],
"test-vitest-inputs": [
"os",
{
"env": "NX_VERBOSE_LOGGING"
},
{
"externalDependencies": ["vitest"]
}
Expand Down
10 changes: 0 additions & 10 deletions packages/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ Optionally, you can override default options for further customization:
| `silent` | `boolean` | `false` | Hides logs from CLI commands (errors will be printed) |
| `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 |
| `configPatterns` | `ConfigPatterns \| null` | `null` | Additional configuration which enables [faster CI runs](#faster-ci-runs-with-configpatterns) |
| `searchCommits` | `boolean \| number` | `false` | If base branch has no cached report in portal, [extends search up to 100 recent commits](#search-latest-commits-for-previous-report) |
Expand All @@ -115,15 +114,6 @@ Optionally, you can override default options for further customization:

[^2]: The `{task}` pattern is replaced with the `task` value, so the default behaviour is to list projects using `npx nx show projects --with-target=code-pushup --json`. The `nxProjectsFilter` options gives Nx users the flexibility to filter projects in alternative ways supported by the Nx CLI (e.g. `--affected`, `--projects`, `--exclude`, `--type`) - refer to [options in Nx docs](https://nx.dev/nx-api/nx/documents/show#options) for details.

The `Logger` object has the following required properties:

| Property | Type | Description |
| :------- | :-------------------------- | :----------------- |
| `error` | `(message: string) => void` | Prints error log |
| `warn` | `(message: string) => void` | Prints warning log |
| `info` | `(message: string) => void` | Prints info log |
| `debug` | `(message: string) => void` | Prints debug log |

## Standalone mode

By default, it is assumed that Code PushUp is set up to run on the whole repo with one command (_standalone mode_).
Expand Down
3 changes: 1 addition & 2 deletions packages/ci/src/lib/cli/commands/collect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DEFAULT_PERSIST_FORMAT } from '@code-pushup/models';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import { executeProcess } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

export async function runCollect(
Expand All @@ -9,7 +9,6 @@ export async function runCollect(
await executeProcess({
command: bin,
args: [
...(isVerbose() ? ['--verbose'] : []),
...(config ? [`--config=${config}`] : []),
...(hasFormats
? []
Expand Down
3 changes: 1 addition & 2 deletions packages/ci/src/lib/cli/commands/compare.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DEFAULT_PERSIST_FORMAT } from '@code-pushup/models';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import { executeProcess } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

export async function runCompare(
Expand All @@ -10,7 +10,6 @@ export async function runCompare(
command: bin,
args: [
'compare',
...(isVerbose() ? ['--verbose'] : []),
...(config ? [`--config=${config}`] : []),
...(hasFormats
? []
Expand Down
3 changes: 1 addition & 2 deletions packages/ci/src/lib/cli/commands/merge-diffs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
DEFAULT_PERSIST_FILENAME,
DEFAULT_PERSIST_OUTPUT_DIR,
} from '@code-pushup/models';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import { executeProcess } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

export async function runMergeDiffs(
Expand All @@ -17,7 +17,6 @@ export async function runMergeDiffs(
command: bin,
args: [
'merge-diffs',
...(isVerbose() ? ['--verbose'] : []),
...files.map(file => `--files=${file}`),
...(config ? [`--config=${config}`] : []),
`--persist.outputDir=${outputDir}`,
Expand Down
2 changes: 0 additions & 2 deletions packages/ci/src/lib/cli/commands/print-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { rm } from 'node:fs/promises';
import path from 'node:path';
import {
executeProcess,
isVerbose,
readJsonFile,
stringifyError,
} from '@code-pushup/utils';
Expand Down Expand Up @@ -31,7 +30,6 @@ export async function runPrintConfig({
args: [
...(config ? [`--config=${config}`] : []),
'print-config',
...(isVerbose() ? ['--verbose'] : []),
`--output=${outputPath}`,
],
cwd: directory,
Expand Down
7 changes: 3 additions & 4 deletions packages/ci/src/lib/comment.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { readFile } from 'node:fs/promises';
import type { Logger, ProviderAPIClient } from './models.js';
import { logger } from '@code-pushup/utils';
import type { ProviderAPIClient } from './models.js';

export async function commentOnPR(
mdPath: string,
api: ProviderAPIClient,
logger: Logger,
): Promise<number> {
const markdown = await readFile(mdPath, 'utf8');
const identifier = `<!-- generated by @code-pushup/ci -->`;
const body = truncateBody(
`${markdown}\n\n${identifier}\n`,
api.maxCommentChars,
logger,
);

const comments = await api.listComments();
Expand All @@ -37,7 +36,7 @@ export async function commentOnPR(
return createdComment.id;
}

function truncateBody(body: string, max: number, logger: Logger): string {
function truncateBody(body: string, max: number): string {
const truncateWarning = '...*[Comment body truncated]*';
if (body.length > max) {
logger.warn(`Comment body is too long. Truncating to ${max} characters.`);
Expand Down
20 changes: 7 additions & 13 deletions packages/ci/src/lib/comment.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { vol } from 'memfs';
import { writeFile } from 'node:fs/promises';
import path from 'node:path';
import { MEMFS_VOLUME } from '@code-pushup/test-utils';
import { logger } from '@code-pushup/utils';
import { commentOnPR } from './comment.js';
import type { Comment, Logger, ProviderAPIClient } from './models.js';
import type { Comment, ProviderAPIClient } from './models.js';

describe('commentOnPR', () => {
const diffText = '# Code PushUp\n\nNo changes to report.\n';
Expand All @@ -28,13 +29,6 @@ describe('commentOnPR', () => {
listComments: vi.fn(),
} satisfies ProviderAPIClient;

const logger: Logger = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};

beforeEach(() => {
vol.fromJSON({ [diffFile]: diffText }, MEMFS_VOLUME);
api.listComments.mockResolvedValue([]);
Expand All @@ -43,7 +37,7 @@ describe('commentOnPR', () => {
it('should create new comment if none existing', async () => {
api.listComments.mockResolvedValue([]);

await expect(commentOnPR(diffPath, api, logger)).resolves.toBe(comment.id);
await expect(commentOnPR(diffPath, api)).resolves.toBe(comment.id);

expect(api.listComments).toHaveBeenCalled();
expect(api.createComment).toHaveBeenCalledWith(comment.body);
Expand All @@ -53,7 +47,7 @@ describe('commentOnPR', () => {
it("should create new comment if existing comments don't match", async () => {
api.listComments.mockResolvedValue([otherComment]);

await expect(commentOnPR(diffPath, api, logger)).resolves.toBe(comment.id);
await expect(commentOnPR(diffPath, api)).resolves.toBe(comment.id);

expect(api.listComments).toHaveBeenCalled();
expect(api.createComment).toHaveBeenCalledWith(comment.body);
Expand All @@ -63,7 +57,7 @@ describe('commentOnPR', () => {
it('should update previous comment if it matches', async () => {
api.listComments.mockResolvedValue([comment]);

await expect(commentOnPR(diffPath, api, logger)).resolves.toBe(comment.id);
await expect(commentOnPR(diffPath, api)).resolves.toBe(comment.id);

expect(api.listComments).toHaveBeenCalled();
expect(api.createComment).not.toHaveBeenCalled();
Expand All @@ -73,7 +67,7 @@ describe('commentOnPR', () => {
it('should update previous comment which matches and ignore other comments', async () => {
api.listComments.mockResolvedValue([otherComment, comment]);

await expect(commentOnPR(diffPath, api, logger)).resolves.toBe(comment.id);
await expect(commentOnPR(diffPath, api)).resolves.toBe(comment.id);

expect(api.listComments).toHaveBeenCalled();
expect(api.createComment).not.toHaveBeenCalled();
Expand All @@ -86,7 +80,7 @@ describe('commentOnPR', () => {
.join('\n');
await writeFile(diffPath, longDiffText);

await expect(commentOnPR(diffPath, api, logger)).resolves.toBe(comment.id);
await expect(commentOnPR(diffPath, api)).resolves.toBe(comment.id);

expect(api.createComment).toHaveBeenCalledWith(
expect.stringContaining('...*[Comment body truncated]*'),
Expand Down
8 changes: 4 additions & 4 deletions packages/ci/src/lib/create-execution-observer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ProcessObserver, isVerbose } from '@code-pushup/utils';
import { type ProcessObserver, logger } from '@code-pushup/utils';

export function createExecutionObserver(
{
Expand All @@ -9,11 +9,11 @@ export function createExecutionObserver(
): ProcessObserver {
return {
onStderr: stderr => {
console.warn(stderr);
logger.warn(stderr);
},
...((!silent || isVerbose()) && {
...((!silent || logger.isVerbose()) && {
onStdout: stdout => {
console.info(stdout);
logger.info(stdout);
},
}),
};
Expand Down
11 changes: 0 additions & 11 deletions packages/ci/src/lib/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export type Options = {
silent?: boolean;
debug?: boolean;
detectNewIssues?: boolean;
logger?: Logger;
skipComment?: boolean;
configPatterns?: ConfigPatterns | null;
searchCommits?: boolean | number;
Expand Down Expand Up @@ -66,16 +65,6 @@ export type GitBranch = {
sha: string;
};

/**
* Logger instance (e.g. `console`) for reporting progress and problems
*/
export type Logger = {
error: (message: string) => void;
warn: (message: string) => void;
info: (message: string) => void;
debug: (message: string) => void;
};

/**
* Code PushUp config patterns which hold for every project in monorepo.
* Providing this information upfront makes CI runs faster (skips print-config).
Expand Down
13 changes: 4 additions & 9 deletions packages/ci/src/lib/monorepo/list-projects.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { glob } from 'glob';
import path from 'node:path';
import { logger } from '@code-pushup/utils';
import { createExecutionObserver } from '../create-execution-observer.js';
import type { Logger, Settings } from '../models.js';
import type { Settings } from '../models.js';
import { detectMonorepoTool } from './detect-tool.js';
import { getToolHandler } from './handlers/index.js';
import { listPackages } from './packages.js';
Expand All @@ -24,7 +25,6 @@ export type RunManyCommand = (
export async function listMonorepoProjects(
settings: Settings,
): Promise<MonorepoProjects> {
const logger = settings.logger;
const options = createMonorepoHandlerOptions(settings);

const tool = await resolveMonorepoTool(settings, options);
Expand All @@ -50,15 +50,13 @@ export async function listMonorepoProjects(
patterns: settings.projects,
cwd: options.cwd,
bin: settings.bin,
logger,
});
return { tool, projects };
}

const projects = await listProjectsByNpmPackages({
cwd: options.cwd,
bin: settings.bin,
logger,
});
return { tool, projects };
}
Expand All @@ -71,7 +69,6 @@ async function resolveMonorepoTool(
// shouldn't happen, handled by caller
throw new Error('Monorepo mode not enabled');
}
const logger = settings.logger;

if (typeof settings.monorepo === 'string') {
logger.info(`Using monorepo tool "${settings.monorepo}" from inputs`);
Expand Down Expand Up @@ -108,9 +105,8 @@ async function listProjectsByGlobs(args: {
patterns: string[];
cwd: string;
bin: string;
logger: Logger;
}): Promise<ProjectConfig[]> {
const { patterns, cwd, bin, logger } = args;
const { patterns, cwd, bin } = args;

const directories = await glob(
patterns.map(pattern => pattern.replace(/\/$/, '/')),
Expand All @@ -134,9 +130,8 @@ async function listProjectsByGlobs(args: {
async function listProjectsByNpmPackages(args: {
cwd: string;
bin: string;
logger: Logger;
}): Promise<ProjectConfig[]> {
const { cwd, bin, logger } = args;
const { cwd, bin } = args;

const packages = await listPackages(cwd);

Expand Down
6 changes: 0 additions & 6 deletions packages/ci/src/lib/monorepo/list-projects.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ describe('listMonorepoProjects', () => {
nxProjectsFilter: '--with-target={task}',
directory: MEMFS_VOLUME,
bin: 'npx --no-install code-pushup',
logger: {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
},
};

const pkgJsonContent = (content: PackageJson): string =>
Expand Down
6 changes: 3 additions & 3 deletions packages/ci/src/lib/output-files.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { copyFile, mkdir } from 'node:fs/promises';
import path from 'node:path';
import { DEFAULT_PERSIST_FILENAME, type Format } from '@code-pushup/models';
import { objectFromEntries, objectToKeys } from '@code-pushup/utils';
import { logger, objectFromEntries, objectToKeys } from '@code-pushup/utils';
import type { OutputFiles, Settings } from './models.js';
import type { ProjectConfig } from './monorepo/tools.js';

Expand All @@ -13,12 +13,12 @@ export async function saveOutputFiles<T extends Partial<OutputFiles>>({
project,
type,
files,
settings: { logger, directory },
settings: { directory },
}: {
project: Pick<ProjectConfig, 'name'> | null;
type: OutputType;
files: T;
settings: Pick<Settings, 'logger' | 'directory'>;
settings: Pick<Settings, 'directory'>;
}): Promise<T> {
const baseDir = project ? path.join(BASE_DIR, project.name) : BASE_DIR;
const outputDir = path.join(directory, baseDir, `.${type}`);
Expand Down
10 changes: 4 additions & 6 deletions packages/ci/src/lib/output-files.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import { vol } from 'memfs';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { MEMFS_VOLUME } from '@code-pushup/test-utils';
import { logger } from '@code-pushup/utils';
import type { Settings } from './models.js';
import { saveOutputFiles } from './output-files.js';

describe('saveOutputFiles', () => {
const settings: Pick<Settings, 'logger' | 'directory'> = {
logger: console,
const settings: Pick<Settings, 'directory'> = {
directory: MEMFS_VOLUME,
};

beforeEach(() => {
vi.spyOn(settings.logger, 'debug').mockImplementation(() => {});

vol.fromJSON(
{
'report.json': '{ "score": 1 }',
Expand Down Expand Up @@ -121,10 +119,10 @@ describe('saveOutputFiles', () => {
settings,
});

expect(settings.logger.debug).toHaveBeenCalledWith(
expect(logger.debug).toHaveBeenCalledWith(
`Copied current report from ${path.join(MEMFS_VOLUME, 'report.json')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.json')}`,
);
expect(settings.logger.debug).toHaveBeenCalledWith(
expect(logger.debug).toHaveBeenCalledWith(
`Copied current report from ${path.join(MEMFS_VOLUME, 'report.md')} to ${path.join(MEMFS_VOLUME, '.code-pushup/.ci/.current/report.md')}`,
);
});
Expand Down
Loading