Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for preload configuration via env vars #355

Merged
merged 2 commits into from Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v1 is >3 years old so making this more straightforward

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
15 changes: 15 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -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