Skip to content

Commit 6ade522

Browse files
committed
Merge branch 'main' into add-plugin-targets
2 parents 5397042 + 5b4069a commit 6ade522

File tree

10 files changed

+87
-29
lines changed

10 files changed

+87
-29
lines changed

.github/workflows/code-coverage.yml

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,33 @@ env:
99
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
1010

1111
jobs:
12+
list-packages:
13+
name: List packages
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout the repository
17+
uses: actions/checkout@v4
18+
- name: Set up Node.js
19+
uses: actions/setup-node@v4
20+
with:
21+
node-version-file: .nvmrc
22+
cache: npm
23+
- name: Install dependencies
24+
run: npm ci
25+
- name: List packages using Nx CLI
26+
id: list-packages
27+
run: |
28+
matrix=$(node tools/scripts/create-codecov-matrix.js)
29+
echo "matrix=$matrix" >> $GITHUB_OUTPUT
30+
outputs:
31+
matrix: ${{ steps.list-packages.outputs.matrix }}
32+
1233
coverage:
34+
needs: [list-packages]
1335
strategy:
1436
fail-fast: false
15-
matrix:
16-
lib:
17-
- cli
18-
- core
19-
- models
20-
- utils
21-
- plugin-eslint
22-
- plugin-coverage
23-
- plugin-js-packages
24-
- plugin-lighthouse
25-
scope: [unit, int]
26-
name: Update code coverage
37+
matrix: ${{ fromJson(needs.list-packages.outputs.matrix) }}
38+
name: Collect code coverage
2739
runs-on: ubuntu-latest
2840
steps:
2941
- name: Checkout the repository
@@ -35,13 +47,13 @@ jobs:
3547
cache: npm
3648
- name: Install dependencies
3749
run: npm ci
38-
- name: Execute all tests and generate coverage reports
39-
run: npx nx run ${{ matrix.lib }}:${{ matrix.scope }}-test --coverage.enabled
50+
- name: Execute tests with coverage
51+
run: npx nx run ${{ matrix.project }}:${{ matrix.target }} --coverage.enabled
4052
- name: Upload coverage reports to Codecov
4153
uses: codecov/codecov-action@v4
4254
with:
43-
directory: coverage/${{ matrix.lib }}/${{ matrix.scope }}-tests/
55+
directory: coverage/${{ matrix.project }}/${{ matrix.target }}s/
4456
files: ./lcov.info
45-
flags: ${{ matrix.lib }}-${{ matrix.scope }}
57+
flags: ${{ matrix.project }}-${{ matrix.target }}
4658
token: ${{ secrets.CODECOV_TOKEN }}
4759
fail_ci_if_error: true

packages/core/src/lib/implementation/execute-plugin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ export async function executePlugin(
6868
? // IF not null, take the result from cache
6969
((await readRunnerResults(pluginMeta.slug, outputDir)) ??
7070
// ELSE execute the plugin runner
71-
(await executePluginRunner(pluginConfig)))
72-
: await executePluginRunner(pluginConfig);
71+
(await executePluginRunner(pluginConfig, persist)))
72+
: await executePluginRunner(pluginConfig, persist);
7373

