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
2 changes: 1 addition & 1 deletion packages/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Optionally, you can override default options for further customization:
| `nxProjectsFilter` | `string \| string[]` | `'--with-target={task}'` | Arguments passed to [`nx show projects`](https://nx.dev/nx-api/nx/documents/show#projects), only relevant for Nx in [monorepo mode](#monorepo-mode) [^2] |
| `directory` | `string` | `process.cwd()` | Directory in which Code PushUp CLI should run |
| `config` | `string \| null` | `null` [^1] | Path to config file (`--config` option) |
| `silent` | `boolean` | `false` | Toggles if logs from CLI commands are printed |
| `silent` | `boolean` | `false` | Hides logs from CLI commands (erros 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 |
Expand Down
12 changes: 6 additions & 6 deletions packages/ci/src/lib/cli/commands/collect.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { DEFAULT_PERSIST_FORMAT } from '@code-pushup/models';
import { executeProcess } from '@code-pushup/utils';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

export async function runCollect({
bin,
config,
directory,
silent,
observer,
}: CommandContext): Promise<void> {
const { stdout } = await executeProcess({
await executeProcess({
command: bin,
args: [
...(isVerbose() ? ['--verbose'] : []),
'--no-progress',
...(config ? [`--config=${config}`] : []),
...DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`),
],
cwd: directory,
observer,
});
if (!silent) {
console.info(stdout);
}
}
11 changes: 5 additions & 6 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 } from '@code-pushup/utils';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

type CompareOptions = {
Expand All @@ -10,21 +10,20 @@ type CompareOptions = {

export async function runCompare(
{ before, after, label }: CompareOptions,
{ bin, config, directory, silent }: CommandContext,
{ bin, config, directory, observer }: CommandContext,
): Promise<void> {
const { stdout } = await executeProcess({
await executeProcess({
command: bin,
args: [
'compare',
...(isVerbose() ? ['--verbose'] : []),
`--before=${before}`,
`--after=${after}`,
...(label ? [`--label=${label}`] : []),
...(config ? [`--config=${config}`] : []),
...DEFAULT_PERSIST_FORMAT.map(format => `--persist.format=${format}`),
],
cwd: directory,
observer,
});
if (!silent) {
console.info(stdout);
}
}
11 changes: 5 additions & 6 deletions packages/ci/src/lib/cli/commands/merge-diffs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,29 @@ import {
DEFAULT_PERSIST_FILENAME,
DEFAULT_PERSIST_OUTPUT_DIR,
} from '@code-pushup/models';
import { executeProcess } from '@code-pushup/utils';
import { executeProcess, isVerbose } from '@code-pushup/utils';
import type { CommandContext } from '../context.js';

export async function runMergeDiffs(
files: string[],
{ bin, config, directory, silent }: CommandContext,
{ bin, config, directory, observer }: CommandContext,
): Promise<string> {
const outputDir = path.join(directory, DEFAULT_PERSIST_OUTPUT_DIR);
const filename = `merged-${DEFAULT_PERSIST_FILENAME}`;

const { stdout } = await executeProcess({
await executeProcess({
command: bin,
args: [
'merge-diffs',
...(isVerbose() ? ['--verbose'] : []),
...files.map(file => `--files=${file}`),
...(config ? [`--config=${config}`] : []),
`--persist.outputDir=${outputDir}`,
`--persist.filename=${filename}`,
],
cwd: directory,
observer,
});
if (!silent) {
console.info(stdout);
}

return path.join(outputDir, `${filename}-diff.md`);
}
10 changes: 5 additions & 5 deletions packages/ci/src/lib/cli/commands/print-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'node:path';
import {
executeProcess,
generateRandomId,
isVerbose,
readJsonFile,
stringifyError,
} from '@code-pushup/utils';
Expand All @@ -12,24 +13,23 @@ export async function runPrintConfig({
bin,
config,
directory,
silent,
observer,
}: CommandContext): Promise<unknown> {
// random file name so command can be run in parallel
const outputFile = `code-pushup.${generateRandomId()}.config.json`;
const outputPath = path.join(directory, outputFile);

const { stdout } = await executeProcess({
await executeProcess({
command: bin,
args: [
...(config ? [`--config=${config}`] : []),
'print-config',
...(isVerbose() ? ['--verbose'] : []),
`--output=${outputFile}`,
],
cwd: directory,
observer,
});
if (!silent) {
console.info(stdout);
}

try {
const content = await readJsonFile(outputPath);
Expand Down
19 changes: 10 additions & 9 deletions packages/ci/src/lib/cli/context.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import type { ProcessObserver } from '@code-pushup/utils';
import { createExecutionObserver } from '../create-execution-observer.js';
import type { Settings } from '../models.js';
import type { ProjectConfig } from '../monorepo/index.js';

export type CommandContext = Pick<
Settings,
'bin' | 'config' | 'directory' | 'silent'
>;
export type CommandContext = Pick<Settings, 'bin' | 'config' | 'directory'> & {
observer?: ProcessObserver;
};

export function createCommandContext(
settings: Settings,
{ config, bin, directory, silent }: Settings,
project: ProjectConfig | null | undefined,
): CommandContext {
return {
bin: project?.bin ?? settings.bin,
directory: project?.directory ?? settings.directory,
config: settings.config,
silent: settings.silent,
bin: project?.bin ?? bin,
directory: project?.directory ?? directory,
config,
observer: createExecutionObserver({ silent }),
};
}
10 changes: 8 additions & 2 deletions packages/ci/src/lib/cli/context.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { expect } from 'vitest';
import { type CommandContext, createCommandContext } from './context.js';

describe('createCommandContext', () => {
const expectedObserver = expect.objectContaining({
onStderr: expect.any(Function),
onStdout: expect.any(Function),
});

it('should pick CLI-related settings in standalone mode', () => {
expect(
createCommandContext(
Expand All @@ -25,7 +31,7 @@ describe('createCommandContext', () => {
bin: 'npx --no-install code-pushup',
directory: '/test',
config: null,
silent: false,
observer: expectedObserver,
});
});

Expand Down Expand Up @@ -57,7 +63,7 @@ describe('createCommandContext', () => {
bin: 'yarn code-pushup',
directory: '/test/ui',
config: null,
silent: false,
observer: expectedObserver,
});
});
});
41 changes: 41 additions & 0 deletions packages/ci/src/lib/create-execution-observer.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect } from 'vitest';
import { executeProcess } from '@code-pushup/utils';
import { createExecutionObserver } from './create-execution-observer.js';

describe('createExecutionObserver', () => {
const message = 'This is stdout';
const error = 'This is stderr';

it('should use execute process and use observer to capture stdout message and stderr will be empty', async () => {
const { stdout, stderr } = await executeProcess({
command: 'node',
args: ['-e', `"console.log('${message}');"`],
observer: createExecutionObserver(),
});

expect(stdout).toMatch(message);
expect(stderr).toMatch('');
});

it('should use execute process and use observer to capture stdout message and stderr will be error', async () => {
const { stdout, stderr } = await executeProcess({
command: 'node',
args: ['-e', `"console.log('${message}'); console.error('${error}');"`],
observer: createExecutionObserver(),
});

expect(stdout).toMatch(message);
expect(stderr).toMatch(error);
});

it('should use execute process and use observer to capture stderr error and ignore stdout message', async () => {
const { stdout, stderr } = await executeProcess({
command: 'node',
args: ['-e', `"console.log('${message}'); console.error('${error}');"`],
observer: createExecutionObserver({ silent: true }),
});

expect(stdout).toMatch('');
expect(stderr).toMatch(error);
});
});
20 changes: 20 additions & 0 deletions packages/ci/src/lib/create-execution-observer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type ProcessObserver, isVerbose } from '@code-pushup/utils';

export function createExecutionObserver(
{
silent,
}: {
silent?: boolean;
} = { silent: false },
): ProcessObserver {
return {
onStderr: stderr => {
console.warn(stderr);
},
...((!silent || isVerbose()) && {
onStdout: stdout => {
console.info(stdout);
},
}),
};
}
33 changes: 33 additions & 0 deletions packages/ci/src/lib/create-execution-observer.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect, vi } from 'vitest';
import { createExecutionObserver } from './create-execution-observer.js';

describe('createExecutionObserver', () => {
it('should create execution observer with default settings', () => {
expect(createExecutionObserver()).toStrictEqual({
onStderr: expect.any(Function),
onStdout: expect.any(Function),
});
});

it('should create execution observer with silent false settings', () => {
expect(createExecutionObserver({ silent: false })).toStrictEqual({
onStderr: expect.any(Function),
onStdout: expect.any(Function),
});
});

it('should create execution observer with default silent taking priority over CP_VERBOSE flag', () => {
vi.stubEnv('CP_VERBOSE', 'false');

expect(createExecutionObserver()).toStrictEqual({
onStderr: expect.any(Function),
onStdout: expect.any(Function),
});
});

it('should create execution observer with silent setting', () => {
expect(createExecutionObserver({ silent: true })).toStrictEqual({
onStderr: expect.any(Function),
});
});
});
12 changes: 5 additions & 7 deletions packages/ci/src/lib/monorepo/handlers/nx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,23 @@ export const nxHandler: MonorepoToolHandler = {
);
},

async listProjects(options) {
async listProjects({ cwd, task, nxProjectsFilter, observer }) {
const { stdout } = await executeProcess({
command: 'npx',
args: [
'nx',
'show',
'projects',
...toArray(options.nxProjectsFilter).map(arg =>
arg.replaceAll('{task}', options.task),
),
...toArray(nxProjectsFilter).map(arg => arg.replaceAll('{task}', task)),
'--json',
],
cwd: options.cwd,
observer: options.observer,
cwd,
observer,
});
const projects = parseProjects(stdout);
return projects.toSorted().map(project => ({
name: project,
bin: `npx nx run ${project}:${options.task} --skip-nx-cache --`,
bin: `npx nx run ${project}:${task} --skip-nx-cache --`,
}));
},

Expand Down
30 changes: 13 additions & 17 deletions packages/ci/src/lib/monorepo/list-projects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { glob } from 'glob';
import path from 'node:path';
import { createExecutionObserver } from '../create-execution-observer.js';
import type { Logger, Settings } from '../models.js';
import { detectMonorepoTool } from './detect-tool.js';
import { getToolHandler } from './handlers/index.js';
Expand Down Expand Up @@ -87,24 +88,19 @@ async function resolveMonorepoTool(
return tool;
}

function createMonorepoHandlerOptions(
settings: Settings,
): MonorepoHandlerOptions {
function createMonorepoHandlerOptions({
task,
directory,
parallel,
nxProjectsFilter,
silent,
}: Settings): MonorepoHandlerOptions {
return {
task: settings.task,
cwd: settings.directory,
parallel: settings.parallel,
nxProjectsFilter: settings.nxProjectsFilter,
...(!settings.silent && {
observer: {
onStdout: stdout => {
console.info(stdout);
},
onStderr: stderr => {
console.warn(stderr);
},
},
}),
task,
cwd: directory,
parallel,
nxProjectsFilter,
observer: createExecutionObserver({ silent }),
};
}

Expand Down
4 changes: 4 additions & 0 deletions packages/ci/src/lib/run-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import type { SimpleGit } from 'simple-git';
Expand Down Expand Up @@ -69,6 +70,9 @@ export async function createRunEnv(
options: Options | undefined,
git: SimpleGit,
): Promise<RunEnv> {
// eslint-disable-next-line functional/immutable-data
process.env['CP_VERBOSE'] = options?.silent ? 'false' : 'true';

const [head, base] = await Promise.all([
normalizeGitRef(refs.head, git),
refs.base && normalizeGitRef(refs.base, git),
Expand Down
Loading