Merge pull request #3 from toddbluhm/2.1.0

Regex for parsing, support inline comments, support `key value` format
This commit is contained in:
Todd Bluhm 2016-08-18 16:55:11 -05:00 committed by GitHub
commit 8fd41397d6
5 changed files with 113 additions and 62 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## 2.1.0
- **Feature**: Added support for `key value` mapping in env vars file
- **Feature**: Added support for inline comments `ENV=VALUE # inline comment`
- **Change**: Will now ignore invalid lines in env vars file instead of throwing an error
- **Change**: Migrated all the parsing over to regex since the file format is simple enough right
now to support that
- **Bug**: Removed old test cases for the `-e/--env` flags that were not needed anymore
## 2.0.0
- ***BREAKING***: Removed the `-e` and `--env` flags. Now it just expects the first arg to `env-cmd` to be the relative path to the env file: `env-cmd env_file command carg1 carg2`
- **Change:** `ParseEnvFile` is now more properly named `ParseEnvString`

View File

@ -5,7 +5,7 @@
[![npm](https://img.shields.io/npm/l/env-cmd.svg?maxAge=2592000)](https://www.npmjs.com/package/env-cmd)
# env-cmd
A simple node program for executing commands using an environment from an env file
A simple node program for executing commands using an environment from an env file.
## Install
`npm install env-cmd`
@ -14,14 +14,13 @@ A simple node program for executing commands using an environment from an env fi
**Environment file `./test/.env`**
```
# This is a comment
ENV1=THANKS
ENV2=FORALL
ENV4=THEFISH
ENV1=THANKS # Yay inline comments support
ENV2=FOR ALL
ENV3 THE FISH # This format is also accepted
```
*This is the only accepted format for an environment file. If other formats are desired please create an issue*
**Package.json**
```js
```json
{
"scripts": {
"test": "env-cmd ./test/.env mocha -R spec"
@ -35,11 +34,17 @@ or
./node_modules/.bin/env-cmd ./test/.env node index.js
```
## Environment File Formats
These are the currently accepted environment file formats. If any other formats are desired please create an issue.
- `key=value`
- `key value`
## Why
Because sometimes its just too cumbersome passing lots of environment variables to scripts. Its usually just easier to have a file with all the vars in them, especially for development and testing.
**Do not commit sensitive env data to a public git repo!**
**Do not commit sensitive environment data to a public git repo!**
## Related Projects

View File

@ -58,37 +58,39 @@ function ParseArgs (args) {
}
}
function ParseEnvString (envFileString) {
const envs = Object.assign({}, process.env)
while (envFileString.length) {
// The the last index of the line using the newline delimiter
let endOfLineIndex = envFileString.indexOf('\n')
function StripComments (envString) {
const commentsRegex = /[ ]*(#.*$)/gim
return envString.replace(commentsRegex, '')
}
// If no newline, then assume end of file
if (endOfLineIndex === -1) {
endOfLineIndex = envFileString.length
}
function StripEmptyLines (envString) {
const emptyLinesRegex = /(^\n)/gim
return envString.replace(emptyLinesRegex, '')
}
// Get the full line
const line = envFileString.slice(0, endOfLineIndex + 1)
// Shrink the file by 1 line
envFileString = envFileString.slice(line.length)
// Only parse lines that are not empty and don't begin with #
if (line.length > 1 && line[0] !== '#') {
// Parse the line
const equalSign = line.indexOf('=')
if (equalSign === -1) {
throw new Error('Error! Malformed line in env file.')
}
// Set then new env var
envs[line.slice(0, equalSign)] = line.slice(equalSign + 1, endOfLineIndex)
}
function ParseEnvVars (envString) {
const envParseRegex = /^((.+?)[ =](.*))$/gim
const matches = {}
let match
while ((match = envParseRegex.exec(envString)) !== null) {
// Note: match[1] is the full env=var line
matches[match[2]] = match[3]
}
return envs
return matches
}
function ParseEnvString (envFileString) {
// First thing we do is stripe out all comments
envFileString = StripComments(envFileString)
// Next we stripe out all the empty lines
envFileString = StripEmptyLines(envFileString)
// Parse the envs vars out
const envs = ParseEnvVars(envFileString)
// Merge the file env vars with the current process env vars (the file vars overwrite process vars)
return Object.assign({}, process.env, envs)
}
function PrintHelp () {
@ -114,5 +116,8 @@ module.exports = {
ParseArgs,
ParseEnvString,
PrintHelp,
HandleUncaughtExceptions
HandleUncaughtExceptions,
StripComments,
StripEmptyLines,
ParseEnvVars
}

View File

@ -1,6 +1,6 @@
{
"name": "env-cmd",
"version": "2.0.0",
"version": "2.1.0",
"description": "Executes a command using the envs in the provided env file",
"main": "lib/index.js",
"bin": {

View File

@ -27,15 +27,13 @@ const ParseArgs = lib.ParseArgs
const ParseEnvString = lib.ParseEnvString
const PrintHelp = lib.PrintHelp
const HandleUncaughtExceptions = lib.HandleUncaughtExceptions
const StripComments = lib.StripComments
const StripEmptyLines = lib.StripEmptyLines
const ParseEnvVars = lib.ParseEnvVars
describe('env-cmd', function () {
describe('ParseArgs', function () {
it('should parse out the -e envfile path', function () {
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
assert(parsedArgs.envFilePath === path.join(__dirname, 'envFile'))
})
it('should parse out the --env envfile path', function () {
it('should parse out the envfile path', function () {
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
assert(parsedArgs.envFilePath === path.join(__dirname, 'envFile'))
})
@ -64,35 +62,70 @@ describe('env-cmd', function () {
})
describe('ParseEnvString', function () {
it('should parse out vars in the environment variable string', function () {
it('should parse env vars and merge (overwrite) with process.env vars', function () {
process.env.TEST = 'SOME TEST VAR'
process.env.NODE_ENV = 'development'
const env = ParseEnvString('BOB=COOL\nNODE_ENV=dev\nANSWER=42\n')
assert(env.BOB === 'COOL')
assert(env.NODE_ENV === 'dev')
assert(env.ANSWER === '42')
assert(env.TEST === 'SOME TEST VAR')
})
})
describe('StripComments', function () {
it('should strip out all full line comments', function () {
const envString = StripComments('#BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n#AnotherComment\n')
assert(envString === '\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n\n')
})
it('should ignore comment lines (starting with \'#\') and empty lines', function () {
const env = ParseEnvString('#BOB=COOL\nNODE_ENV=dev\n\n#ANSWER=42\n')
assert(env.BOB === undefined)
assert(env.NODE_ENV === 'dev')
assert(env.ANSWER === undefined)
it('should strip out all inline comments and preceding spaces', function () {
const envString = StripComments('BOB=COOL#inline1\nNODE_ENV=dev #cool\nANSWER=42 AND COUNTING #multiple-spaces\n')
assert(envString === 'BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n')
})
})
describe('StripEmptyLines', function () {
it('should strip out all empty lines', function () {
const envString = StripEmptyLines('\nBOB=COOL\n\nNODE_ENV=dev\n\nANSWER=42 AND COUNTING\n\n')
assert(envString === 'BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n')
})
})
describe('ParseEnvVars', function () {
it('should parse out all env vars in string when not ending with \'\\n\'', function () {
const envVars = ParseEnvVars('BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING')
assert(envVars.BOB === 'COOL')
assert(envVars.NODE_ENV === 'dev')
assert(envVars.ANSWER === '42 AND COUNTING')
})
it('should parse out env vars even if string does not end in \'\\n\'', function () {
const env = ParseEnvString('BOB=COOL\nNODE_ENV=dev\nANSWER=42')
assert(env.BOB === 'COOL')
assert(env.NODE_ENV === 'dev')
assert(env.ANSWER === '42')
it('should parse out all env vars in string with format \'key=value\'', function () {
const envVars = ParseEnvVars('BOB=COOL\nNODE_ENV=dev\nANSWER=42 AND COUNTING\n')
assert(envVars.BOB === 'COOL')
assert(envVars.NODE_ENV === 'dev')
assert(envVars.ANSWER === '42 AND COUNTING')
})
it('should throw parse error due to malformed env var string', function () {
try {
ParseEnvString('BOB=COOL\nNODE_ENV dev\nANSWER=42\n')
} catch (e) {
assert(e.message === 'Error! Malformed line in env file.')
return
}
assert(!'No exception thrown')
it('should parse out all env vars in string with format \'key value\'', function () {
const envVars = ParseEnvVars('BOB COOL\nNODE_ENV dev\nANSWER 42 AND COUNTING\n')
assert(envVars.BOB === 'COOL')
assert(envVars.NODE_ENV === 'dev')
assert(envVars.ANSWER === '42 AND COUNTING')
})
it('should parse out all env vars in string with mixed format \'key=value\' & \'key value\'', function () {
const envVars = ParseEnvVars('BOB=COOL\nNODE_ENV dev\nANSWER=42 AND COUNTING\n')
assert(envVars.BOB === 'COOL')
assert(envVars.NODE_ENV === 'dev')
assert(envVars.ANSWER === '42 AND COUNTING')
})
it('should ignore invalid lines', function () {
const envVars = ParseEnvVars('BOB=COOL\nTHISIS$ANDINVALIDLINE\nANSWER=42 AND COUNTING\n')
assert(Object.keys(envVars).length === 2)
assert(envVars.BOB === 'COOL')
assert(envVars.ANSWER === '42 AND COUNTING')
})
})