mirror of
https://github.com/toddbluhm/env-cmd.git
synced 2025-12-08 18:23:33 +00:00
Merge pull request #1 from toddbluhm/2.0.0
Removed -e/--env flags, allow # comment lines in env file, bug fixes
This commit is contained in:
commit
c81fb43183
@ -1,4 +1,3 @@
|
||||
.gitignore
|
||||
coverage/
|
||||
node_modules/
|
||||
test/
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 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`
|
||||
- Changed ParseEnvFile over to more generic name: ParseEnvString
|
||||
- ParseEnvString: Ignore comment lines (lines starting with '#')
|
||||
- ParseEnvString: Ignore empty lines in env file
|
||||
|
||||
## 1.0.1
|
||||
- Fixed badges
|
||||
- Added .npmignore
|
||||
|
||||
11
README.md
11
README.md
@ -12,6 +12,7 @@ A simple node program for executing commands using an environment from an env fi
|
||||
## Usage
|
||||
**Environment file ``./test/.env`**
|
||||
```
|
||||
# This is a comment
|
||||
ENV1=THANKS
|
||||
ENV2=FORALL
|
||||
ENV4=THEFISH
|
||||
@ -22,7 +23,7 @@ ENV4=THEFISH
|
||||
```js
|
||||
{
|
||||
"scripts": {
|
||||
"test": "env-cmd -e ./test/.env mocha -R spec"
|
||||
"test": "env-cmd ./test/.env mocha -R spec"
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -30,15 +31,15 @@ or
|
||||
|
||||
**Terminal**
|
||||
```sh
|
||||
./node_modules/.bin/env-cmd -e ./test/.env node index.js
|
||||
./node_modules/.bin/env-cmd ./test/.env node index.js
|
||||
```
|
||||
|
||||
## Why
|
||||
|
||||
Because sometimes it just too cumbersome passing tons of environment variables to scripts. Its usually just easier to have a file with all the vars in them, especially for development.
|
||||
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 data to a public git!**
|
||||
**Do not commit sensitive env data to a public git repo!**
|
||||
|
||||
## Special Thanks
|
||||
|
||||
Special thanks to [cross-env](https://github.com/kentcdodds/cross-env) for inspiration (use's the same `cross-spawn` lib underneath).
|
||||
Special thanks to [cross-env](https://github.com/kentcdodds/cross-env) for inspiration (use's the same `cross-spawn` lib underneath too).
|
||||
|
||||
76
lib/index.js
76
lib/index.js
@ -5,8 +5,21 @@ const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
function EnvCmd (args) {
|
||||
// Parse the args from the command line
|
||||
const parsedArgs = ParseArgs(args)
|
||||
const env = ParseEnvFile(parsedArgs.envFilePath)
|
||||
|
||||
// Attempt to open the provided file
|
||||
let file
|
||||
try {
|
||||
file = fs.readFileSync(parsedArgs.envFilePath, { encoding: 'utf8' })
|
||||
} catch (e) {
|
||||
throw new Error(`Error! Could not find or read file at ${parsedArgs.envFilePath}`)
|
||||
}
|
||||
|
||||
// Parse the env file string
|
||||
const env = ParseEnvString(file)
|
||||
|
||||
// Execute the command with the given environment variables
|
||||
if (parsedArgs.command) {
|
||||
const proc = spawn(parsedArgs.command, parsedArgs.commandArgs, {
|
||||
stdio: 'inherit',
|
||||
@ -19,30 +32,25 @@ function EnvCmd (args) {
|
||||
}
|
||||
|
||||
function ParseArgs (args) {
|
||||
if (args.length < 3) {
|
||||
if (args.length < 2) {
|
||||
throw new Error('Error! Too few arguments passed to env-cmd.')
|
||||
}
|
||||
|
||||
const envFileFlags = /(^\-e$|^\-\-env$)/g
|
||||
let envFilePath
|
||||
let command
|
||||
let commandArgs = args.slice()
|
||||
while (commandArgs.length) {
|
||||
const arg = commandArgs.shift()
|
||||
|
||||
// if this is the env file flag the get the file
|
||||
if (arg.match(envFileFlags)) {
|
||||
envFilePath = path.resolve(process.cwd(), commandArgs.shift())
|
||||
// assume the first arg is the env file
|
||||
if (!envFilePath) {
|
||||
envFilePath = path.resolve(process.cwd(), arg)
|
||||
} else {
|
||||
command = arg
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!envFilePath) {
|
||||
throw new Error('Error! No -e or --env flag passed.')
|
||||
}
|
||||
|
||||
return {
|
||||
envFilePath,
|
||||
command,
|
||||
@ -50,38 +58,44 @@ function ParseArgs (args) {
|
||||
}
|
||||
}
|
||||
|
||||
function ParseEnvFile (envFilePath) {
|
||||
let file = fs.readFileSync(envFilePath, { encoding: 'utf8' })
|
||||
function ParseEnvString (envFileString) {
|
||||
const envs = Object.assign({}, process.env)
|
||||
while (file.length) {
|
||||
// Get the full line
|
||||
const line = file.slice(0, file.indexOf('\n') + 1)
|
||||
while (envFileString.length) {
|
||||
// The the last index of the line using the newline delimiter
|
||||
let endOfLineIndex = envFileString.indexOf('\n')
|
||||
|
||||
// Shrink the file by 1 line
|
||||
file = file.slice(line.length)
|
||||
|
||||
// Parse the line
|
||||
const equalSign = line.indexOf('=')
|
||||
|
||||
if (equalSign === -1) {
|
||||
throw new Error(`Error! Malformed line in ${path.parse(envFilePath).base}.`)
|
||||
// If no newline, then assume end of file
|
||||
if (endOfLineIndex === -1) {
|
||||
endOfLineIndex = envFileString.length
|
||||
}
|
||||
|
||||
// Set then new env var
|
||||
envs[line.slice(0, equalSign)] = line.slice(line.indexOf('=') + 1, -1)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
function PrintHelp () {
|
||||
return `
|
||||
Usage: env-cmd -e [file] command [command options]
|
||||
Usage: env-cmd env_file command [command options]
|
||||
|
||||
A simple application for running a cli application using an env config file
|
||||
|
||||
Options:
|
||||
|
||||
-e, --env Relative path to the env file
|
||||
`
|
||||
}
|
||||
|
||||
@ -98,7 +112,7 @@ process.on('uncaughtException', HandleUncaughtExceptions)
|
||||
module.exports = {
|
||||
EnvCmd,
|
||||
ParseArgs,
|
||||
ParseEnvFile,
|
||||
ParseEnvString,
|
||||
PrintHelp,
|
||||
HandleUncaughtExceptions
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "env-cmd",
|
||||
"version": "1.0.1",
|
||||
"version": "2.0.0",
|
||||
"description": "Executes a command using the envs in the provided env file",
|
||||
"main": "lib/index.js",
|
||||
"bin": {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
BOB=COOL
|
||||
NODE_ENV dev
|
||||
NICE=42
|
||||
85
test/test.js
85
test/test.js
@ -5,14 +5,18 @@ const describe = require('mocha').describe
|
||||
const it = require('mocha').it
|
||||
const afterEach = require('mocha').afterEach
|
||||
const beforeEach = require('mocha').beforeEach
|
||||
const before = require('mocha').before
|
||||
const after = require('mocha').after
|
||||
const path = require('path')
|
||||
const proxyquire = require('proxyquire')
|
||||
const sinon = require('sinon')
|
||||
const fs = require('fs')
|
||||
|
||||
const spawnStub = sinon.spy(() => ({
|
||||
on: sinon.stub(),
|
||||
exit: sinon.stub()
|
||||
}))
|
||||
|
||||
const lib = proxyquire('../lib', {
|
||||
'cross-spawn': {
|
||||
spawn: spawnStub
|
||||
@ -20,29 +24,29 @@ const lib = proxyquire('../lib', {
|
||||
})
|
||||
const EnvCmd = lib.EnvCmd
|
||||
const ParseArgs = lib.ParseArgs
|
||||
const ParseEnvFile = lib.ParseEnvFile
|
||||
const ParseEnvString = lib.ParseEnvString
|
||||
const PrintHelp = lib.PrintHelp
|
||||
const HandleUncaughtExceptions = lib.HandleUncaughtExceptions
|
||||
|
||||
describe('env-cmd', function () {
|
||||
describe('ParseArgs', function () {
|
||||
it('should parse out the -e envfile path', function () {
|
||||
const parsedArgs = ParseArgs(['-e', './test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
assert(parsedArgs.envFilePath === path.join(__dirname, 'envFile'))
|
||||
})
|
||||
|
||||
it('should parse out the --env envfile path', function () {
|
||||
const parsedArgs = ParseArgs(['--env', './test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
assert(parsedArgs.envFilePath === path.join(__dirname, 'envFile'))
|
||||
})
|
||||
|
||||
it('should parse out the command', function () {
|
||||
const parsedArgs = ParseArgs(['-e', './test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
assert(parsedArgs.command === 'command')
|
||||
})
|
||||
|
||||
it('should parse out the command args', function () {
|
||||
const parsedArgs = ParseArgs(['-e', './test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
assert(parsedArgs.commandArgs.length === 2)
|
||||
assert(parsedArgs.commandArgs[0] === 'cmda1')
|
||||
assert(parsedArgs.commandArgs[1] === 'cmda2')
|
||||
@ -50,55 +54,80 @@ describe('env-cmd', function () {
|
||||
|
||||
it('should error out if incorrect number of args passed', function () {
|
||||
try {
|
||||
ParseArgs(['-e', './test/envFile'])
|
||||
ParseArgs(['./test/envFile'])
|
||||
} catch (e) {
|
||||
assert(e.message === 'Error! Too few arguments passed to env-cmd.')
|
||||
return
|
||||
}
|
||||
assert(!'No exepection thrown')
|
||||
})
|
||||
|
||||
it('should error out if no envFile flag found', function () {
|
||||
try {
|
||||
ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
} catch (e) {
|
||||
assert(e.message === 'Error! No -e or --env flag passed.')
|
||||
return
|
||||
}
|
||||
assert(!'No exepection thrown')
|
||||
assert(!'No exception thrown')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ParseEnvFile', function () {
|
||||
it('should parse out vars in the environment variable file', function () {
|
||||
const env = ParseEnvFile(path.join(__dirname, '/.env'))
|
||||
describe('ParseEnvString', function () {
|
||||
it('should parse out vars in the environment variable string', function () {
|
||||
const env = ParseEnvString('BOB=COOL\nNODE_ENV=dev\nANSWER=42\n')
|
||||
assert(env.BOB === 'COOL')
|
||||
assert(env.NODE_ENV === 'dev')
|
||||
assert(env.NICE === '42')
|
||||
assert(env.ANSWER === '42')
|
||||
})
|
||||
|
||||
it('should parse out vars in the environment variable file', function () {
|
||||
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 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 throw parse error due to malformed env var string', function () {
|
||||
try {
|
||||
ParseEnvFile(path.join(__dirname, '/.env-malformed'))
|
||||
ParseEnvString('BOB=COOL\nNODE_ENV dev\nANSWER=42\n')
|
||||
} catch (e) {
|
||||
assert(e.message === 'Error! Malformed line in .env-malformed.')
|
||||
assert(e.message === 'Error! Malformed line in env file.')
|
||||
return
|
||||
}
|
||||
assert(!'No exepection thrown')
|
||||
assert(!'No exception thrown')
|
||||
})
|
||||
})
|
||||
|
||||
describe('EnvCmd', function () {
|
||||
before(function () {
|
||||
this.readFileStub = sinon.stub(fs, 'readFileSync')
|
||||
})
|
||||
after(function () {
|
||||
this.readFileStub.restore()
|
||||
})
|
||||
afterEach(function () {
|
||||
spawnStub.reset()
|
||||
this.readFileStub.reset()
|
||||
})
|
||||
it('should spawn a new process with the env vars set', function () {
|
||||
EnvCmd(['-e', './test/.env', 'echo', '$BOB'])
|
||||
this.readFileStub.returns('BOB=COOL\nNODE_ENV=dev\nANSWER=42\n')
|
||||
EnvCmd(['./test/.env', 'echo', '$BOB'])
|
||||
assert(this.readFileStub.args[0][0] === path.join(process.cwd(), 'test/.env'))
|
||||
assert(spawnStub.args[0][0] === 'echo')
|
||||
assert(spawnStub.args[0][1][0] === '$BOB')
|
||||
assert(spawnStub.args[0][2].env.BOB === 'COOL')
|
||||
assert(spawnStub.args[0][2].env.NODE_ENV === 'dev')
|
||||
assert(spawnStub.args[0][2].env.NICE === '42')
|
||||
assert(spawnStub.args[0][2].env.ANSWER === '42')
|
||||
})
|
||||
|
||||
it('should throw error if file does not exist', function () {
|
||||
this.readFileStub.restore()
|
||||
try {
|
||||
EnvCmd(['./test/.non-existent-file', 'echo', '$BOB'])
|
||||
} catch (e) {
|
||||
const resolvedPath = path.join(process.cwd(), 'test/.non-existent-file')
|
||||
assert(e.message === `Error! Could not find or read file at ${resolvedPath}`)
|
||||
return
|
||||
}
|
||||
assert(!'No exception thrown')
|
||||
})
|
||||
})
|
||||
|
||||
@ -108,7 +137,7 @@ describe('env-cmd', function () {
|
||||
assert(typeof helpText === 'string')
|
||||
assert(helpText.match(/Usage/g).length !== 0)
|
||||
assert(helpText.match(/env-cmd/).length !== 0)
|
||||
assert(helpText.match(/-e/).length !== 0)
|
||||
assert(helpText.match(/env_file/).length !== 0)
|
||||
})
|
||||
})
|
||||
describe('HandleUncaughtExceptions', function () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user