diff --git a/.travis.yml b/.travis.yml index aca7a0f..7d64a27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ sudo: required dist: trusty +os: + - linux + # - windows Can not use windows at this time as secrets are not supported yet + language: node_js node_js: - "8" diff --git a/CHANGELOG.md b/CHANGELOG.md index bc01b8f..2f9c2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - **Change**: Update package-lock.json file - **Feature**: Added support for asyncronous .env and .rc files - **Feature**: Added support for a programmatic API +- **Feature**: Added --use-shell option - **Fix**: Keep newline (`\n`) characters intact when parsing env files - **Change**: Added node v10 and v12 to build automation diff --git a/README.md b/README.md index 5461ad7..ca7daf8 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,6 @@ later environments overwritting earlier ones. Prevents overwritting of existing env vars on `process.env` - ### `--fallback` file usage option You can specify a `.env.local` (or any name) env file, add that to your `.gitignore` and use that @@ -97,6 +96,16 @@ in your local development environment. Then you can use a regular `.env` file in with production configs that you can commit to a private/protected repo. When `env-cmd` cannot find the `.env.local` file it will fallback to looking for a regular `.env` file. +### `--use-shell` + +Executes the command within a new shell instance. This is useful if you want to string multiple +commands together and share the same env vars. + +**Terminal** +```sh +./node_modules/.bin/env-cmd -f ./test/.env --use-shell "node run lint && node test" +``` + ### Asynchronous env file support EnvCmd supports reading from asynchronous `.env` files. Instead of using a `.env` file, pass in a `.js` @@ -151,7 +160,8 @@ Executes a command in a new child process with the given options - **`environments`** { `string[]` }: List of environment to read from the `.rc` file - **`filePath`** { `string` }: Custom path to the `.rc` file (defaults to: `./.env-cmdrc(|.js|.json)`) - **`options`** { `object` } - - **`noOverride`** { `boolean` }: Prevent `.env` file vars from overriding existing `process.env` vars + - **`noOverride`** { `boolean` }: Prevent `.env` file vars from overriding existing `process.env` vars (default: `false`) + - **`useShell`** { `boolean` }: Runs command inside a new shell instance (default: `false`) - **Returns** { `Promise` }: key is env var name and value is the env var value ### `GetEnvVars` diff --git a/lib/env-cmd.js b/lib/env-cmd.js index 2730e26..8768475 100644 --- a/lib/env-cmd.js +++ b/lib/env-cmd.js @@ -50,6 +50,7 @@ function EnvCmd({ command, commandArgs, envFile, rc, options }) { // Execute the command with the given environment variables const proc = spawn_1.spawn(command, commandArgs, { stdio: 'inherit', + shell: options.useShell, env }); // Handle any termination signals for parent and child proceses diff --git a/lib/parse-args.js b/lib/parse-args.js index 3d029be..0338708 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -15,27 +15,23 @@ function parseArgs(args) { .option('-e, --environments [env1,env2,...]', 'The rc-file environments to select', utils_1.parseArgList) .option('--fallback', 'Enables auto fallback to default env file location ./.env') .option('--no-override', 'Do not override existing env vars on process.env') + .option('--use-shell', 'Make env variables available to multiple commands and as command line arguments') .parse(['_', '_', ...args]); // get the command and command args const command = program.args[0]; const commandArgs = program.args.slice(1); const noOverride = !program.override; + const useShell = !!program.useShell; let rc; if (program.environments && program.environments.length) { rc = rc || {}; rc.environments = program.environments; - } - if (program.rcFile) { - rc = rc || {}; rc.filePath = program.rcFile; } let envFile; if (program.file) { envFile = envFile || {}; envFile.filePath = program.file; - } - if (program.fallback != null) { - envFile = envFile || {}; envFile.fallback = program.fallback; } return { @@ -44,7 +40,8 @@ function parseArgs(args) { envFile, rc, options: { - noOverride + noOverride, + useShell } }; } diff --git a/src/env-cmd.ts b/src/env-cmd.ts index 5b4bef3..3be4926 100644 --- a/src/env-cmd.ts +++ b/src/env-cmd.ts @@ -42,6 +42,7 @@ export async function EnvCmd ( // Execute the command with the given environment variables const proc = spawn(command, commandArgs, { stdio: 'inherit', + shell: options.useShell, env }) diff --git a/src/parse-args.ts b/src/parse-args.ts index a527674..220587d 100644 --- a/src/parse-args.ts +++ b/src/parse-args.ts @@ -15,20 +15,19 @@ export function parseArgs (args: string[]): EnvCmdOptions { .option('-e, --environments [env1,env2,...]', 'The rc-file environments to select', parseArgList) .option('--fallback', 'Enables auto fallback to default env file location ./.env') .option('--no-override', 'Do not override existing env vars on process.env') + .option('--use-shell', 'Make env variables available to multiple commands and as command line arguments') .parse(['_', '_', ...args]) // get the command and command args const command = program.args[0] const commandArgs = program.args.slice(1) const noOverride = !program.override + const useShell = !!program.useShell let rc: any if (program.environments && program.environments.length) { rc = rc || {} rc.environments = program.environments - } - if (program.rcFile) { - rc = rc || {} rc.filePath = program.rcFile } @@ -36,9 +35,6 @@ export function parseArgs (args: string[]): EnvCmdOptions { if (program.file) { envFile = envFile || {} envFile.filePath = program.file - } - if (program.fallback != null) { - envFile = envFile || {} envFile.fallback = program.fallback } @@ -48,7 +44,8 @@ export function parseArgs (args: string[]): EnvCmdOptions { envFile, rc, options: { - noOverride + noOverride, + useShell } } } diff --git a/src/parse-env-file.ts b/src/parse-env-file.ts index c4683e2..e217d11 100644 --- a/src/parse-env-file.ts +++ b/src/parse-env-file.ts @@ -80,4 +80,4 @@ export function stripComments (envString: string): string { export function stripEmptyLines (envString: string): string { const emptyLinesRegex = /(^\n)/gim return envString.replace(emptyLinesRegex, '') -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index 546d1e9..a7fc9a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,5 +14,6 @@ export interface EnvCmdOptions extends GetEnvVarOptions { commandArgs: string[] options?: { noOverride?: boolean + useShell?: boolean } } diff --git a/test/env-cmd.spec.ts b/test/env-cmd.spec.ts index 2eabf29..221f706 100644 --- a/test/env-cmd.spec.ts +++ b/test/env-cmd.spec.ts @@ -60,7 +60,7 @@ describe('EnvCmd', (): void => { assert.equal(spawnStub.callCount, 1) }) - it('should if noOverride option is false/missing it should override existing env vars', + it('should should override existing env vars if noOverride option is false/missing', async (): Promise => { process.env.BOB = 'cool' getEnvVarsStub.returns({ BOB: 'test' }) @@ -82,7 +82,7 @@ describe('EnvCmd', (): void => { } ) - it('should if noOverride option is true it should not override existing env vars', + it('should should not override existing env vars if noOverride option is true', async (): Promise => { process.env.BOB = 'cool' getEnvVarsStub.returns({ BOB: 'test' }) @@ -106,4 +106,29 @@ describe('EnvCmd', (): void => { assert.equal(spawnStub.args[0][2].env.BOB, 'cool') } ) + + it('should should spawn process with shell option if useShell option is true', + async (): Promise => { + process.env.BOB = 'cool' + getEnvVarsStub.returns({ BOB: 'test' }) + await envCmdLib.EnvCmd({ + command: 'node', + commandArgs: ['-v'], + envFile: { + filePath: './.env', + fallback: true + }, + rc: { + environments: ['dev'], + filePath: './.rc' + }, + options: { + useShell: true + } + }) + assert.equal(getEnvVarsStub.callCount, 1) + assert.equal(spawnStub.callCount, 1) + assert.equal(spawnStub.args[0][2].shell, true) + } + ) }) diff --git a/test/parse-args.spec.ts b/test/parse-args.spec.ts index f875cd9..54aa526 100644 --- a/test/parse-args.spec.ts +++ b/test/parse-args.spec.ts @@ -36,6 +36,12 @@ describe('parseArgs', (): void => { assert.isTrue(res.options!.noOverride) }) + it('should parse use shell option', (): void => { + const res = parseArgs(['-e', environments[0], '--use-shell', command, ...commandArgs]) + assert.exists(res.options) + assert.isTrue(res.options!.useShell) + }) + it('should parse rc file path', (): void => { const res = parseArgs(['-e', environments[0], '-r', rcFilePath, command, ...commandArgs]) assert.exists(res.rc)