From ec36eebed485851eb75db923270990e96c30b58d Mon Sep 17 00:00:00 2001 From: orta Date: Sat, 3 Feb 2024 11:55:48 +0000 Subject: [PATCH 01/17] Add support for additional .env files in the cli = re: #9877 --- packages/cli/src/index.js | 26 +++++++++++++++ .../src/middleware/addAdditionalEnvFiles.js | 32 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 packages/cli/src/middleware/addAdditionalEnvFiles.js diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index ff8281269ee2..a0fcc99dee54 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -35,6 +35,7 @@ import * as upgradeCommand from './commands/upgrade' import { getPaths, findUp } from './lib' import { exitWithError } from './lib/exit' import * as updateCheck from './lib/updateCheck' +import { addAdditionalEnvFiles } from './middleware/addAdditionalEnvFiles' import { loadPlugins } from './plugin' import { startTelemetry, shutdownTelemetry } from './telemetry/index' @@ -104,6 +105,7 @@ process.env.RWJS_CWD = cwd // # Load .env, .env.defaults // // This should be done as early as possible, and the earliest we can do it is after setting `cwd`. +// Further down in middleware, we allow additional .env files to be loaded based on args. if (!process.env.REDWOOD_ENV_FILES_LOADED) { config({ @@ -181,6 +183,30 @@ async function runYargs() { .option('cwd', { describe: 'Working directory to use (where `redwood.toml` is located)', }) + .option('include-env', { + describe: + 'Add include running additional environment files to your command. These are incremental', + array: true, + }) + .example( + 'rw exec MigrateUsers --include-env prod --include-env stripe-prod', + 'run a script, and also include .env.prod and .env.stripe-prod' + ) + .middleware( + [ + // We've already handled `cwd` above, but it may still be in `argv`. + // We don't need it anymore so let's get rid of it. + // Likewise for `telemetry`. + (argv) => { + delete argv.cwd + delete argv.telemetry + }, + telemetry && telemetryMiddleware, + updateCheck.isEnabled() && updateCheck.updateCheckMiddleware, + addAdditionalEnvFiles(cwd), + ].filter(Boolean) + ) + .option('telemetry', { describe: 'Whether to send anonymous usage telemetry to RedwoodJS', boolean: true, diff --git a/packages/cli/src/middleware/addAdditionalEnvFiles.js b/packages/cli/src/middleware/addAdditionalEnvFiles.js new file mode 100644 index 000000000000..73fb428d53b5 --- /dev/null +++ b/packages/cli/src/middleware/addAdditionalEnvFiles.js @@ -0,0 +1,32 @@ +// @ts-check +import { existsSync } from 'fs' + +import { config } from 'dotenv' + +/** + * @param { string } cwd + * @returns {(yargs: import('yargs').Argv) => void} + */ +export const addAdditionalEnvFiles = (cwd) => (yargs) => { + // Allow for additional .env files to be included via --include-env + if ('includeEnv' in yargs && Array.isArray(yargs.includeEnv)) { + for (const suffix of yargs.includeEnv) { + const envPath = `${cwd}/.env.${suffix}` + if (!existsSync(envPath)) { + throw new Error( + `Couldn't find an .env file at '${envPath}' - which was noted via --include-env` + ) + } + + config({ path: `${cwd}/.env.${suffix}` }) + } + } + + // Support automatically matching a .env file based on the NODE_ENV + if (process.env.NODE_ENV) { + const processBasedEnvPath = `${cwd}/.env.${process.env.NODE_ENV}` + if (existsSync(processBasedEnvPath)) { + config({ path: processBasedEnvPath }) + } + } +} From 04f6550db5257f00433a5e4b6057062ffffbdaa3 Mon Sep 17 00:00:00 2001 From: orta Date: Sat, 3 Feb 2024 12:12:42 +0000 Subject: [PATCH 02/17] Fixes --- packages/cli/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index a0fcc99dee54..44cdc801af76 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -189,8 +189,8 @@ async function runYargs() { array: true, }) .example( - 'rw exec MigrateUsers --include-env prod --include-env stripe-prod', - 'run a script, and also include .env.prod and .env.stripe-prod' + 'yarn rw exec MigrateUsers --include-env prod --include-env stripe-prod', + '"Run a script, and also include .env.prod and .env.stripe-prod"' ) .middleware( [ From 76b4eea7c12826b525b237f20f5cdc7ceb70117f Mon Sep 17 00:00:00 2001 From: orta Date: Sat, 3 Feb 2024 12:15:55 +0000 Subject: [PATCH 03/17] Update the template --- packages/create-redwood-app/templates/js/gitignore.template | 4 +++- packages/create-redwood-app/templates/ts/gitignore.template | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/create-redwood-app/templates/js/gitignore.template b/packages/create-redwood-app/templates/js/gitignore.template index 9b8149560d9b..31d9637ede81 100644 --- a/packages/create-redwood-app/templates/js/gitignore.template +++ b/packages/create-redwood-app/templates/js/gitignore.template @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md diff --git a/packages/create-redwood-app/templates/ts/gitignore.template b/packages/create-redwood-app/templates/ts/gitignore.template index 9b8149560d9b..31d9637ede81 100644 --- a/packages/create-redwood-app/templates/ts/gitignore.template +++ b/packages/create-redwood-app/templates/ts/gitignore.template @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md From 25d3428759b904b55d1c23da89bdcce1109f38e4 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sat, 3 Feb 2024 14:19:08 +0000 Subject: [PATCH 04/17] Apply suggestions from code review Agree, with your copy changes :+1: Co-authored-by: Tobbe Lundberg --- packages/cli/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 44cdc801af76..cbb133566189 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -105,7 +105,7 @@ process.env.RWJS_CWD = cwd // # Load .env, .env.defaults // // This should be done as early as possible, and the earliest we can do it is after setting `cwd`. -// Further down in middleware, we allow additional .env files to be loaded based on args. +// Further down in middleware, we allow additional .env files to be loaded based on args. if (!process.env.REDWOOD_ENV_FILES_LOADED) { config({ @@ -185,7 +185,7 @@ async function runYargs() { }) .option('include-env', { describe: - 'Add include running additional environment files to your command. These are incremental', + 'Load additional .env files. These are incremental', array: true, }) .example( From 15798612e991fd806280480d586e6854b35e09c3 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 17:23:44 -0800 Subject: [PATCH 05/17] chore: lint fix --- packages/cli/src/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index cbb133566189..9114159753be 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -184,8 +184,7 @@ async function runYargs() { describe: 'Working directory to use (where `redwood.toml` is located)', }) .option('include-env', { - describe: - 'Load additional .env files. These are incremental', + describe: 'Load additional .env files. These are incremental', array: true, }) .example( From 6b679d03b105c371a391c6f5a147bb3577af7a17 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 17:33:47 -0800 Subject: [PATCH 06/17] fix: register middlewares once --- packages/cli/src/index.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 9114159753be..162a766726f0 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -191,21 +191,7 @@ async function runYargs() { 'yarn rw exec MigrateUsers --include-env prod --include-env stripe-prod', '"Run a script, and also include .env.prod and .env.stripe-prod"' ) - .middleware( - [ - // We've already handled `cwd` above, but it may still be in `argv`. - // We don't need it anymore so let's get rid of it. - // Likewise for `telemetry`. - (argv) => { - delete argv.cwd - delete argv.telemetry - }, - telemetry && telemetryMiddleware, - updateCheck.isEnabled() && updateCheck.updateCheckMiddleware, - addAdditionalEnvFiles(cwd), - ].filter(Boolean) - ) - + .middleware(addAdditionalEnvFiles(cwd)) .option('telemetry', { describe: 'Whether to send anonymous usage telemetry to RedwoodJS', boolean: true, From aba2606cb99b5d63f3c50416c66c52327fccf4b2 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:09:11 -0800 Subject: [PATCH 07/17] update test project fixtures --- __fixtures__/fragment-test-project/.gitignore | 4 +++- __fixtures__/test-project-rsa/.gitignore | 4 +++- __fixtures__/test-project-rsc-external-packages/.gitignore | 4 +++- __fixtures__/test-project/.gitignore | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/__fixtures__/fragment-test-project/.gitignore b/__fixtures__/fragment-test-project/.gitignore index 9b8149560d9b..31d9637ede81 100644 --- a/__fixtures__/fragment-test-project/.gitignore +++ b/__fixtures__/fragment-test-project/.gitignore @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md diff --git a/__fixtures__/test-project-rsa/.gitignore b/__fixtures__/test-project-rsa/.gitignore index 9b8149560d9b..31d9637ede81 100644 --- a/__fixtures__/test-project-rsa/.gitignore +++ b/__fixtures__/test-project-rsa/.gitignore @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md diff --git a/__fixtures__/test-project-rsc-external-packages/.gitignore b/__fixtures__/test-project-rsc-external-packages/.gitignore index 9b8149560d9b..31d9637ede81 100644 --- a/__fixtures__/test-project-rsc-external-packages/.gitignore +++ b/__fixtures__/test-project-rsc-external-packages/.gitignore @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md diff --git a/__fixtures__/test-project/.gitignore b/__fixtures__/test-project/.gitignore index 9b8149560d9b..31d9637ede81 100644 --- a/__fixtures__/test-project/.gitignore +++ b/__fixtures__/test-project/.gitignore @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md From d12b6d335cb629949271b96dac0f2ab0c2f1588a Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:13:45 -0800 Subject: [PATCH 08/17] refactor to use path join and envPath --- packages/cli/src/middleware/addAdditionalEnvFiles.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/middleware/addAdditionalEnvFiles.js b/packages/cli/src/middleware/addAdditionalEnvFiles.js index 73fb428d53b5..c29e9926334c 100644 --- a/packages/cli/src/middleware/addAdditionalEnvFiles.js +++ b/packages/cli/src/middleware/addAdditionalEnvFiles.js @@ -1,5 +1,6 @@ // @ts-check -import { existsSync } from 'fs' +import fs from 'fs' +import path from 'path' import { config } from 'dotenv' @@ -11,21 +12,21 @@ export const addAdditionalEnvFiles = (cwd) => (yargs) => { // Allow for additional .env files to be included via --include-env if ('includeEnv' in yargs && Array.isArray(yargs.includeEnv)) { for (const suffix of yargs.includeEnv) { - const envPath = `${cwd}/.env.${suffix}` - if (!existsSync(envPath)) { + const envPath = path.join(cwd, `.env.${suffix}`) + if (!fs.existsSync(envPath)) { throw new Error( `Couldn't find an .env file at '${envPath}' - which was noted via --include-env` ) } - config({ path: `${cwd}/.env.${suffix}` }) + config({ path: envPath }) } } // Support automatically matching a .env file based on the NODE_ENV if (process.env.NODE_ENV) { const processBasedEnvPath = `${cwd}/.env.${process.env.NODE_ENV}` - if (existsSync(processBasedEnvPath)) { + if (fs.existsSync(processBasedEnvPath)) { config({ path: processBasedEnvPath }) } } From 1eefb3e3fb695a326b5c50c24113890993f2071d Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:24:43 -0800 Subject: [PATCH 09/17] revert rsc change --- __fixtures__/test-project-rsc-external-packages/.gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/__fixtures__/test-project-rsc-external-packages/.gitignore b/__fixtures__/test-project-rsc-external-packages/.gitignore index 31d9637ede81..9b8149560d9b 100644 --- a/__fixtures__/test-project-rsc-external-packages/.gitignore +++ b/__fixtures__/test-project-rsc-external-packages/.gitignore @@ -1,8 +1,6 @@ .idea .DS_Store -.env* -!.env.example -!.env.defaults +.env .netlify .redwood/* !.redwood/README.md From dd10521e992eb3886ff56c174962abc589d521cd Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:26:03 -0800 Subject: [PATCH 10/17] reapply rsc fixture change --- .../test-project-rsc-external-packages-and-cells/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/__fixtures__/test-project-rsc-external-packages-and-cells/.gitignore b/__fixtures__/test-project-rsc-external-packages-and-cells/.gitignore index 9793c1b19029..a7adbb3c2499 100644 --- a/__fixtures__/test-project-rsc-external-packages-and-cells/.gitignore +++ b/__fixtures__/test-project-rsc-external-packages-and-cells/.gitignore @@ -1,6 +1,8 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify .redwood/* !.redwood/README.md From 7c8e0227cfa1a454950056a0a24b720b469d6274 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:30:23 -0800 Subject: [PATCH 11/17] clean up argv --- packages/cli/src/index.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 162a766726f0..a31e5f289442 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -191,7 +191,19 @@ async function runYargs() { 'yarn rw exec MigrateUsers --include-env prod --include-env stripe-prod', '"Run a script, and also include .env.prod and .env.stripe-prod"' ) - .middleware(addAdditionalEnvFiles(cwd)) + .middleware([ + addAdditionalEnvFiles(cwd), + // Once we've loaded the additional .env files, remove the option from yargs. + // If we leave it in, it and its alias will be passed to scripts run via `yarn rw exec` like... + // + // ``` + // { args: { _: [ 'exec' ], 'include-env': [ 'prod' ], includeEnv: [ 'prod' ], '$0': 'rw' } } + // ``` + (argv) => { + delete argv.includeEnv + delete argv['include-env'] + }, + ]) .option('telemetry', { describe: 'Whether to send anonymous usage telemetry to RedwoodJS', boolean: true, From c7a19d3c22ad2829e8cd789a165cfe01c1d1f908 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 18:57:53 -0800 Subject: [PATCH 12/17] work on tests --- .../redwood-app-env-collision/.env.base | 1 + .../redwood-app-env-collision/.env.collision | 1 + .../redwood-app-env-many/.env.dev | 1 + .../redwood-app-env-many/.env.prod | 1 + .../redwood-app-env-prod/.env.prod | 1 + .../__tests__/addAdditionalEnvFiles.test.js | 84 +++++++++++++++++++ 6 files changed, 89 insertions(+) create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod create mode 100644 packages/cli/src/__tests__/addAdditionalEnvFiles.test.js diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base new file mode 100644 index 000000000000..5676bc95ecf7 --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base @@ -0,0 +1 @@ +DATABASE_URL="postgresql://user:password@localhost:5432/mydb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision new file mode 100644 index 000000000000..b12bbdc62fda --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision @@ -0,0 +1 @@ +DATABASE_URL="postgresql://user:password@localhost:5432/mycollisiondb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev new file mode 100644 index 000000000000..76c15133b8e0 --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev @@ -0,0 +1 @@ +DEV_DATABASE_URL="postgresql://user:password@localhost:5432/mydevdb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod new file mode 100644 index 000000000000..e7a72b95961b --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod @@ -0,0 +1 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod new file mode 100644 index 000000000000..e7a72b95961b --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod @@ -0,0 +1 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js new file mode 100644 index 000000000000..556af045d19e --- /dev/null +++ b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js @@ -0,0 +1,84 @@ +import path from 'path' + +import { afterEach, beforeAll, describe, expect, it, test } from 'vitest' + +import { addAdditionalEnvFiles } from '../middleware/addAdditionalEnvFiles' + +describe('addAdditionalEnvFiles', () => { + let originalProcessEnv + beforeAll(() => { + originalProcessEnv = { ...process.env } + }) + afterEach(() => { + process.env = { ...originalProcessEnv } + }) + + it("doesn't load .env files if there are none to load", () => { + const fn = addAdditionalEnvFiles() + fn({}) + + const envWithAdditionalEnvFiles = { ...process.env } + expect(envWithAdditionalEnvFiles).toEqual(originalProcessEnv) + }) + + it("doesn't load .env files if not instructed to", () => { + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-prod') + ) + fn({}) + + const envWithAdditionalEnvFiles = { ...process.env } + expect(envWithAdditionalEnvFiles).toEqual(originalProcessEnv) + }) + + it('loads specified .env files', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-prod') + ) + fn({ includeEnv: ['prod'] }) + + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/myproddb' + ) + }) + + test('process.env is reset between tests', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + }) + + it('loads multiple .env files', () => { + expect(process.env).not.toHaveProperty('DEV_DATABASE_URL') + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-many') + ) + fn({ includeEnv: ['dev', 'prod'] }) + + expect(process.env).toHaveProperty( + 'DEV_DATABASE_URL', + 'postgresql://user:password@localhost:5432/mydevdb' + ) + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/myproddb' + ) + }) + + it('is additive (i.e. only adds to process.env, not overwrites)', () => { + expect(process.env).not.toHaveProperty('DATABASE_URL') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-collision') + ) + fn({ includeEnv: ['base', 'collision'] }) + + expect(process.env).toHaveProperty( + 'DATABASE_URL', + 'postgresql://user:password@localhost:5432/mydb' + ) + }) +}) From 01ffc44d4a8663e6849709a1e31911d833dfb567 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 19:34:51 -0800 Subject: [PATCH 13/17] Revert "work on tests" This reverts commit c7a19d3c22ad2829e8cd789a165cfe01c1d1f908. --- .../redwood-app-env-collision/.env.base | 1 - .../redwood-app-env-collision/.env.collision | 1 - .../redwood-app-env-many/.env.dev | 1 - .../redwood-app-env-many/.env.prod | 1 - .../redwood-app-env-prod/.env.prod | 1 - .../__tests__/addAdditionalEnvFiles.test.js | 84 ------------------- 6 files changed, 89 deletions(-) delete mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base delete mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision delete mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev delete mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod delete mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod delete mode 100644 packages/cli/src/__tests__/addAdditionalEnvFiles.test.js diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base deleted file mode 100644 index 5676bc95ecf7..000000000000 --- a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgresql://user:password@localhost:5432/mydb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision deleted file mode 100644 index b12bbdc62fda..000000000000 --- a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgresql://user:password@localhost:5432/mycollisiondb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev deleted file mode 100644 index 76c15133b8e0..000000000000 --- a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev +++ /dev/null @@ -1 +0,0 @@ -DEV_DATABASE_URL="postgresql://user:password@localhost:5432/mydevdb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod deleted file mode 100644 index e7a72b95961b..000000000000 --- a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod +++ /dev/null @@ -1 +0,0 @@ -PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod deleted file mode 100644 index e7a72b95961b..000000000000 --- a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod +++ /dev/null @@ -1 +0,0 @@ -PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js deleted file mode 100644 index 556af045d19e..000000000000 --- a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js +++ /dev/null @@ -1,84 +0,0 @@ -import path from 'path' - -import { afterEach, beforeAll, describe, expect, it, test } from 'vitest' - -import { addAdditionalEnvFiles } from '../middleware/addAdditionalEnvFiles' - -describe('addAdditionalEnvFiles', () => { - let originalProcessEnv - beforeAll(() => { - originalProcessEnv = { ...process.env } - }) - afterEach(() => { - process.env = { ...originalProcessEnv } - }) - - it("doesn't load .env files if there are none to load", () => { - const fn = addAdditionalEnvFiles() - fn({}) - - const envWithAdditionalEnvFiles = { ...process.env } - expect(envWithAdditionalEnvFiles).toEqual(originalProcessEnv) - }) - - it("doesn't load .env files if not instructed to", () => { - const fn = addAdditionalEnvFiles( - path.join(__dirname, '__fixtures__/redwood-app-env-prod') - ) - fn({}) - - const envWithAdditionalEnvFiles = { ...process.env } - expect(envWithAdditionalEnvFiles).toEqual(originalProcessEnv) - }) - - it('loads specified .env files', () => { - expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') - - const fn = addAdditionalEnvFiles( - path.join(__dirname, '__fixtures__/redwood-app-env-prod') - ) - fn({ includeEnv: ['prod'] }) - - expect(process.env).toHaveProperty( - 'PROD_DATABASE_URL', - 'postgresql://user:password@localhost:5432/myproddb' - ) - }) - - test('process.env is reset between tests', () => { - expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') - }) - - it('loads multiple .env files', () => { - expect(process.env).not.toHaveProperty('DEV_DATABASE_URL') - expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') - - const fn = addAdditionalEnvFiles( - path.join(__dirname, '__fixtures__/redwood-app-env-many') - ) - fn({ includeEnv: ['dev', 'prod'] }) - - expect(process.env).toHaveProperty( - 'DEV_DATABASE_URL', - 'postgresql://user:password@localhost:5432/mydevdb' - ) - expect(process.env).toHaveProperty( - 'PROD_DATABASE_URL', - 'postgresql://user:password@localhost:5432/myproddb' - ) - }) - - it('is additive (i.e. only adds to process.env, not overwrites)', () => { - expect(process.env).not.toHaveProperty('DATABASE_URL') - - const fn = addAdditionalEnvFiles( - path.join(__dirname, '__fixtures__/redwood-app-env-collision') - ) - fn({ includeEnv: ['base', 'collision'] }) - - expect(process.env).toHaveProperty( - 'DATABASE_URL', - 'postgresql://user:password@localhost:5432/mydb' - ) - }) -}) From 573df8ff4d9c45a0f6716706218014425d34d51e Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 19:35:07 -0800 Subject: [PATCH 14/17] work on tests --- .../redwood-app-env-collision/.env.base | 2 + .../redwood-app-env-collision/.env.collision | 2 + .../redwood-app-env-many/.env.dev | 1 + .../redwood-app-env-many/.env.prod | 1 + .../redwood-app-env-node-env/.env.bazinga | 2 + .../redwood-app-env-node-env/.env.prod | 1 + .../redwood-app-env-prod/.env.prod | 1 + .../__tests__/addAdditionalEnvFiles.test.js | 137 ++++++++++++++++++ 8 files changed, 147 insertions(+) create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.bazinga create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.prod create mode 100644 packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod create mode 100644 packages/cli/src/__tests__/addAdditionalEnvFiles.test.js diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base new file mode 100644 index 000000000000..ad03f36b034a --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.base @@ -0,0 +1,2 @@ +DATABASE_URL="postgresql://user:password@localhost:5432/mydb" +TEST_BASE=1 diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision new file mode 100644 index 000000000000..2b93192fdbb6 --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-collision/.env.collision @@ -0,0 +1,2 @@ +DATABASE_URL="postgresql://user:password@localhost:5432/mycollisiondb" +TEST_COLLISION=1 diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev new file mode 100644 index 000000000000..76c15133b8e0 --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.dev @@ -0,0 +1 @@ +DEV_DATABASE_URL="postgresql://user:password@localhost:5432/mydevdb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod new file mode 100644 index 000000000000..e7a72b95961b --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-many/.env.prod @@ -0,0 +1 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.bazinga b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.bazinga new file mode 100644 index 000000000000..e603ee15cbbe --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.bazinga @@ -0,0 +1,2 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/bazinga" +BAZINGA=1 diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.prod new file mode 100644 index 000000000000..e7a72b95961b --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-node-env/.env.prod @@ -0,0 +1 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod new file mode 100644 index 000000000000..e7a72b95961b --- /dev/null +++ b/packages/cli/src/__tests__/__fixtures__/redwood-app-env-prod/.env.prod @@ -0,0 +1 @@ +PROD_DATABASE_URL="postgresql://user:password@localhost:5432/myproddb" diff --git a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js new file mode 100644 index 000000000000..59b77908d7d8 --- /dev/null +++ b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js @@ -0,0 +1,137 @@ +import path from 'path' + +import { afterEach, beforeAll, describe, expect, it, test } from 'vitest' + +import { addAdditionalEnvFiles } from '../middleware/addAdditionalEnvFiles' + +describe('addAdditionalEnvFiles', () => { + let originalProcessEnv + beforeAll(() => { + originalProcessEnv = { ...process.env } + }) + afterEach(() => { + process.env = { ...originalProcessEnv } + }) + + it("doesn't load .env files if there are none to load", () => { + const fn = addAdditionalEnvFiles() + fn({}) + + expect(process.env).toEqual(originalProcessEnv) + }) + + it("doesn't load .env files if not instructed to", () => { + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-prod') + ) + fn({}) + + expect(process.env).toEqual(originalProcessEnv) + }) + + it('loads specified .env files', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-prod') + ) + fn({ includeEnv: ['prod'] }) + + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/myproddb' + ) + }) + + test('process.env is reset between tests', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + }) + + it('loads multiple .env files', () => { + expect(process.env).not.toHaveProperty('DEV_DATABASE_URL') + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-many') + ) + fn({ includeEnv: ['dev', 'prod'] }) + + expect(process.env).toHaveProperty( + 'DEV_DATABASE_URL', + 'postgresql://user:password@localhost:5432/mydevdb' + ) + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/myproddb' + ) + }) + + it('is additive (i.e. only adds to process.env, not overwrites)', () => { + expect(process.env).not.toHaveProperty('DATABASE_URL') + expect(process.env).not.toHaveProperty('TEST_BASE') + expect(process.env).not.toHaveProperty('TEST_COLLISION') + + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-collision') + ) + fn({ includeEnv: ['base', 'collision'] }) + + expect(process.env).toHaveProperty( + 'DATABASE_URL', + 'postgresql://user:password@localhost:5432/mydb' + ) + expect(process.env).toHaveProperty('TEST_BASE', '1') + expect(process.env).toHaveProperty('TEST_COLLISION', '1') + }) + + it('loads .env files based on NODE_ENV ', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + expect(process.env).not.toHaveProperty('BAZINGA') + + process.env.NODE_ENV = 'bazinga' + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-node-env') + ) + fn({}) + + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/bazinga' + ) + expect(process.env).toHaveProperty('BAZINGA', '1') + }) + + it('loads .env files based on NODE_ENV last', () => { + expect(process.env).not.toHaveProperty('PROD_DATABASE_URL') + expect(process.env).not.toHaveProperty('BAZINGA') + + process.env.NODE_ENV = 'bazinga' + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-node-env') + ) + fn({ + includeEnv: ['prod'], + }) + + expect(process.env).toHaveProperty( + 'PROD_DATABASE_URL', + 'postgresql://user:password@localhost:5432/myproddb' + ) + expect(process.env).toHaveProperty('BAZINGA', '1') + }) + + it("throws if it can't find a specified env file", () => { + const fn = addAdditionalEnvFiles( + path.join(__dirname, '__fixtures__/redwood-app-env-node-env') + ) + + try { + fn({ + includeEnv: ['missing'], + }) + } catch (error) { + // Just testing that the error message reports the file it tried to load. + expect(error.message).toMatch(/\.env\.missing/) + } + }) +}) From 89cac9c5402dcd449c6b692e73d05ab902bfab0e Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 19:48:32 -0800 Subject: [PATCH 15/17] rename flag --- .../cli/src/__tests__/addAdditionalEnvFiles.test.js | 10 +++++----- packages/cli/src/index.js | 10 +++++----- packages/cli/src/middleware/addAdditionalEnvFiles.js | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js index 59b77908d7d8..c3529df7ee61 100644 --- a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js +++ b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js @@ -35,7 +35,7 @@ describe('addAdditionalEnvFiles', () => { const fn = addAdditionalEnvFiles( path.join(__dirname, '__fixtures__/redwood-app-env-prod') ) - fn({ includeEnv: ['prod'] }) + fn({ includeEnvFiles: ['prod'] }) expect(process.env).toHaveProperty( 'PROD_DATABASE_URL', @@ -54,7 +54,7 @@ describe('addAdditionalEnvFiles', () => { const fn = addAdditionalEnvFiles( path.join(__dirname, '__fixtures__/redwood-app-env-many') ) - fn({ includeEnv: ['dev', 'prod'] }) + fn({ includeEnvFiles: ['dev', 'prod'] }) expect(process.env).toHaveProperty( 'DEV_DATABASE_URL', @@ -74,7 +74,7 @@ describe('addAdditionalEnvFiles', () => { const fn = addAdditionalEnvFiles( path.join(__dirname, '__fixtures__/redwood-app-env-collision') ) - fn({ includeEnv: ['base', 'collision'] }) + fn({ includeEnvFiles: ['base', 'collision'] }) expect(process.env).toHaveProperty( 'DATABASE_URL', @@ -110,7 +110,7 @@ describe('addAdditionalEnvFiles', () => { path.join(__dirname, '__fixtures__/redwood-app-env-node-env') ) fn({ - includeEnv: ['prod'], + includeEnvFiles: ['prod'], }) expect(process.env).toHaveProperty( @@ -127,7 +127,7 @@ describe('addAdditionalEnvFiles', () => { try { fn({ - includeEnv: ['missing'], + includeEnvFiles: ['missing'], }) } catch (error) { // Just testing that the error message reports the file it tried to load. diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index a31e5f289442..9490c763956e 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -183,12 +183,12 @@ async function runYargs() { .option('cwd', { describe: 'Working directory to use (where `redwood.toml` is located)', }) - .option('include-env', { + .option('include-env-files', { describe: 'Load additional .env files. These are incremental', array: true, }) .example( - 'yarn rw exec MigrateUsers --include-env prod --include-env stripe-prod', + 'yarn rw exec MigrateUsers --include-env-files prod stripe-prod', '"Run a script, and also include .env.prod and .env.stripe-prod"' ) .middleware([ @@ -197,11 +197,11 @@ async function runYargs() { // If we leave it in, it and its alias will be passed to scripts run via `yarn rw exec` like... // // ``` - // { args: { _: [ 'exec' ], 'include-env': [ 'prod' ], includeEnv: [ 'prod' ], '$0': 'rw' } } + // { args: { _: [ 'exec' ], 'include-env-files': [ 'prod' ], includeEnvFiles: [ 'prod' ], '$0': 'rw' } } // ``` (argv) => { - delete argv.includeEnv - delete argv['include-env'] + delete argv.includeEnvFiles + delete argv['include-env-files'] }, ]) .option('telemetry', { diff --git a/packages/cli/src/middleware/addAdditionalEnvFiles.js b/packages/cli/src/middleware/addAdditionalEnvFiles.js index c29e9926334c..ecc7f096ea4a 100644 --- a/packages/cli/src/middleware/addAdditionalEnvFiles.js +++ b/packages/cli/src/middleware/addAdditionalEnvFiles.js @@ -10,8 +10,8 @@ import { config } from 'dotenv' */ export const addAdditionalEnvFiles = (cwd) => (yargs) => { // Allow for additional .env files to be included via --include-env - if ('includeEnv' in yargs && Array.isArray(yargs.includeEnv)) { - for (const suffix of yargs.includeEnv) { + if ('includeEnvFiles' in yargs && Array.isArray(yargs.includeEnvFiles)) { + for (const suffix of yargs.includeEnvFiles) { const envPath = path.join(cwd, `.env.${suffix}`) if (!fs.existsSync(envPath)) { throw new Error( From 754585b2ad1ce1bf7a0f879dae343aa48247a588 Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 20:02:42 -0800 Subject: [PATCH 16/17] changelog and closing changes --- CHANGELOG.md | 23 +++++++++++++++++++ .../__tests__/addAdditionalEnvFiles.test.js | 2 +- .../src/middleware/addAdditionalEnvFiles.js | 4 ++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2108ab360d75..d640b362f7e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ ## Unreleased +- Add support for additional env var files (#9961) + + Fixes #9877. This PR adds a new middleware step to the CLI that looks for an `--include-env-files` flag and includes `.env.[file]` to the list of dotfiles to load. This PR also introduces functionality so that `.env.[file]` files are loaded based on `NODE_ENV`. + + Using the `--include-env-files` flag: + + ```bash + yarn rw exec myScript --include-env-files prod stripe-prod + # Alternatively you can specify the flag twice: + yarn rw exec myScript --include-env-files prod --include-env-files stripe-prod + ``` + + Using `NODE_ENV`: + + ``` + # loads .env.production + NODE_ENV=production yarn rw exec myScript + ``` + + These files are loaded in addition to `.env` and `.env.defaults` and more generally are additive. Subsequent dotfiles won't overwrite environment variables defined previous ones. As such, files loaded via NODE_ENV have lower priority than those loaded specifically via `--include-env-files`. + + Note that this feature is mainly for local scripting. Most deploy providers don't let you upload dotfiles and usually have their own way of determining environments. + - fix(render): reduce memory and handle server file This PR improves Render deploys by reducing memory consumption and fixing it so that it uses the server file if it's present. diff --git a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js index c3529df7ee61..fc20fe13079b 100644 --- a/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js +++ b/packages/cli/src/__tests__/addAdditionalEnvFiles.test.js @@ -14,7 +14,7 @@ describe('addAdditionalEnvFiles', () => { }) it("doesn't load .env files if there are none to load", () => { - const fn = addAdditionalEnvFiles() + const fn = addAdditionalEnvFiles(__dirname) fn({}) expect(process.env).toEqual(originalProcessEnv) diff --git a/packages/cli/src/middleware/addAdditionalEnvFiles.js b/packages/cli/src/middleware/addAdditionalEnvFiles.js index ecc7f096ea4a..7186dfd78dcd 100644 --- a/packages/cli/src/middleware/addAdditionalEnvFiles.js +++ b/packages/cli/src/middleware/addAdditionalEnvFiles.js @@ -23,9 +23,9 @@ export const addAdditionalEnvFiles = (cwd) => (yargs) => { } } - // Support automatically matching a .env file based on the NODE_ENV + // Support automatically matching a .env file based on NODE_ENV if (process.env.NODE_ENV) { - const processBasedEnvPath = `${cwd}/.env.${process.env.NODE_ENV}` + const processBasedEnvPath = path.join(cwd, `.env.${process.env.NODE_ENV}`) if (fs.existsSync(processBasedEnvPath)) { config({ path: processBasedEnvPath }) } From 385f7c913e572bc1a618019a69f300c9bdcaf12c Mon Sep 17 00:00:00 2001 From: Dominic Saadi Date: Fri, 23 Feb 2024 20:24:29 -0800 Subject: [PATCH 17/17] update server test --- tasks/server-tests/bothServer.test.mts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasks/server-tests/bothServer.test.mts b/tasks/server-tests/bothServer.test.mts index 4e6ec138c022..ab0fde7f26b5 100644 --- a/tasks/server-tests/bothServer.test.mts +++ b/tasks/server-tests/bothServer.test.mts @@ -20,6 +20,8 @@ describe('rw serve', () => { --version Show version number [boolean] --cwd Working directory to use (where \`redwood.toml\` is located) + --include-env-files Load additional .env files. These + are incremental [array] --telemetry Whether to send anonymous usage telemetry to RedwoodJS [boolean] --webPort, --web-port The port for the web server to @@ -65,6 +67,8 @@ describe('rw serve', () => { --version Show version number [boolean] --cwd Working directory to use (where \`redwood.toml\` is located) + --include-env-files Load additional .env files. These + are incremental [array] --telemetry Whether to send anonymous usage telemetry to RedwoodJS [boolean] --webPort, --web-port The port for the web server to