Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add options to disable loading dotenv files #14016

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/generated/cli/affected.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ Type: `boolean`

Show help

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/cli/exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Default: `[]`

Exclude certain projects from being processed

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/cli/run-many.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ Type: `boolean`

Show help

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/packages/nx/documents/affected.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ Type: `boolean`

Show help

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/packages/nx/documents/exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Default: `[]`

Exclude certain projects from being processed

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/packages/nx/documents/run-many.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ Type: `boolean`

Show help

### loadDotEnvFiles

Type: `boolean`

Default: `true`

### nx-bail

Type: `boolean`
Expand Down
7 changes: 6 additions & 1 deletion docs/generated/packages/nx/executors/run-commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@
},
"envFile": {
"type": "string",
"description": "You may specify a custom .env file path."
"description": "A custom .env file path to explicitly attempt to load. When this option is present 'loadDotEnvFile' and the task runner option 'loadDotEnvFiles' are ignored."
},
"loadDotEnvFile": {
"type": "boolean",
"default": true,
"description": "Determines if by default a '.env' file is attempted to be loaded from the current working directory. This behaviour can also be prevented by setting the task runner option 'loadDotEnvFiles' to false. Any explicit value for this option takes precedence over the task runner option."
},
"color": {
"type": "boolean",
Expand Down
1 change: 1 addition & 0 deletions docs/shared/reference/nx-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ by `"nx/tasks-runners/default"` and `"@nrwl/nx-cloud"`.
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cacheableOperations | defines the list of targets/operations that are cached by Nx |
| parallel | defines the max number of targets ran in parallel (in older versions of Nx you had to pass `--parallel --maxParallel=3` instead of `--parallel=3`) |
| loadDotEnvFiles | defines whether by default .env files will be loaded. To not load .env files use `--no-loadDotEnvFiles` (default `true`) |
| captureStderr | defines whether the cache captures stderr or just stdout |
| skipNxCache | defines whether the Nx Cache should be skipped (defaults to `false`) |
| cacheDirectory | defines where the local cache is stored (defaults to `node_modules/.cache/nx`) |
Expand Down
4 changes: 4 additions & 0 deletions packages/nx/src/command-line/nx-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,10 @@ function withRunOptions(yargs: yargs.Argv): yargs.Argv {
type: 'number',
hidden: true,
})
.option('loadDotEnvFiles', {
type: 'boolean',
default: true,
})
.options('runner', {
describe: 'This is the name of the tasks runner configured in nx.json',
type: 'string',
Expand Down
136 changes: 136 additions & 0 deletions packages/nx/src/executors/run-commands/run-commands.impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,93 @@ describe('Run Commands', () => {
expect(readFile(f)).toEqual('https://nrwl.io/');
});

it('should load the root .env file by default if "loadDotEnvFile" is true and process.env.NX_LOAD_DOT_ENV_FILES is not "true"', async () => {
let f = fileSync().name;
process.env.NX_LOAD_DOT_ENV_FILES = 'false';

const result = await runCommands(
{
commands: [
{
command: `echo $NRWL_SITE >> ${f}`,
},
],
loadDotEnvFile: true,
__unparsed__: [],
},
context
);

delete process.env.NX_LOAD_DOT_ENV_FILES;

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('https://nrwl.io/');
});

it('should NOT load the root .env file by default if option "loadDotEnvFile" is false', async () => {
let f = fileSync().name;
const result = await runCommands(
{
commands: [
{
command: `echo $NRWL_SITE >> ${f}`,
},
],
loadDotEnvFile: false,
__unparsed__: [],
},
context
);

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('');
});

it('should NOT load the root .env file by default if option "loadDotEnvFile" is false and process.env.NX_LOAD_DOT_ENV_FILES is "true"', async () => {
let f = fileSync().name;
process.env.NX_LOAD_DOT_ENV_FILES = 'true';

const result = await runCommands(
{
commands: [
{
command: `echo $NRWL_SITE >> ${f}`,
},
],
loadDotEnvFile: false,
__unparsed__: [],
},
context
);

delete process.env.NX_LOAD_DOT_ENV_FILES;

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('');
});

it('should NOT load the root .env file by default if process.env.NX_LOAD_DOT_ENV_FILES is not "true"', async () => {
let f = fileSync().name;
process.env.NX_LOAD_DOT_ENV_FILES = 'false';

const result = await runCommands(
{
commands: [
{
command: `echo $NRWL_SITE >> ${f}`,
},
],
__unparsed__: [],
},
context
);

delete process.env.NX_LOAD_DOT_ENV_FILES;

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('');
});

it('should load the specified .env file instead of the root one', async () => {
const devEnv = fileSync().name;
writeFileSync(devEnv, 'NX_SITE=https://nx.dev/');
Expand All @@ -359,6 +446,55 @@ describe('Run Commands', () => {
expect(readFile(f)).toEqual('https://nx.dev/');
});

it('should load the specified .env file instead of the root one even when option "loadDotEnvFile" is false', async () => {
const devEnv = fileSync().name;
writeFileSync(devEnv, 'NX_SITE=https://nx.dev/');
let f = fileSync().name;
const result = await runCommands(
{
commands: [
{
command: `echo $NX_SITE >> ${f} && echo $NRWL_SITE >> ${f}`,
},
],
loadDotEnvFile: false,
envFile: devEnv,
__unparsed__: [],
},
context
);

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('https://nx.dev/');
});

it('should load the specified .env file instead of the root one even when process.env.NX_LOAD_DOT_ENV_FILES is not "true"', async () => {
const devEnv = fileSync().name;
writeFileSync(devEnv, 'NX_SITE=https://nx.dev/');
let f = fileSync().name;

process.env.NX_LOAD_DOT_ENV_FILES = 'false';

const result = await runCommands(
{
commands: [
{
command: `echo $NX_SITE >> ${f} && echo $NRWL_SITE >> ${f}`,
},
],
loadDotEnvFile: false,
envFile: devEnv,
__unparsed__: [],
},
context
);

delete process.env.NX_LOAD_DOT_ENV_FILES;

expect(result).toEqual(expect.objectContaining({ success: true }));
expect(readFile(f)).toEqual('https://nx.dev/');
});

it('should error if the specified .env file does not exist', async () => {
let f = fileSync().name;
try {
Expand Down
18 changes: 15 additions & 3 deletions packages/nx/src/executors/run-commands/run-commands.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import * as chalk from 'chalk';

export const LARGE_BUFFER = 1024 * 1000000;

async function loadEnvVars(path?: string) {
async function loadEnvVars(options: RunCommandsOptions) {
const path = options.envFile;

if (path) {
const result = (await import('dotenv')).config({ path });
if (result.error) {
throw result.error;
}
} else {
} else if (
options.loadDotEnvFile === true ||
(options.loadDotEnvFile !== false &&
process.env.NX_LOAD_DOT_ENV_FILES === 'true')
) {
// NOTE: This is a bug? We shouldn't be loading the env file from CWD
// since the env file hierarchy has already been loaded by this point and all
// env vars are properly merged. For example, when running 'nx run proj:target'
// from the workspace root, this would reload the top-level .env file
// over top of the other env vars from the other .env files.
try {
(await import('dotenv')).config();
} catch {}
Expand Down Expand Up @@ -42,6 +53,7 @@ export interface RunCommandsOptions extends Json {
color?: boolean;
parallel?: boolean;
readyWhen?: string;
loadDotEnvFile?: boolean;
cwd?: string;
args?: string;
envFile?: string;
Expand Down Expand Up @@ -73,7 +85,7 @@ export default async function (
options: RunCommandsOptions,
context: ExecutorContext
): Promise<{ success: boolean }> {
await loadEnvVars(options.envFile);
await loadEnvVars(options);
const normalized = normalizeOptions(options);

if (options.readyWhen && !options.parallel) {
Expand Down
7 changes: 6 additions & 1 deletion packages/nx/src/executors/run-commands/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@
},
"envFile": {
"type": "string",
"description": "You may specify a custom .env file path."
"description": "A custom .env file path to explicitly attempt to load. When this option is present 'loadDotEnvFile' and the task runner option 'loadDotEnvFiles' are ignored."
},
"loadDotEnvFile": {
"type": "boolean",
"default": true,
"description": "Determines if by default a '.env' file is attempted to be loaded from the current working directory. This behaviour can also be prevented by setting the task runner option 'loadDotEnvFiles' to false. Any explicit value for this option takes precedence over the task runner option."
},
"color": {
"type": "boolean",
Expand Down
29 changes: 29 additions & 0 deletions packages/nx/src/tasks-runner/default-tasks-runner.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import defaultTasksRunner from './default-tasks-runner';

describe('defaultTasksRunner', () => {
it('should set pocess.env.NX_LOAD_DOT_ENV_FILES to "false" if runner option "loadDotEnvFiles" is false', async () => {
const tasks = [];
const options = {
loadDotEnvFiles: false,
lifeCycle: { startCommand: () => {}, endCommand: () => {} },
};
const context: any = {};
process.env.NX_LOAD_DOT_ENV_FILES = 'true';
expect(process.env.NX_LOAD_DOT_ENV_FILES).toBe('true');
await defaultTasksRunner(tasks, options, context);
expect(process.env.NX_LOAD_DOT_ENV_FILES).toBe('false');
});

it('should leave pocess.env.NX_LOAD_DOT_ENV_FILES as-is if runner option "loadDotEnvFiles" is not false', async () => {
const tasks = [];
const options = {
loadDotEnvFiles: true,
lifeCycle: { startCommand: () => {}, endCommand: () => {} },
};
const context: any = {};
process.env.NX_LOAD_DOT_ENV_FILES = 'true';
expect(process.env.NX_LOAD_DOT_ENV_FILES).toBe('true');
await defaultTasksRunner(tasks, options, context);
expect(process.env.NX_LOAD_DOT_ENV_FILES).toBe('true');
});
});
14 changes: 13 additions & 1 deletion packages/nx/src/tasks-runner/default-tasks-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DefaultTasksRunnerOptions {
lifeCycle: LifeCycle;
captureStderr?: boolean;
skipNxCache?: boolean;
loadDotEnvFiles?: boolean;
}

export const defaultTasksRunner: TasksRunner<
Expand All @@ -42,6 +43,13 @@ export const defaultTasksRunner: TasksRunner<
daemon: DaemonClient;
}
): Promise<{ [id: string]: TaskStatus }> => {
// Override the Nx env var NX_LOAD_DOT_ENV_FILES
// so that the executor can respect this env var
// to determine if env vars should be loaded.
if (options.loadDotEnvFiles === false) {
process.env.NX_LOAD_DOT_ENV_FILES = 'false';
}

if (
(options as any)['parallel'] === 'false' ||
(options as any)['parallel'] === false
Expand All @@ -57,7 +65,11 @@ export const defaultTasksRunner: TasksRunner<

options.lifeCycle.startCommand();
try {
return await runAllTasks(tasks, options, context);
if (tasks.length) {
return await runAllTasks(tasks, options, context);
} else {
return {};
}
} finally {
options.lifeCycle.endCommand();
}
Expand Down