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
65 changes: 37 additions & 28 deletions .github/actions/code-pushup/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,42 +129,51 @@ function createGitHubApiClient(): ProviderAPIClient {
};
}

function setupOptions(): Options {
const isMonorepo = process.env['MODE'] === 'monorepo';

if (isMonorepo) {
return {
jobId: 'monorepo-mode',
monorepo: 'nx',
nxProjectsFilter: '--with-target=code-pushup --exclude=workspace',
configPatterns: {
persist: {
...DEFAULT_PERSIST_CONFIG,
outputDir: '.code-pushup/{projectName}',
},
...(process.env['CP_API_KEY'] && {
upload: {
server: 'https://api.staging.code-pushup.dev/graphql',
apiKey: process.env['CP_API_KEY'],
organization: 'code-pushup',
project: 'cli-{projectName}',
},
}),
},
};
}

// tsx importer need to resolve plugin runner scripts
// eslint-disable-next-line functional/immutable-data
process.env['NODE_OPTIONS'] = '--import=tsx';

return {
jobId: 'standalone-mode',
// run without Nx to demonstrate native GitHub Actions log groups
bin: 'node packages/cli/src/index.ts',
};
}

async function run(): Promise<void> {
try {
if (core.isDebug()) {
logger.setVerbose(true);
}

const isMonorepo = process.env['MODE'] === 'monorepo';

const options: Options = isMonorepo
? {
jobId: 'monorepo-mode',
monorepo: 'nx',
nxProjectsFilter: '--with-target=code-pushup --exclude=workspace',
configPatterns: {
persist: {
...DEFAULT_PERSIST_CONFIG,
outputDir: '.code-pushup/{projectName}',
},
...(process.env['CP_API_KEY'] && {
upload: {
server: 'https://api.staging.code-pushup.dev/graphql',
apiKey: process.env['CP_API_KEY'],
organization: 'code-pushup',
project: 'cli-{projectName}',
},
}),
},
}
: {
jobId: 'standalone-mode',
bin: 'npx nx code-pushup --',
};

const gitRefs = parseGitRefs();

const apiClient = createGitHubApiClient();
const options = setupOptions();

const result = await runInCI(gitRefs, apiClient, options);

Expand Down
23 changes: 23 additions & 0 deletions packages/utils/src/lib/logger.int.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
import type { MockInstance } from 'vitest';
import { Logger } from './logger.js';

describe('Logger', () => {

Check failure on line 9 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2582: Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.
let stdout: string;
let consoleLogSpy: MockInstance<unknown[], void>;
let processStdoutSpy: MockInstance<[], typeof process.stdout>;
let performanceNowSpy: MockInstance<[], number>;
let mathRandomSpy: MockInstance<[], number>;

beforeAll(() => {

Check failure on line 16 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'beforeAll'.
stdout = '';
vi.useFakeTimers();

Check failure on line 18 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.

consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(message => {

Check failure on line 20 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.
stdout += `${message}\n`;
});

Expand All @@ -38,19 +38,20 @@
return true;
},
};
processStdoutSpy = vi

Check failure on line 41 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.
.spyOn(process, 'stdout', 'get')
.mockReturnValue(mockProcessStdout as typeof process.stdout);
});

beforeEach(() => {

Check failure on line 46 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'beforeEach'.
stdout = '';
performanceNowSpy = vi.spyOn(performance, 'now');

Check failure on line 48 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.
mathRandomSpy = vi.spyOn(Math, 'random');

Check failure on line 49 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.

vi.stubEnv('CI', 'false');

Check failure on line 51 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.
vi.stubEnv('GITHUB_ACTIONS', 'false');

Check failure on line 52 in packages/utils/src/lib/logger.int.test.ts

View workflow job for this annotation

GitHub Actions / Standalone mode

<✓> TypeScript | Semantic errors

TS2304: Cannot find name 'vi'.
vi.stubEnv('GITLAB_CI', 'false');
vi.stubEnv('NX_TASK_TARGET_TARGET', '');
});

afterAll(() => {
Expand Down Expand Up @@ -247,6 +248,28 @@
└ ESLint reported 4 errors and 11 warnings (1.23 s)
::endgroup::

`);
});

it('should NOT use native GitHub Actions log groups if run within Nx target', async () => {
vi.stubEnv('CI', 'true');
vi.stubEnv('GITHUB_ACTIONS', 'true');
vi.stubEnv('NX_TASK_TARGET_TARGET', 'code-pushup');
performanceNowSpy.mockReturnValueOnce(0).mockReturnValueOnce(1234); // group duration: 1.23 s
const logger = new Logger();

await logger.group('Running plugin "ESLint"', async () => {
logger.info('$ npx eslint . --format=json');
logger.warn('Skipping unknown rule "deprecation/deprecation"');
return 'ESLint reported 4 errors and 11 warnings';
});

expect(ansis.strip(stdout)).toBe(`
❯ Running plugin "ESLint"
│ $ npx eslint . --format=json
│ Skipping unknown rule "deprecation/deprecation"
└ ESLint reported 4 errors and 11 warnings (1.23 s)

`);
});

Expand Down
20 changes: 14 additions & 6 deletions packages/utils/src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { formatDuration, indentLines, transformLines } from './formatting.js';
import { settlePromise } from './promises.js';

type GroupColor = Extract<AnsiColors, 'cyan' | 'magenta'>;
type CiPlatform = 'GitHub Actions' | 'GitLab CI/CD';
type CiPlatform = 'GitHub' | 'GitLab';

/** Additional options for log methods */
export type LogOptions = {
Expand Down Expand Up @@ -43,9 +43,9 @@ export class Logger {
#isVerbose = isEnvVarEnabled('CP_VERBOSE');
#isCI = isEnvVarEnabled('CI');
#ciPlatform: CiPlatform | undefined = isEnvVarEnabled('GITHUB_ACTIONS')
? 'GitHub Actions'
? 'GitHub'
: isEnvVarEnabled('GITLAB_CI')
? 'GitLab CI/CD'
? 'GitLab'
: undefined;
#groupColor: GroupColor | undefined;

Expand Down Expand Up @@ -350,15 +350,23 @@ export class Logger {
start: (title: string) => string;
end: () => string;
} {
switch (this.#ciPlatform) {
case 'GitHub Actions':
// Nx typically renders native log groups for each target in GitHub
// + GitHub doesn't support nested log groups: https://github.com/actions/toolkit/issues/1001
// => skip native GitHub log groups if run within Nx target
const platform =
this.#ciPlatform === 'GitHub' && process.env['NX_TASK_TARGET_TARGET'] // https://nx.dev/docs/reference/environment-variables
? undefined
: this.#ciPlatform;

switch (platform) {
case 'GitHub':
// https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#grouping-log-lines
return {
start: title =>
`::group::${this.#formatGroupTitle(title, { prefix: false })}`,
end: () => '::endgroup::',
};
case 'GitLab CI/CD':
case 'GitLab':
// https://docs.gitlab.com/ci/jobs/job_logs/#custom-collapsible-sections
const ansiEscCode = '\u001B[0K'; // '\e' ESC character only works for `echo -e`, Node console must use '\u001B'
const id = Math.random().toString(HEX_RADIX).slice(2);
Expand Down