diff --git a/CHANGELOG.md b/CHANGELOG.md index e95bac7d..f240c186 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/lib/main.js b/lib/main.js index 64daea9b..91cabe47 100644 --- a/lib/main.js +++ b/lib/main.js @@ -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`) @@ -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') @@ -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 @@ -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(',')}`) diff --git a/package.json b/package.json index 2c54f286..abb20ea9 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/tests/test-config-vault.js b/tests/test-config-vault.js index 3656a5d2..550bbce9 100644 --- a/tests/test-config-vault.js +++ b/tests/test-config-vault.js @@ -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') @@ -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) @@ -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 => { @@ -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.') + } +}) diff --git a/tests/test-config.js b/tests/test-config.js index 029d29ba..7e458f2b 100644 --- a/tests/test-config.js +++ b/tests/test-config.js @@ -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() +})