Skip to content

Commit 0396b84

Browse files
authored
refactor(nx-plugin): add env vars to executor (#1161)
1 parent 0cf7d2a commit 0396b84

File tree

17 files changed

+204
-83
lines changed

17 files changed

+204
-83
lines changed
Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import { afterEach, expect, vi } from 'vitest';
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
import { executorContext } from '@code-pushup/test-nx-utils';
33
import * as executeProcessModule from '../../internal/execute-process.js';
4-
import runAutorunExecutor from './executor.js';
4+
import runCliExecutor from './executor.js';
55
import * as utils from './utils.js';
66

7-
describe('runAutorunExecutor', () => {
8-
const parseAutorunExecutorOptionsSpy = vi.spyOn(
9-
utils,
10-
'parseAutorunExecutorOptions',
11-
);
7+
describe('runCliExecutor', () => {
8+
const parseCliExecutorOptionsSpy = vi.spyOn(utils, 'parseCliExecutorOptions');
129
const executeProcessSpy = vi.spyOn(executeProcessModule, 'executeProcess');
1310

1411
beforeEach(() => {
@@ -22,22 +19,22 @@ describe('runAutorunExecutor', () => {
2219
});
2320

2421
afterEach(() => {
25-
parseAutorunExecutorOptionsSpy.mockReset();
22+
parseCliExecutorOptionsSpy.mockRestore();
2623
executeProcessSpy.mockReset();
2724
});
2825

2926
it('should normalize context, parse CLI options and execute command', async () => {
3027
expect(process.env).not.toHaveProperty('CP_VERBOSE', 'true');
31-
const output = await runAutorunExecutor(
28+
const output = await runCliExecutor(
3229
{ verbose: true },
3330
executorContext('utils'),
3431
);
3532
expect(output.success).toBe(true);
3633

37-
expect(parseAutorunExecutorOptionsSpy).toHaveBeenCalledTimes(1);
34+
expect(parseCliExecutorOptionsSpy).toHaveBeenCalledTimes(1);
3835

3936
//is context normalized
40-
expect(parseAutorunExecutorOptionsSpy).toHaveBeenCalledWith(
37+
expect(parseCliExecutorOptionsSpy).toHaveBeenCalledWith(
4138
{ verbose: true },
4239
expect.objectContaining({
4340
projectConfig: expect.objectContaining({ name: 'utils' }),
@@ -49,7 +46,24 @@ describe('runAutorunExecutor', () => {
4946
args: expect.arrayContaining(['@code-pushup/cli']),
5047
cwd: process.cwd(),
5148
});
49+
});
5250

53-
expect(process.env).toHaveProperty('CP_VERBOSE', 'true');
51+
it('should forward env options to executeProcess', async () => {
52+
const output = await runCliExecutor(
53+
{
54+
verbose: true,
55+
env: { TEST_VALUE: '42' },
56+
},
57+
executorContext('utils'),
58+
);
59+
expect(output.success).toBe(true);
60+
expect(executeProcessSpy).toHaveBeenCalledTimes(1);
61+
expect(executeProcessSpy).toHaveBeenCalledWith(
62+
expect.objectContaining({
63+
env: expect.objectContaining({
64+
TEST_VALUE: '42',
65+
}),
66+
}),
67+
);
5468
});
5569
});

packages/nx-plugin/src/executors/cli/executor.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { ExecutorContext } from '@nx/devkit';
22
import { executeProcess } from '../../internal/execute-process.js';
33
import { normalizeContext } from '../internal/context.js';
4-
import type { AutorunCommandExecutorOptions } from './schema.js';
5-
import { parseAutorunExecutorOptions } from './utils.js';
4+
import type { CliCommandExecutorOptions } from './schema.js';
5+
import { parseCliExecutorOptions } from './utils.js';
66

77
export type ExecutorOutput = {
88
success: boolean;
@@ -11,44 +11,58 @@ export type ExecutorOutput = {
1111
};
1212

1313
/* eslint-disable-next-line max-lines-per-function */
14-
export default async function runAutorunExecutor(
15-
terminalAndExecutorOptions: AutorunCommandExecutorOptions,
14+
export default async function runCliExecutor(
15+
terminalAndExecutorOptions: CliCommandExecutorOptions,
1616
context: ExecutorContext,
1717
): Promise<ExecutorOutput> {
1818
const { objectToCliArgs, formatCommandStatus, logger, stringifyError } =
1919
await import('@code-pushup/utils');
2020
const normalizedContext = normalizeContext(context);
21-
const cliArgumentObject = parseAutorunExecutorOptions(
22-
terminalAndExecutorOptions,
23-
normalizedContext,
24-
);
25-
const { command: cliCommand } = terminalAndExecutorOptions;
26-
const { verbose = false, dryRun, bin, ...restArgs } = cliArgumentObject;
21+
const {
22+
command: cliCommand,
23+
verbose = false,
24+
dryRun,
25+
env: executorEnv,
26+
bin,
27+
...restArgs
28+
} = parseCliExecutorOptions(terminalAndExecutorOptions, normalizedContext);
29+
// this sets `CP_VERBOSE=true` on process.env
2730
logger.setVerbose(verbose);
2831

2932
const command = bin ? `node` : 'npx';
30-
const positionals = [
33+
const args = [
3134
bin ?? '@code-pushup/cli',
3235
...(cliCommand ? [cliCommand] : []),
36+
...objectToCliArgs(restArgs),
3337
];
34-
const args = [...positionals, ...objectToCliArgs(restArgs)];
35-
const executorEnvVariables = {
38+
const loggedEnvVars = {
39+
...executorEnv,
3640
...(verbose && { CP_VERBOSE: 'true' }),
3741
};
3842
const commandString = formatCommandStatus([command, ...args].join(' '), {
3943
cwd: context.cwd,
40-
env: executorEnvVariables,
44+
env: loggedEnvVars,
4145
});
4246

4347
if (dryRun) {
4448
logger.warn(`DryRun execution of: ${commandString}`);
4549
} else {
4650
try {
47-
logger.debug(`With env vars: ${executorEnvVariables}`);
51+
logger.debug(`Run CLI with env vars: ${loggedEnvVars}`);
4852
await executeProcess({
4953
command,
5054
args,
5155
...(context.cwd ? { cwd: context.cwd } : {}),
56+
...(executorEnv && Object.keys(executorEnv).length > 0
57+
? {
58+
env: {
59+
// if env is undefined, executeProcess extends process.env by default
60+
...process.env,
61+
// we don't pass `CP_VERBOSE=true` as it is handled inside logger.setVerbose
62+
...executorEnv,
63+
},
64+
}
65+
: {}),
5266
});
5367
} catch (error) {
5468
logger.error(stringifyError(error));

packages/nx-plugin/src/executors/cli/executor.unit.test.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { afterAll, afterEach, beforeEach, expect, vi } from 'vitest';
1+
import {
2+
afterAll,
3+
afterEach,
4+
beforeAll,
5+
beforeEach,
6+
describe,
7+
expect,
8+
it,
9+
vi,
10+
} from 'vitest';
211
import { executorContext } from '@code-pushup/test-nx-utils';
312
import { MEMFS_VOLUME } from '@code-pushup/test-utils';
413
import * as executeProcessModule from '../../internal/execute-process.js';
5-
import runAutorunExecutor from './executor.js';
14+
import runCliExecutor from './executor.js';
615

7-
describe('runAutorunExecutor', () => {
16+
describe('runCliExecutor', () => {
817
const processEnvCP = Object.fromEntries(
918
Object.entries(process.env).filter(([k]) => k.startsWith('CP_')),
1019
);
@@ -41,7 +50,7 @@ describe('runAutorunExecutor', () => {
4150
});
4251

4352
it('should call executeProcess with return result', async () => {
44-
const output = await runAutorunExecutor({}, executorContext('utils'));
53+
const output = await runCliExecutor({}, executorContext('utils'));
4554
expect(output.success).toBe(true);
4655
expect(output.command).toMatch('npx @code-pushup/cli');
4756
expect(executeProcessSpy).toHaveBeenCalledWith({
@@ -52,15 +61,16 @@ describe('runAutorunExecutor', () => {
5261
});
5362

5463
it('should normalize context', async () => {
55-
const output = await runAutorunExecutor(
64+
const output = await runCliExecutor(
5665
{},
5766
{
5867
...executorContext('utils'),
5968
cwd: 'cwd-form-context',
6069
},
6170
);
6271
expect(output.success).toBe(true);
63-
expect(output.command).toMatch('utils');
72+
expect(output.command).toMatch('npx @code-pushup/cli');
73+
expect(output.command).toContain('cwd-form-context');
6474
expect(executeProcessSpy).toHaveBeenCalledWith({
6575
command: 'npx',
6676
args: expect.arrayContaining(['@code-pushup/cli']),
@@ -69,7 +79,7 @@ describe('runAutorunExecutor', () => {
6979
});
7080

7181
it('should process executorOptions', async () => {
72-
const output = await runAutorunExecutor(
82+
const output = await runCliExecutor(
7383
{ output: 'code-pushup.config.json', persist: { filename: 'REPORT' } },
7484
executorContext('testing-utils'),
7585
);
@@ -79,7 +89,7 @@ describe('runAutorunExecutor', () => {
7989
});
8090

8191
it('should create command from context and options if no api key is set', async () => {
82-
const output = await runAutorunExecutor(
92+
const output = await runCliExecutor(
8393
{ persist: { filename: 'REPORT', format: ['md', 'json'] } },
8494
executorContext('core'),
8595
);
@@ -91,7 +101,7 @@ describe('runAutorunExecutor', () => {
91101

92102
it('should create command from context, options and arguments if api key is set', async () => {
93103
vi.stubEnv('CP_API_KEY', 'cp_1234567');
94-
const output = await runAutorunExecutor(
104+
const output = await runCliExecutor(
95105
{
96106
persist: { filename: 'REPORT', format: ['md', 'json'] },
97107
upload: { project: 'CLI' },
@@ -107,7 +117,7 @@ describe('runAutorunExecutor', () => {
107117
});
108118

109119
it('should set env var information if verbose is set', async () => {
110-
const output = await runAutorunExecutor(
120+
const output = await runCliExecutor(
111121
{
112122
verbose: true,
113123
},
@@ -131,8 +141,8 @@ describe('runAutorunExecutor', () => {
131141
expect(logger.warn).toHaveBeenCalledTimes(0);
132142
});
133143

134-
it('should log env var in dryRun information if verbose is set', async () => {
135-
const output = await runAutorunExecutor(
144+
it('should log CP_VERBOSE env var in dryRun information if verbose is set', async () => {
145+
const output = await runCliExecutor(
136146
{
137147
dryRun: true,
138148
verbose: true,
@@ -150,7 +160,7 @@ describe('runAutorunExecutor', () => {
150160
});
151161

152162
it('should log command if dryRun is set', async () => {
153-
await runAutorunExecutor({ dryRun: true }, executorContext('utils'));
163+
await runCliExecutor({ dryRun: true }, executorContext('utils'));
154164

155165
expect(logger.command).toHaveBeenCalledTimes(0);
156166
expect(logger.warn).toHaveBeenCalledTimes(1);

packages/nx-plugin/src/executors/cli/schema.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "http://json-schema.org/schema",
3-
"$id": "AutorunExecutorOptions",
3+
"$id": "CliExecutorOptions",
44
"title": "CodePushup CLI autorun executor",
55
"description": "Executes the @code-pushup/cli autorun command See: https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#autorun-command",
66
"type": "object",
@@ -21,6 +21,13 @@
2121
"type": "string",
2222
"description": "Path to Code PushUp CLI"
2323
},
24+
"env": {
25+
"type": "object",
26+
"additionalProperties": {
27+
"type": "string"
28+
},
29+
"description": "Environment variables added to Code PushUp CLI process"
30+
},
2431
"verbose": {
2532
"type": "boolean",
2633
"description": "Print additional logs"

packages/nx-plugin/src/executors/cli/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import type {
88

99
export type PrintConfigOptions = { output?: string };
1010
export type PrintConfigCommandExecutorOptions = PrintConfigOptions;
11-
export type AutorunCommandExecutorOnlyOptions = ProjectExecutorOnlyOptions &
11+
export type CliCommandExecutorOnlyOptions = ProjectExecutorOnlyOptions &
1212
CollectExecutorOnlyOptions &
1313
GeneralExecutorOnlyOptions;
1414

15-
export type AutorunCommandExecutorOptions = Partial<
15+
export type CliCommandExecutorOptions = Partial<
1616
{
1717
upload: Partial<UploadConfig>;
1818
persist: Partial<PersistConfig>;
19-
} & AutorunCommandExecutorOnlyOptions &
19+
} & CliCommandExecutorOnlyOptions &
2020
GlobalExecutorOptions
2121
> &
2222
PrintConfigOptions;

packages/nx-plugin/src/executors/cli/utils.int.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { expect, vi } from 'vitest';
1+
import { describe, expect, it, vi } from 'vitest';
22
import type { UploadConfig } from '@code-pushup/models';
33
import { normalizedExecutorContext } from '../../../mock/utils/executor.js';
44
import * as config from '../internal/config.js';
5-
import { parseAutorunExecutorOptions } from './utils.js';
5+
import { parseCliExecutorOptions } from './utils.js';
66

7-
describe('parseAutorunExecutorOptions', () => {
7+
describe('parseCliExecutorOptions', () => {
88
const persistConfigSpy = vi.spyOn(config, 'persistConfig');
99
const uploadConfigSpy = vi.spyOn(config, 'uploadConfig');
1010
const globalConfigSpy = vi.spyOn(config, 'globalConfig');
@@ -17,7 +17,7 @@ describe('parseAutorunExecutorOptions', () => {
1717
});
1818

1919
it('should call child config functions with options', () => {
20-
parseAutorunExecutorOptions(
20+
parseCliExecutorOptions(
2121
{
2222
verbose: true,
2323
persist: { filename: 'my-name' },

packages/nx-plugin/src/executors/cli/utils.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import {
55
} from '../internal/config.js';
66
import type { NormalizedExecutorContext } from '../internal/context.js';
77
import type {
8-
AutorunCommandExecutorOnlyOptions,
9-
AutorunCommandExecutorOptions,
8+
CliCommandExecutorOnlyOptions,
9+
CliCommandExecutorOptions,
1010
PrintConfigCommandExecutorOptions,
1111
} from './schema.js';
1212

13-
export function parseAutorunExecutorOnlyOptions(
14-
options: Partial<AutorunCommandExecutorOnlyOptions>,
15-
): AutorunCommandExecutorOnlyOptions {
16-
const { projectPrefix, dryRun, onlyPlugins } = options;
13+
export function parseCliExecutorOnlyOptions(
14+
options: Partial<CliCommandExecutorOnlyOptions>,
15+
): CliCommandExecutorOnlyOptions {
16+
const { projectPrefix, dryRun, onlyPlugins, env, bin } = options;
1717
return {
1818
...(projectPrefix && { projectPrefix }),
1919
...(dryRun != null && { dryRun }),
2020
...(onlyPlugins && { onlyPlugins }),
21+
...(env && { env }),
22+
...(bin && { bin }),
2123
};
2224
}
2325

@@ -30,10 +32,10 @@ export function parsePrintConfigExecutorOptions(
3032
};
3133
}
3234

33-
export function parseAutorunExecutorOptions(
34-
options: Partial<AutorunCommandExecutorOptions>,
35+
export function parseCliExecutorOptions(
36+
options: Partial<CliCommandExecutorOptions>,
3537
normalizedContext: NormalizedExecutorContext,
36-
): AutorunCommandExecutorOptions {
38+
): CliCommandExecutorOptions {
3739
const { projectPrefix, persist, upload, command, output } = options;
3840
const needsUploadParams =
3941
command === 'upload' || command === 'autorun' || command === undefined;
@@ -44,7 +46,7 @@ export function parseAutorunExecutorOptions(
4446
const hasApiToken = uploadCfg?.apiKey != null;
4547
return {
4648
...parsePrintConfigExecutorOptions(options),
47-
...parseAutorunExecutorOnlyOptions(options),
49+
...parseCliExecutorOnlyOptions(options),
4850
...globalConfig(options, normalizedContext),
4951
...(output ? { output } : {}),
5052
persist: persistConfig({ projectPrefix, ...persist }, normalizedContext),

0 commit comments

Comments
 (0)