mirror of
https://github.com/toddbluhm/env-cmd.git
synced 2025-12-08 18:23:33 +00:00
Added in support for .env-cmdrc file and updated to 3.0.0
This commit is contained in:
parent
4bb8233bec
commit
f37249e467
@ -6,6 +6,7 @@ node_js:
|
||||
- "4"
|
||||
- "5"
|
||||
- "6"
|
||||
- "7"
|
||||
|
||||
env:
|
||||
global:
|
||||
@ -17,6 +18,7 @@ cache:
|
||||
- node_modules
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run test-cover
|
||||
|
||||
after_script:
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 3.0.0
|
||||
- **Feature**: Added ability to use an `.env-cmdrc` file to hold multiple configs
|
||||
- **Feature**: Added ability to pass in a regular `.js` file exporting an object for your env file (special thanks to Jon Scheiding!)
|
||||
- **Change**: Updated core `cross-spawn` lib to 5.0.1
|
||||
|
||||
## 2.2.0
|
||||
- **Feature**: Added support for .json env files (special thanks to Eric Lanehart!)
|
||||
|
||||
|
||||
28
README.md
28
README.md
@ -3,6 +3,7 @@
|
||||
[](https://www.npmjs.com/package/env-cmd)
|
||||
[](https://www.npmjs.com/package/env-cmd)
|
||||
[](https://www.npmjs.com/package/env-cmd)
|
||||
[](http://standardjs.com/)
|
||||
|
||||
# env-cmd
|
||||
A simple node program for executing commands using an environment from an env file.
|
||||
@ -33,6 +34,25 @@ or
|
||||
```sh
|
||||
./node_modules/.bin/env-cmd ./test/.env node index.js
|
||||
```
|
||||
or
|
||||
|
||||
**.env-cmdrc file `.env-cmdrc`**
|
||||
|
||||
```json
|
||||
{
|
||||
"development": {
|
||||
"ENV1": "Thanks",
|
||||
"ENV2": "For All"
|
||||
},
|
||||
"production": {
|
||||
"ENV1": "The Fish"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
./node_modules/.bin/env-cmd production node index.js
|
||||
```
|
||||
|
||||
## Environment File Formats
|
||||
|
||||
@ -41,6 +61,7 @@ These are the currently accepted environment file formats. If any other formats
|
||||
- `key value`
|
||||
- Key/value pairs as JSON
|
||||
- JavaScript file exporting an object
|
||||
- `.env-cmdrc` file (as valid json) in execution directory
|
||||
|
||||
## Why
|
||||
|
||||
@ -60,3 +81,10 @@ Special thanks to [`cross-env`](https://github.com/kentcdodds/cross-env) for ins
|
||||
|
||||
- Eric Lanehart
|
||||
- Jon Scheiding
|
||||
|
||||
## Contributing Guide
|
||||
I welcome all pull requests. Please make sure you add appropriate test cases for any features added. Before opening a PR please make sure to run the following scripts:
|
||||
|
||||
- `npm run lint` checks for code errors and formats according to [js-standard](https://github.com/feross/standard)
|
||||
- `npm test` make sure all tests pass
|
||||
- `npm run test-cover` make sure the coverage has not decreased from current master
|
||||
|
||||
97
lib/index.js
97
lib/index.js
@ -3,52 +3,40 @@
|
||||
const spawn = require('cross-spawn').spawn
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const rcFileLocation = path.join(process.cwd(), '.env-cmdrc')
|
||||
|
||||
function EnvCmd (args) {
|
||||
// Parse the args from the command line
|
||||
// First Parse the args from the command line
|
||||
const parsedArgs = ParseArgs(args)
|
||||
|
||||
// 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}`)
|
||||
}
|
||||
|
||||
const ext = path.extname(parsedArgs.envFilePath).toLowerCase()
|
||||
|
||||
// Parse the env file string
|
||||
const env = ext === '.json' || ext === '.js'
|
||||
? Object.assign({}, process.env, require(parsedArgs.envFilePath))
|
||||
: ParseEnvString(file)
|
||||
// If a .rc file was found then use that
|
||||
const env = fs.existsSync(rcFileLocation) ? UseRCFile(parsedArgs) : UseCmdLine(parsedArgs)
|
||||
|
||||
// Execute the command with the given environment variables
|
||||
if (parsedArgs.command) {
|
||||
const proc = spawn(parsedArgs.command, parsedArgs.commandArgs, {
|
||||
stdio: 'inherit',
|
||||
env
|
||||
})
|
||||
process.on('SIGTERM', () => proc.kill('SIGTERM'))
|
||||
proc.on('exit', process.exit)
|
||||
return proc
|
||||
}
|
||||
const proc = spawn(parsedArgs.command, parsedArgs.commandArgs, {
|
||||
stdio: 'inherit',
|
||||
env
|
||||
})
|
||||
process.on('SIGTERM', proc.kill.bind(proc, 'SIGTERM'))
|
||||
proc.on('exit', process.exit)
|
||||
return proc
|
||||
}
|
||||
|
||||
// Parses the arguments passed into the cli
|
||||
function ParseArgs (args) {
|
||||
if (args.length < 2) {
|
||||
throw new Error('Error! Too few arguments passed to env-cmd.')
|
||||
}
|
||||
|
||||
let envFilePath
|
||||
let envFile
|
||||
let command
|
||||
let commandArgs = args.slice()
|
||||
while (commandArgs.length) {
|
||||
const arg = commandArgs.shift()
|
||||
|
||||
// assume the first arg is the env file
|
||||
if (!envFilePath) {
|
||||
envFilePath = path.resolve(process.cwd(), arg)
|
||||
// assume the first arg is the env file (or if using .rc the environment name)
|
||||
if (!envFile) {
|
||||
envFile = arg
|
||||
} else {
|
||||
command = arg
|
||||
break
|
||||
@ -56,22 +44,25 @@ function ParseArgs (args) {
|
||||
}
|
||||
|
||||
return {
|
||||
envFilePath,
|
||||
envFile,
|
||||
command,
|
||||
commandArgs
|
||||
}
|
||||
}
|
||||
|
||||
// Strips out comments from env file string
|
||||
function StripComments (envString) {
|
||||
const commentsRegex = /[ ]*(#.*$)/gim
|
||||
return envString.replace(commentsRegex, '')
|
||||
}
|
||||
|
||||
// Strips out newlines from env file string
|
||||
function StripEmptyLines (envString) {
|
||||
const emptyLinesRegex = /(^\n)/gim
|
||||
return envString.replace(emptyLinesRegex, '')
|
||||
}
|
||||
|
||||
// Parse out all env vars from an env file string
|
||||
function ParseEnvVars (envString) {
|
||||
const envParseRegex = /^((.+?)[ =](.*))$/gim
|
||||
const matches = {}
|
||||
@ -83,6 +74,7 @@ function ParseEnvVars (envString) {
|
||||
return matches
|
||||
}
|
||||
|
||||
// Parse out all env vars from a given env file string and return an object
|
||||
function ParseEnvString (envFileString) {
|
||||
// First thing we do is stripe out all comments
|
||||
envFileString = StripComments(envFileString)
|
||||
@ -97,11 +89,49 @@ function ParseEnvString (envFileString) {
|
||||
return Object.assign({}, process.env, envs)
|
||||
}
|
||||
|
||||
// Reads and parses the .env-cmdrc file
|
||||
function ParseRCFile (fileData) {
|
||||
return JSON.parse(fileData)
|
||||
}
|
||||
|
||||
// Uses the rc file to get env vars
|
||||
function UseRCFile (parsedArgs) {
|
||||
const fileData = fs.readFileSync(rcFileLocation, { encoding: 'utf8' })
|
||||
const parsedData = ParseRCFile(fileData)
|
||||
return parsedData[parsedArgs.envFile]
|
||||
}
|
||||
|
||||
// Uses the cli passed env file to get env vars
|
||||
function UseCmdLine (parsedArgs) {
|
||||
const envFilePath = path.join(process.cwd(), parsedArgs.envFile)
|
||||
|
||||
// Attempt to open the provided file
|
||||
let file
|
||||
try {
|
||||
file = fs.readFileSync(envFilePath, { encoding: 'utf8' })
|
||||
} catch (e) {
|
||||
throw new Error(`Error! Could not find or read file at ${envFilePath}`)
|
||||
}
|
||||
|
||||
const ext = path.extname(envFilePath).toLowerCase()
|
||||
|
||||
// Parse the env file string using the correct parser
|
||||
const env = ext === '.json' || ext === '.js'
|
||||
? Object.assign({}, process.env, require(envFilePath))
|
||||
: ParseEnvString(file)
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// Prints out some minor help text
|
||||
function PrintHelp () {
|
||||
return `
|
||||
Usage: env-cmd env_file command [command options]
|
||||
Usage: env-cmd [env_file | env_name] command [command options]
|
||||
|
||||
A simple application for running a cli application using an env config file
|
||||
A simple utility for running a cli application using an env config file.
|
||||
|
||||
Also supports using a .env-cmdrc json file in the execution directory to support multiple
|
||||
environment configs in one file.
|
||||
`
|
||||
}
|
||||
|
||||
@ -123,5 +153,8 @@ module.exports = {
|
||||
HandleUncaughtExceptions,
|
||||
StripComments,
|
||||
StripEmptyLines,
|
||||
ParseEnvVars
|
||||
ParseEnvVars,
|
||||
ParseRCFile,
|
||||
UseRCFile,
|
||||
UseCmdLine
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "env-cmd",
|
||||
"version": "2.2.0",
|
||||
"version": "3.0.0",
|
||||
"description": "Executes a command using the envs in the provided env file",
|
||||
"main": "lib/index.js",
|
||||
"bin": {
|
||||
@ -9,7 +9,9 @@
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"test-cover": "istanbul cover node_modules/.bin/_mocha -- -R spec",
|
||||
"coveralls": "coveralls < coverage/lcov.info"
|
||||
"test-lint": "standard",
|
||||
"coveralls": "coveralls < coverage/lcov.info",
|
||||
"lint": "standard --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -42,6 +44,7 @@
|
||||
"istanbul": "^0.4.4",
|
||||
"mocha": "^3.0.2",
|
||||
"proxyquire": "^1.7.10",
|
||||
"sinon": "^1.17.5"
|
||||
"sinon": "^1.17.5",
|
||||
"standard": "^8.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
58
test/test.js
58
test/test.js
@ -14,7 +14,8 @@ const fs = require('fs')
|
||||
|
||||
const spawnStub = sinon.spy(() => ({
|
||||
on: sinon.stub(),
|
||||
exit: sinon.stub()
|
||||
exit: sinon.stub(),
|
||||
kill: sinon.stub()
|
||||
}))
|
||||
|
||||
const lib = proxyquire('../lib', {
|
||||
@ -43,9 +44,9 @@ const ParseEnvVars = lib.ParseEnvVars
|
||||
|
||||
describe('env-cmd', function () {
|
||||
describe('ParseArgs', function () {
|
||||
it('should parse out the envfile path', function () {
|
||||
it('should parse out the envfile', function () {
|
||||
const parsedArgs = ParseArgs(['./test/envFile', 'command', 'cmda1', 'cmda2'])
|
||||
assert(parsedArgs.envFilePath === path.join(__dirname, 'envFile'))
|
||||
assert(parsedArgs.envFile === './test/envFile')
|
||||
})
|
||||
|
||||
it('should parse out the command', function () {
|
||||
@ -145,10 +146,12 @@ describe('env-cmd', function () {
|
||||
proxyquire.noCallThru()
|
||||
})
|
||||
after(function () {
|
||||
spawnStub.reset()
|
||||
this.readFileStub.restore()
|
||||
proxyquire.callThru()
|
||||
})
|
||||
afterEach(function () {
|
||||
spawnStub.reset()
|
||||
})
|
||||
it('should parse env vars from JSON with node module loader if file extension is .json', function () {
|
||||
EnvCmd(['./test/.env.json', 'echo', '$BOB'])
|
||||
assert(spawnStub.args[0][0] === 'echo')
|
||||
@ -167,6 +170,51 @@ describe('env-cmd', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.RC file support (.env-cmdrc)', function () {
|
||||
before(function () {
|
||||
this.readFileStub = sinon.stub(fs, 'readFileSync')
|
||||
this.readFileStub.returns(`{
|
||||
"development": {
|
||||
"BOB": "COOL",
|
||||
"NODE_ENV": "dev",
|
||||
"ANSWER": "42"
|
||||
},
|
||||
"production": {
|
||||
"BOB": "COOL",
|
||||
"NODE_ENV": "prod",
|
||||
"ANSWER": "43"
|
||||
}
|
||||
}`)
|
||||
this.existsSyncStub = sinon.stub(fs, 'existsSync')
|
||||
this.existsSyncStub.returns(true)
|
||||
proxyquire.noCallThru()
|
||||
})
|
||||
after(function () {
|
||||
this.readFileStub.restore()
|
||||
this.existsSyncStub.restore()
|
||||
proxyquire.callThru()
|
||||
})
|
||||
afterEach(function () {
|
||||
spawnStub.reset()
|
||||
})
|
||||
it('should parse env vars from .env-cmdrc file using development env', function () {
|
||||
EnvCmd(['development', 'echo', '$BOB'])
|
||||
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.ANSWER === '42')
|
||||
})
|
||||
it('should parse env vars from .env-cmdrc file using production env', function () {
|
||||
EnvCmd(['production', 'echo', '$BOB'])
|
||||
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 === 'prod')
|
||||
assert(spawnStub.args[0][2].env.ANSWER === '43')
|
||||
})
|
||||
})
|
||||
|
||||
describe('EnvCmd', function () {
|
||||
before(function () {
|
||||
this.readFileStub = sinon.stub(fs, 'readFileSync')
|
||||
@ -209,8 +257,10 @@ describe('env-cmd', function () {
|
||||
assert(helpText.match(/Usage/g).length !== 0)
|
||||
assert(helpText.match(/env-cmd/).length !== 0)
|
||||
assert(helpText.match(/env_file/).length !== 0)
|
||||
assert(helpText.match(/env_name/).length !== 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('HandleUncaughtExceptions', function () {
|
||||
beforeEach(function () {
|
||||
this.logStub = sinon.stub(console, 'log')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user