7474
if (cacheWrite) {
7575
await writeRunnerResults(pluginMeta.slug, outputDir, {

packages/core/src/lib/implementation/execute-plugin.unit.test.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ describe('executePlugin', () => {
1818
vi.restoreAllMocks();
1919
});
2020

21-
it('should execute a valid plugin config', async () => {
21+
it('should execute a valid plugin config and pass runner params', async () => {
22+
const executePluginRunnerSpy = vi.spyOn(
23+
runnerModule,
24+
'executePluginRunner',
25+
);
26+
2227
await expect(
2328
executePlugin(MINIMAL_PLUGIN_CONFIG_MOCK, {
2429
persist: { outputDir: '' },
@@ -39,6 +44,11 @@ describe('executePlugin', () => {
3944
}),
4045
]),
4146
});
47+
48+
expect(executePluginRunnerSpy).toHaveBeenCalledWith(
49+
MINIMAL_PLUGIN_CONFIG_MOCK,
50+
{ outputDir: '' },
51+
);
4252
});
4353

4454
it('should try to read cache if cache.read is true', async () => {
@@ -102,7 +112,7 @@ describe('executePlugin', () => {
102112

103113
await expect(
104114
executePlugin(MINIMAL_PLUGIN_CONFIG_MOCK, {
105-
persist: { outputDir: 'dummy-path-result-is-mocked' },
115+
persist: { outputDir: MEMFS_VOLUME },
106116
cache: { read: true, write: false },
107117
}),
108118
).resolves.toStrictEqual({
@@ -122,6 +132,7 @@ describe('executePlugin', () => {
122132

123133
expect(executePluginRunnerSpy).toHaveBeenCalledWith(
124134
MINIMAL_PLUGIN_CONFIG_MOCK,
135+
{ outputDir: MEMFS_VOLUME },
125136
);
126137
});
127138

@@ -383,8 +394,8 @@ describe('executePlugins', () => {
383394
{
384395
...MINIMAL_PLUGIN_CONFIG_MOCK,
385396
runner: {
386-
command: 'node',
387-
args: ['-v'],
397+
command: 'echo',
398+
args: ['16'],
388399
outputFile: 'output.json',
389400
outputTransform: (outputs: unknown): Promise<AuditOutputs> =>
390401
Promise.resolve([
@@ -398,7 +409,7 @@ describe('executePlugins', () => {
398409
},
399410
},
400411
],
401-
persist: { outputDir: '.code-pushup' },
412+
persist: { outputDir: MEMFS_VOLUME },
402413
cache: { read: false, write: false },
403414
},
404415
{ progress: false },

packages/core/src/lib/implementation/runner.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { writeFile } from 'node:fs/promises';
33
import path from 'node:path';
44
import {
55
type AuditOutputs,
6+
type PersistConfig,
67
type PluginConfig,
78
type RunnerConfig,
89
type RunnerFunction,
@@ -14,6 +15,7 @@ import {
1415
executeProcess,
1516
fileExists,
1617
isVerbose,
18+
objectToCliArgs,
1719
readJsonFile,
1820
removeDirectoryIfExists,
1921
ui,
@@ -32,12 +34,13 @@ export type ValidatedRunnerResult = Omit<RunnerResult, 'audits'> & {
3234

3335
export async function executeRunnerConfig(
3436
cfg: RunnerConfig,
37+
config: Required<Pick<PersistConfig, 'outputDir'>>,
3538
): Promise<RunnerResult> {
3639
const { args, command, outputFile, outputTransform } = cfg;
3740

3841
const { duration, date } = await executeProcess({
3942
command,
40-
args,
43+
args: [...(args ?? []), ...objectToCliArgs(config)],
4144
observer: {
4245
onStdout: stdout => {
4346
if (isVerbose()) {
@@ -66,12 +69,13 @@ export async function executeRunnerConfig(
6669

6770
export async function executeRunnerFunction(
6871
runner: RunnerFunction,
72+
config: PersistConfig,
6973
): Promise<RunnerResult> {
7074
const date = new Date().toISOString();
7175
const start = performance.now();
7276

7377
// execute plugin runner
74-
const audits = await runner();
78+
const audits = await runner(config);
7579

7680
// create runner result
7781
return {
@@ -96,12 +100,13 @@ export class AuditOutputsMissingAuditError extends Error {
96100

97101
export async function executePluginRunner(
98102
pluginConfig: Pick<PluginConfig, 'audits' | 'runner'>,
103+
persist: Required<Pick<PersistConfig, 'outputDir'>>,
99104
): Promise<Omit<RunnerResult, 'audits'> & { audits: AuditOutputs }> {
100105
const { audits: pluginConfigAudits, runner } = pluginConfig;
101106
const runnerResult: RunnerResult =
102107
typeof runner === 'object'
103-
? await executeRunnerConfig(runner)
104-
: await executeRunnerFunction(runner);
108+
? await executeRunnerConfig(runner, persist)
109+
: await executeRunnerFunction(runner, persist);
105110
const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult;
106111

107112
const result = auditOutputsSchema.safeParse(unvalidatedAuditOutputs);

packages/models/docs/models-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ _Function._
14211421

14221422
_Parameters:_
14231423

1424-
- _none_
1424+
1. [PersistConfig](#persistconfig)
14251425

14261426
_Returns:_
14271427

packages/models/src/lib/runner-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { z } from 'zod/v4';
22
import { auditOutputsSchema } from './audit-output.js';
33
import { convertAsyncZodFunctionToSchema } from './implementation/function.js';
44
import { filePathSchema } from './implementation/schemas.js';
5+
import { persistConfigSchema } from './persist-config.js';
56

67
export const outputTransformSchema = convertAsyncZodFunctionToSchema(
78
z.function({
@@ -25,6 +26,7 @@ export type RunnerConfig = z.infer<typeof runnerConfigSchema>;
2526

2627
export const runnerFunctionSchema = convertAsyncZodFunctionToSchema(
2728
z.function({
29+
input: [persistConfigSchema],
2830
output: z.union([auditOutputsSchema, z.promise(auditOutputsSchema)]),
2931
}),
3032
);

packages/plugin-eslint/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { eslintPlugin } from './lib/eslint-plugin.js';
33
export default eslintPlugin;
44

55
export type { ESLintPluginConfig } from './lib/config.js';
6+
export { ESLINT_PLUGIN_SLUG } from './lib/constants.js';
67

78
export {
89
eslintConfigFromAllNxProjects,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const ESLINT_PLUGIN_SLUG = 'eslint';

packages/plugin-eslint/src/lib/eslint-plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
eslintPluginConfigSchema,
1010
eslintPluginOptionsSchema,
1111
} from './config.js';
12+
import { ESLINT_PLUGIN_SLUG } from './constants.js';
1213
import { listAuditsAndGroups } from './meta/index.js';
1314
import { createRunnerConfig } from './runner/index.js';
1415

@@ -60,7 +61,7 @@ export async function eslintPlugin(
6061
) as typeof import('../../package.json');
6162

6263
return {
63-
slug: 'eslint',
64+
slug: ESLINT_PLUGIN_SLUG,
6465
title: 'ESLint',
6566
icon: 'eslint',
6667
description: 'Official Code PushUp ESLint plugin',
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @ts-check
2+
import { createProjectGraphAsync } from '@nx/devkit';
3+
4+
const graph = await createProjectGraphAsync({
5+
exitOnError: true,
6+
resetDaemonClient: true,
7+
});
8+
9+
const projects = Object.values(graph.nodes)
10+
.filter(project => project.data.root === `packages/${project.name}`)
11+
.sort((a, b) => a.name.localeCompare(b.name));
12+
const targets = ['unit-test', 'int-test'];
13+
const excludes = targets.flatMap(target =>
14+
projects
15+
.filter(project => project.data.targets?.[target] == null)
16+
.map(project => ({ project: project.name, target })),
17+
);
18+
19+
const matrix = {
20+
project: projects.map(project => project.name),
21+
target: targets,
22+
exclude: excludes,
23+
};
24+
25+
console.info(JSON.stringify(matrix));

0 commit comments

Comments
 (0)