Skip to content

Commit

Permalink
Merge 3656bb0 into 0ff6bad
Browse files Browse the repository at this point in the history
  • Loading branch information
toddbluhm committed Nov 16, 2019
2 parents 0ff6bad + 3656bb0 commit dc4cf6d
Show file tree
Hide file tree
Showing 25 changed files with 497 additions and 183 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog

## 10.1.0 - Pending

- **Feature**: Added a new `--verbose` flag that prints additional debugging info to `console.info`
- **Change**: Updated `commander` dependency to `v4`

## 10.0.1

- **Fix**: Fixed bug introduced by strict equal checking for `undefined` when the value was `null`. This
Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -64,6 +64,7 @@ Options:
--fallback Fallback to default env file path, if custom env file path not found
--no-override Do not override existing environment variables
--use-shell Execute the command in a new shell with the given environment
--verbose Print helpful debugging information
-h, --help output usage information
```

Expand Down Expand Up @@ -186,6 +187,7 @@ A function that executes a given command in a new child process with the given e
- **`options`** { `object` }
- **`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`)
- **`verbose`** { `boolean` }: Prints extra debug logs to `console.info` (default: `false`)
- **Returns** { `Promise<object>` }: key is env var name and value is the env var value

### `GetEnvVars`
Expand All @@ -199,6 +201,7 @@ A function that parses environment variables from a `.env` or a `.rc` file
- **`rc`** { `object` }
- **`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)`)
- **`verbose`** { `boolean` }: Prints extra debug logs to `console.info` (default: `false`)
- **Returns** { `Promise<object>` }: key is env var name and value is the env var value

## Why
Expand Down
4 changes: 2 additions & 2 deletions dist/env-cmd.js
Expand Up @@ -32,7 +32,7 @@ exports.CLI = CLI;
* @returns {Promise<{ [key: string]: any }>} Returns an object containing [environment variable name]: value
*/
async function EnvCmd({ command, commandArgs, envFile, rc, options = {} }) {
let env = await get_env_vars_1.getEnvVars({ envFile, rc });
let env = await get_env_vars_1.getEnvVars({ envFile, rc, verbose: options.verbose });
// Override the merge order if --no-override flag set
if (options.noOverride === true) {
env = Object.assign({}, env, process.env);
Expand All @@ -48,7 +48,7 @@ async function EnvCmd({ command, commandArgs, envFile, rc, options = {} }) {
env
});
// Handle any termination signals for parent and child proceses
const signals = new signal_termination_1.TermSignals();
const signals = new signal_termination_1.TermSignals({ verbose: options.verbose });
signals.handleUncaughtExceptions();
signals.handleTermSignals(proc);
return env;
Expand Down
6 changes: 4 additions & 2 deletions dist/get-env-vars.d.ts
Expand Up @@ -2,15 +2,17 @@ import { GetEnvVarOptions } from './types';
export declare function getEnvVars(options?: GetEnvVarOptions): Promise<{
[key: string]: any;
}>;
export declare function getEnvFile({ filePath, fallback }: {
export declare function getEnvFile({ filePath, fallback, verbose }: {
filePath?: string;
fallback?: boolean;
verbose?: boolean;
}): Promise<{
[key: string]: any;
}>;
export declare function getRCFile({ environments, filePath }: {
export declare function getRCFile({ environments, filePath, verbose }: {
environments: string[];
filePath?: string;
verbose?: boolean;
}): Promise<{
[key: string]: any;
}>;
85 changes: 67 additions & 18 deletions dist/get-env-vars.js
Expand Up @@ -8,54 +8,103 @@ async function getEnvVars(options = {}) {
options.envFile = options.envFile !== undefined ? options.envFile : {};
// Check for rc file usage
if (options.rc !== undefined) {
return getRCFile({ environments: options.rc.environments, filePath: options.rc.filePath });
return getRCFile({
environments: options.rc.environments,
filePath: options.rc.filePath,
verbose: options.verbose
});
}
return getEnvFile({ filePath: options.envFile.filePath, fallback: options.envFile.fallback });
return getEnvFile({
filePath: options.envFile.filePath,
fallback: options.envFile.fallback,
verbose: options.verbose
});
}
exports.getEnvVars = getEnvVars;
async function getEnvFile({ filePath, fallback }) {
async function getEnvFile({ filePath, fallback, verbose }) {
// Use env file
if (filePath !== undefined) {
try {
return await parse_env_file_1.getEnvFileVars(filePath);
const env = await parse_env_file_1.getEnvFileVars(filePath);
if (verbose === true) {
console.info(`Found .env file at path: ${filePath}`);
}
return env;
}
catch (e) {
if (verbose === true) {
console.info(`Failed to find .env file at path: ${filePath}`);
}
}
catch (e) { }
if (fallback !== true) {
throw new Error(`Unable to locate env file at location (${filePath})`);
throw new Error(`Failed to find .env file at path: ${filePath}`);
}
}
// Use the default env file locations
for (const path of ENV_FILE_DEFAULT_LOCATIONS) {
try {
return await parse_env_file_1.getEnvFileVars(path);
const env = await parse_env_file_1.getEnvFileVars(path);
if (verbose === true) {
console.info(`Found .env file at default path: ${path}`);
}
return env;
}
catch (e) { }
}
throw new Error(`Unable to locate env file at default locations (${ENV_FILE_DEFAULT_LOCATIONS})`);
const error = `Failed to find .env file at default paths: ${ENV_FILE_DEFAULT_LOCATIONS}`;
if (verbose === true) {
console.info(error);
}
throw new Error(error);
}
exports.getEnvFile = getEnvFile;
async function getRCFile({ environments, filePath }) {
async function getRCFile({ environments, filePath, verbose }) {
// User provided an .rc file path
if (filePath !== undefined) {
try {
return await parse_rc_file_1.getRCFileVars({ environments, filePath });
const env = await parse_rc_file_1.getRCFileVars({ environments, filePath });
if (verbose === true) {
console.info(`Found environments: ${environments} for .rc file at path: ${filePath}`);
}
return env;
}
catch (e) {
if (e.name !== 'PathError')
console.error(e);
throw new Error(`Unable to locate .rc file at location (${filePath})`);
if (e.name === 'PathError') {
if (verbose === true) {
console.info(`Failed to find .rc file at path: ${filePath}`);
}
}
if (e.name === 'EnvironmentError') {
if (verbose === true) {
console.info(`Failed to find environments: ${environments} for .rc file at path: ${filePath}`);
}
}
throw e;
}
}
// Use the default .rc file locations
for (const filePath of RC_FILE_DEFAULT_LOCATIONS) {
for (const path of RC_FILE_DEFAULT_LOCATIONS) {
try {
return await parse_rc_file_1.getRCFileVars({ environments, filePath });
const env = await parse_rc_file_1.getRCFileVars({ environments, filePath: path });
if (verbose === true) {
console.info(`Found environments: ${environments} for default .rc file at path: ${path}`);
}
return env;
}
catch (e) {
if (e.name !== 'PathError')
console.error(e);
if (e.name === 'EnvironmentError') {
const errorText = `Failed to find environments: ${environments} for .rc file at path: ${path}`;
if (verbose === true) {
console.info(errorText);
}
throw new Error(errorText);
}
}
}
throw new Error(`Unable to locate .rc file at default locations (${RC_FILE_DEFAULT_LOCATIONS})`);
const errorText = `Failed to find .rc file at default paths: ${RC_FILE_DEFAULT_LOCATIONS}`;
if (verbose === true) {
console.info(errorText);
}
throw new Error(errorText);
}
exports.getRCFile = getRCFile;
11 changes: 9 additions & 2 deletions dist/parse-args.js
Expand Up @@ -14,6 +14,7 @@ function parseArgs(args) {
program = parseArgsUsingCommander(args.slice(0, args.indexOf(command)));
const noOverride = !program.override;
const useShell = !!program.useShell;
const verbose = !!program.verbose;
let rc;
if (program.environments !== undefined && program.environments.length !== 0) {
rc = {
Expand All @@ -28,16 +29,21 @@ function parseArgs(args) {
fallback: program.fallback
};
}
return {
const options = {
command,
commandArgs,
envFile,
rc,
options: {
noOverride,
useShell
useShell,
verbose
}
};
if (verbose === true) {
console.info(`Options: ${JSON.stringify(options, null, 0)}`);
}
return options;
}
exports.parseArgs = parseArgs;
function parseArgsUsingCommander(args) {
Expand All @@ -51,6 +57,7 @@ function parseArgsUsingCommander(args) {
.option('--fallback', 'Fallback to default env file path, if custom env file path not found')
.option('--no-override', 'Do not override existing environment variables')
.option('--use-shell', 'Execute the command in a new shell with the given environment')
.option('--verbose', 'Print helpful debugging information')
.parse(['_', '_', ...args]);
}
exports.parseArgsUsingCommander = parseArgsUsingCommander;
4 changes: 3 additions & 1 deletion dist/parse-env-file.js
Expand Up @@ -10,7 +10,9 @@ const REQUIRE_HOOK_EXTENSIONS = ['.json', '.js'];
async function getEnvFileVars(envFilePath) {
const absolutePath = utils_1.resolveEnvFilePath(envFilePath);
if (!fs.existsSync(absolutePath)) {
throw new Error(`Invalid env file path (${envFilePath}).`);
const pathError = new Error(`Invalid env file path (${envFilePath}).`);
pathError.name = 'PathError';
throw pathError;
}
// Get the file extension
const ext = path.extname(absolutePath).toLowerCase();
Expand Down
6 changes: 0 additions & 6 deletions dist/parse-rc-file.d.ts
Expand Up @@ -7,9 +7,3 @@ export declare function getRCFileVars({ environments, filePath }: {
}): Promise<{
[key: string]: any;
}>;
/**
* Reads and parses the .rc file
*/
export declare function parseRCFile(fileData: string): {
[key: string]: any;
};
47 changes: 17 additions & 30 deletions dist/parse-rc-file.js
Expand Up @@ -15,20 +15,27 @@ async function getRCFileVars({ environments, filePath }) {
await statAsync(absolutePath);
}
catch (e) {
const pathError = new Error('Invalid .rc file path.');
const pathError = new Error(`Failed to find .rc file at path: ${absolutePath}`);
pathError.name = 'PathError';
throw pathError;
}
// Get the file extension
const ext = path_1.extname(absolutePath).toLowerCase();
let parsedData;
if (ext === '.json' || ext === '.js') {
const possiblePromise = require(absolutePath); /* eslint-disable-line */
parsedData = utils_1.isPromise(possiblePromise) ? await possiblePromise : possiblePromise;
try {
if (ext === '.json' || ext === '.js') {
const possiblePromise = require(absolutePath); /* eslint-disable-line */
parsedData = utils_1.isPromise(possiblePromise) ? await possiblePromise : possiblePromise;
}
else {
const file = await readFileAsync(absolutePath, { encoding: 'utf8' });
parsedData = JSON.parse(file);
}
}
else {
const file = await readFileAsync(absolutePath, { encoding: 'utf8' });
parsedData = parseRCFile(file);
catch (e) {
const parseError = new Error(`Failed to parse .rc file at path: ${absolutePath}`);
parseError.name = 'ParseError';
throw parseError;
}
// Parse and merge multiple rc environments together
let result = {};
Expand All @@ -41,30 +48,10 @@ async function getRCFileVars({ environments, filePath }) {
}
});
if (!environmentFound) {
console.error(`Error:
Could not find any environments:
${environments}
in .rc file:
${absolutePath}`);
throw new Error(`All environments (${environments}) are missing in in .rc file (${absolutePath}).`);
const environmentError = new Error(`Failed to find environments ${environments} at .rc file location: ${absolutePath}`);
environmentError.name = 'EnvironmentError';
throw environmentError;
}
return result;
}
exports.getRCFileVars = getRCFileVars;
/**
* Reads and parses the .rc file
*/
function parseRCFile(fileData) {
let data;
try {
data = JSON.parse(fileData);
}
catch (e) {
console.error(`Error:
Failed to parse the .rc file.
Please make sure its a valid JSON format.`);
throw new Error('Unable to parse JSON in .rc file.');
}
return data;
}
exports.parseRCFile = parseRCFile;
4 changes: 4 additions & 0 deletions dist/signal-termination.d.ts
Expand Up @@ -2,7 +2,11 @@
import { ChildProcess } from 'child_process';
export declare class TermSignals {
private readonly terminateSpawnedProcessFuncHandlers;
private readonly verbose;
_exitCalled: boolean;
constructor(options?: {
verbose?: boolean;
});
handleTermSignals(proc: ChildProcess): void;
/**
* Enables catching of unhandled exceptions
Expand Down
11 changes: 10 additions & 1 deletion dist/signal-termination.js
Expand Up @@ -4,9 +4,11 @@ const SIGNALS_TO_HANDLE = [
'SIGINT', 'SIGTERM', 'SIGHUP'
];
class TermSignals {
constructor() {
constructor(options = {}) {
this.terminateSpawnedProcessFuncHandlers = {};
this.verbose = false;
this._exitCalled = false;
this.verbose = options.verbose === true;
}
handleTermSignals(proc) {
// Terminate child process if parent process receives termination events
Expand All @@ -15,6 +17,9 @@ class TermSignals {
(signal, code) => {
this._removeProcessListeners();
if (!this._exitCalled) {
if (this.verbose === true) {
console.info(`Parent process exited with signal: ${signal}. Terminating child process...`);
}
this._exitCalled = true;
proc.kill(signal);
this._terminateProcess(code, signal);
Expand All @@ -28,6 +33,10 @@ class TermSignals {
this._removeProcessListeners();
const convertedSignal = signal != null ? signal : undefined;
if (!this._exitCalled) {
if (this.verbose === true) {
console.info(`Child process exited with code: ${code} and signal: ${signal}. ` +
'Terminating parent process...');
}
this._exitCalled = true;
this._terminateProcess(code, convertedSignal);
}
Expand Down
2 changes: 2 additions & 0 deletions dist/types.d.ts
Expand Up @@ -7,12 +7,14 @@ export interface GetEnvVarOptions {
environments: string[];
filePath?: string;
};
verbose?: boolean;
}
export interface EnvCmdOptions extends GetEnvVarOptions {
command: string;
commandArgs: string[];
options?: {
noOverride?: boolean;
useShell?: boolean;
verbose?: boolean;
};
}

0 comments on commit dc4cf6d

Please sign in to comment.