Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.6.0...master)
## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.6.1...master)

## [16.6.1](https://github.com/motdotla/dotenv/compare/v16.6.0...v16.6.1) (2025-06-27)

### Changed

- Default `quiet` to true – hiding the runtime log message ([#874](https://github.com/motdotla/dotenv/pull/874))
- NOTICE: 17.0.0 will be released with quiet defaulting to false. Use `config({ quiet: true })` to suppress.
- And check out the new [dotenvx](https://github.com/dotenvx/dotenvx). As coding workflows evolve and agents increasingly handle secrets, encrypted .env files offer a much safer way to deploy both agents and code together with secure secrets.

## [16.6.0](https://github.com/motdotla/dotenv/compare/v16.5.0...v16.6.0) (2025-06-26)

Expand Down
17 changes: 11 additions & 6 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ function parse (src) {
}

function _parseVault (options) {
const vaultPath = _vaultPath(options)
options = options || {}

// Parse .env.vault
options.path = vaultPath
const vaultPath = _vaultPath(options)
options.path = vaultPath // parse .env.vault
const result = DotenvModule.configDotenv(options)
if (!result.parsed) {
const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`)
Expand Down Expand Up @@ -190,7 +190,7 @@ function _resolveHome (envPath) {

function _configVault (options) {
const debug = Boolean(options && options.debug)
const quiet = Boolean(options && options.quiet)
const quiet = options && 'quiet' in options ? options.quiet : true

if (debug || !quiet) {
_log('Loading env from encrypted .env.vault')
Expand All @@ -212,7 +212,7 @@ function configDotenv (options) {
const dotenvPath = path.resolve(process.cwd(), '.env')
let encoding = 'utf8'
const debug = Boolean(options && options.debug)
const quiet = Boolean(options && options.quiet)
const quiet = options && 'quiet' in options ? options.quiet : true

if (options && options.encoding) {
encoding = options.encoding
Expand Down Expand Up @@ -266,7 +266,12 @@ function configDotenv (options) {
try {
const relative = path.relative(process.cwd(), filePath)
shortPaths.push(relative)
} catch {}
} catch (e) {
if (debug) {
_debug(`Failed to load ${filePath} ${e.message}`)
}
lastError = e
}
}

_log(`injecting env (${keysCount}) from ${shortPaths.join(',')}`)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"lint": "standard",
"pretest": "npm run lint && npm run dts-check",
"test": "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
"test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=lcov",
"test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
"prerelease": "npm test",
"release": "standard-version"
},
Expand Down
38 changes: 33 additions & 5 deletions tests/test-config-vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ t.test('logs when no path is set', ct => {
ct.ok(logStub.called)
})

t.test('DOES log by default', ct => {
t.test('does not log by default', ct => {
ct.plan(1)

logStub = sinon.stub(console, 'log')

dotenv.config({ path: testPath })
ct.ok(logStub.called)
ct.ok(logStub.notCalled)
})

t.test('does not log if quiet flag passed', ct => {
t.test('does not log if quiet flag passed true', ct => {
ct.plan(1)

logStub = sinon.stub(console, 'log')
Expand All @@ -52,6 +52,24 @@ t.test('does not log if quiet flag passed', ct => {
ct.ok(logStub.notCalled)
})

t.test('does log if quiet flag false', ct => {
ct.plan(1)

logStub = sinon.stub(console, 'log')

dotenv.config({ path: testPath, quiet: false })
ct.ok(logStub.called)
})

t.test('does log if quiet flag present and undefined/null', ct => {
ct.plan(1)

logStub = sinon.stub(console, 'log')

dotenv.config({ path: testPath, quiet: undefined })
ct.ok(logStub.called)
})

t.test('logs if debug set', ct => {
ct.plan(1)

Expand All @@ -61,13 +79,13 @@ t.test('logs if debug set', ct => {
ct.ok(logStub.called)
})

t.test('logs when testPath calls to .env.vault directly (interpret what the user meant)', ct => {
t.test('does not log when testPath calls to .env.vault directly (interpret what the user meant)', ct => {
ct.plan(1)

logStub = sinon.stub(console, 'log')

dotenv.config({ path: `${testPath}.vault` })
ct.ok(logStub.called)
ct.ok(logStub.notCalled)
})

t.test('logs when testPath calls to .env.vault directly (interpret what the user meant) and debug true', ct => {
Expand Down Expand Up @@ -390,3 +408,13 @@ t.test('raises error if some other uncaught decryption error', ct => {

ct.end()
})

t.test('_parseVault when empty args', ct => {
ct.plan(1)

try {
dotenv._parseVault()
} catch (e) {
ct.equal(e.message, 'NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment DOTENV_VAULT_DEVELOPMENT in your .env.vault file.')
}
})
53 changes: 53 additions & 0 deletions tests/test-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,56 @@ t.test('logs any errors parsing when in debug and override mode', ct => {

logStub.restore()
})

t.test('deals with file:// path', ct => {
const logStub = sinon.stub(console, 'log')

const testPath = 'file:///tests/.env'
const env = dotenv.config({ path: testPath })

ct.equal(env.parsed.BASIC, undefined)
ct.equal(process.env.BASIC, undefined)
ct.equal(env.error.message, "ENOENT: no such file or directory, open 'file:///tests/.env'")

ct.ok(logStub.notCalled)

logStub.restore()

ct.end()
})

t.test('deals with file:// path and debug true', ct => {
const logStub = sinon.stub(console, 'log')

const testPath = 'file:///tests/.env'
const env = dotenv.config({ path: testPath, debug: true })

ct.equal(env.parsed.BASIC, undefined)
ct.equal(process.env.BASIC, undefined)
ct.equal(env.error.message, "ENOENT: no such file or directory, open 'file:///tests/.env'")

ct.ok(logStub.called)

logStub.restore()

ct.end()
})

t.test('path.relative fails somehow', ct => {
const logStub = sinon.stub(console, 'log')
const pathRelativeStub = sinon.stub(path, 'relative').throws(new Error('fail'))

const testPath = 'file:///tests/.env'
const env = dotenv.config({ path: testPath, debug: true })

ct.equal(env.parsed.BASIC, undefined)
ct.equal(process.env.BASIC, undefined)
ct.equal(env.error.message, 'fail')

ct.ok(logStub.called)

logStub.restore()
pathRelativeStub.restore()

ct.end()
})