Skip to content

Commit

Permalink
add support for preload configuration via env vars (#355)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxbeatty committed Dec 5, 2018
1 parent 1f84ba1 commit 1eacd53
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 35 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [6.2.0] - 2018-12-03

### Added

- Support preload configuration via environment variables ([#351](https://github.com/motdotla/dotenv/issues/351))

## [6.1.0] - 2018-10-08

### Added
Expand Down Expand Up @@ -32,7 +38,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Removed

- Testing aginst Node v7
- Testing against Node v7


## [4.0.0] - 2016-12-23
Expand Down Expand Up @@ -98,7 +104,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Removed
- support for multiple `.env` files. should always use one `.env` file for the current environment

[Unreleased]: https://github.com/motdotla/dotenv/compare/v6.1.0...HEAD
[Unreleased]: https://github.com/motdotla/dotenv/compare/v6.2.0...HEAD
[6.2.0]: https://github.com/motdotla/dotenv/compare/v6.1.0...v6.2.0
[6.1.0]: https://github.com/motdotla/dotenv/compare/v6.0.0...v6.1.0
[6.0.0]: https://github.com/motdotla/dotenv/compare/v5.0.0...v6.0.0
[5.0.0]: https://github.com/motdotla/dotenv/compare/v4.0.0...v5.0.0
Expand Down
16 changes: 12 additions & 4 deletions README.md
Expand Up @@ -53,7 +53,7 @@ db.connect({

### Preload

You can use the `--require` (`-r`) command line option to preload dotenv. By doing this, you do not need to require and load dotenv in your application code. This is the preferred approach when using `import` instead of `require`.
You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#cli_r_require_module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code. This is the preferred approach when using `import` instead of `require`.

```bash
$ node -r dotenv/config your_script.js
Expand All @@ -65,6 +65,16 @@ The configuration options below are supported as command line arguments in the f
$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/your/env/vars
```

Additionally, you can use environment variables to set configuration options. Command line arguments will precede these.

```bash
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js
```

```bash
$ DOTENV_CONFIG_ENCODING=base64 node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
```

## Config

_Alias: `load`_
Expand Down Expand Up @@ -212,9 +222,7 @@ variableExpansion(myEnv)

### What about variable expansion?

For `dotenv@2.x.x`: Use [dotenv-expand](https://github.com/motdotla/dotenv-expand).

For `dotenv@1.x.x`: We haven't been presented with a compelling use case for expanding variables and believe it leads to env vars that are not "fully orthogonal" as [The Twelve-Factor App](http://12factor.net/config) outlines.<sup>[[1](https://github.com/motdotla/dotenv/issues/39)][[2](https://github.com/motdotla/dotenv/pull/97)]</sup> Please open an issue if you have a compelling use case.
Try [dotenv-expand](https://github.com/motdotla/dotenv-expand)

### How do I use dotenv with `import`?

Expand Down
6 changes: 5 additions & 1 deletion config.js
Expand Up @@ -2,6 +2,10 @@

(function () {
require('./lib/main').config(
require('./lib/cli-options')(process.argv)
Object.assign(
{},
require('./lib/env-options'),
require('./lib/cli-options')(process.argv)
)
)
})()
18 changes: 18 additions & 0 deletions lib/env-options.js
@@ -0,0 +1,18 @@
/* @flow */

// ../config.js accepts options via environment variables
const options = {}

if (process.env.DOTENV_CONFIG_ENCODING) {
options.encoding = process.env.DOTENV_CONFIG_ENCODING
}

if (process.env.DOTENV_CONFIG_PATH) {
options.path = process.env.DOTENV_CONFIG_PATH
}

if (process.env.DOTENV_CONFIG_DEBUG) {
options.debug = process.env.DOTENV_CONFIG_DEBUG
}

module.exports = options
17 changes: 16 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "dotenv",
"version": "6.2.0-rc1",
"version": "6.2.0-0",
"description": "Loads environment variables from .env file",
"main": "lib/main.js",
"scripts": {
Expand All @@ -26,6 +26,7 @@
"readmeFilename": "README.md",
"license": "BSD-2-Clause",
"devDependencies": {
"decache": "^4.5.0",
"flow-bin": "^0.84.0",
"sinon": "^6.3.5",
"standard": "^12.0.1",
Expand Down
65 changes: 51 additions & 14 deletions tests/test-config-cli.js
Expand Up @@ -3,29 +3,66 @@
const cp = require('child_process')
const path = require('path')

const test = require('tap').test
const t = require('tap')

const nodeBinary = process.argv[0]
function spawn (cmd, options = {}) {
const { stdout } = cp.spawnSync(
process.argv[0], // node binary
cmd,
Object.assign(
{},
{
cwd: path.resolve(__dirname, '..'),
timeout: 5000,
encoding: 'utf8'
},
options
)
)

test('config preload loads .env', t => {
t.plan(1)
return stdout
}

const { stdout } = cp.spawnSync(
nodeBinary,
t.plan(3)

// dotenv/config enables preloading
t.equal(
spawn([
'-r',
'../config',
'-e',
'console.log(process.env.BASIC)',
'dotenv_config_encoding=utf8',
'dotenv_config_path=./tests/.env'
]),
'basic\n'
)

// dotenv/config supports configuration via environment variables
t.equal(
spawn(['-r', '../config', '-e', 'console.log(process.env.BASIC)'], {
env: {
DOTENV_CONFIG_PATH: './tests/.env'
}
}),
'basic\n'
)

// dotenv/config takes CLI configuration over environment variables
t.equal(
spawn(
[
'-r',
'../config',
'-e',
'console.log(process.env.BASIC)',
'dotenv_config_encoding=utf8',
'dotenv_config_path=./tests/.env'
],
{
cwd: path.resolve(__dirname, '..'),
timeout: 5000,
encoding: 'utf8'
env: {
DOTENV_CONFIG_PATH: '/tmp/dne/path/.env.should.break'
}
}
)

t.equal(stdout, 'basic\n')
})
),
'basic\n'
)
29 changes: 17 additions & 12 deletions tests/test-config.js
Expand Up @@ -11,6 +11,8 @@ const mockParseResponse = { test: 'foo' }
let readFileSyncStub
let parseStub

t.plan(9)

t.beforeEach(done => {
readFileSyncStub = sinon.stub(fs, 'readFileSync').returns('test=foo')
parseStub = sinon.stub(dotenv, 'parse').returns(mockParseResponse)
Expand Down Expand Up @@ -81,18 +83,21 @@ t.test('does not write over keys already in process.env', ct => {
ct.equal(process.env.test, existing)
})

t.test('does not write over keys already in process.env if the key has a falsy value', ct => {
ct.plan(2)

const existing = ''
process.env.test = existing
// 'foo' returned as value in `beforeEach`. should keep this ''
const env = dotenv.config()

ct.equal(env.parsed && env.parsed.test, mockParseResponse.test)
// NB: process.env.test becomes undefined on Windows
ct.notOk(process.env.test)
})
t.test(
'does not write over keys already in process.env if the key has a falsy value',
ct => {
ct.plan(2)

const existing = ''
process.env.test = existing
// 'foo' returned as value in `beforeEach`. should keep this ''
const env = dotenv.config()

ct.equal(env.parsed && env.parsed.test, mockParseResponse.test)
// NB: process.env.test becomes undefined on Windows
ct.notOk(process.env.test)
}
)

t.test('returns parsed object', ct => {
ct.plan(2)
Expand Down
50 changes: 50 additions & 0 deletions tests/test-env-options.js
@@ -0,0 +1,50 @@
/* @flow */

const t = require('tap')
const decache = require('decache')

// warm cache
require('../lib/env-options')

// preserve existing env
const e = process.env.DOTENV_CONFIG_ENCODING
const p = process.env.DOTENV_CONFIG_PATH
const d = process.env.DOTENV_CONFIG_DEBUG

// get fresh object for each test
function options () {
decache('../lib/env-options.js')
return require('../lib/env-options')
}

function testOption (envVar, tmpVal, expect) {
delete process.env[envVar]
process.env[envVar] = tmpVal

t.same(options(), expect)

delete process.env[envVar]
}

t.plan(4)

// returns empty object when no options set in process.env
delete process.env.DOTENV_CONFIG_ENCODING
delete process.env.DOTENV_CONFIG_PATH
delete process.env.DOTENV_CONFIG_DEBUG

t.same(options(), {})

// sets encoding option
testOption('DOTENV_CONFIG_ENCODING', 'base64', { encoding: 'base64' })

// sets path option
testOption('DOTENV_CONFIG_PATH', '~/.env.test', { path: '~/.env.test' })

// sets debug option
testOption('DOTENV_CONFIG_DEBUG', 'true', { debug: 'true' })

// restore existing env
process.env.DOTENV_CONFIG_ENCODING = e
process.env.DOTENV_CONFIG_PATH = p
process.env.DOTENV_CONFIG_DEBUG = d

0 comments on commit 1eacd53

Please sign in to comment.