From d49a28149b5467862aa68a47e4667699fab43373 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 10:25:17 -0800 Subject: [PATCH 1/6] fix tests to show failures --- tests/.env.test | 4 ++++ tests/main.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tests/.env.test b/tests/.env.test index b49ebdc..c3cd3e7 100644 --- a/tests/.env.test +++ b/tests/.env.test @@ -68,3 +68,7 @@ DONT_CHOKE8='@}:[4#g%[R-CFR});bY(Z[KcDQDsVn2_y4cSdU { + // Clear process.env before each test + process.env = {} +}) + t.test('returns object', ct => { const dotenv = { parsed: {} } const parsed = dotenvExpand.expand(dotenv).parsed @@ -167,6 +172,15 @@ t.test('expands environment variables', ct => { ct.end() }) +t.test('expands environment variables (process.env)', ct => { + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + dotenvExpand.expand(dotenv) + + ct.equal(process.env.BASIC_EXPAND, 'basic') + + ct.end() +}) + t.test('expands environment variables existing already on the machine', ct => { process.env.MACHINE = 'machine' @@ -178,6 +192,17 @@ t.test('expands environment variables existing already on the machine', ct => { ct.end() }) +t.test('expands environment variables existing already on the machine (process.env)', ct => { + process.env.MACHINE = 'machine' + + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + dotenvExpand.expand(dotenv) + + ct.equal(process.env.MACHINE_EXPAND, 'machine') + + ct.end() +}) + t.test('expands missing environment variables to an empty string', ct => { const dotenv = require('dotenv').config({ path: 'tests/.env.test', processEnv: {} }) const parsed = dotenvExpand.expand(dotenv).parsed @@ -187,6 +212,15 @@ t.test('expands missing environment variables to an empty string', ct => { ct.end() }) +t.test('expands missing environment variables to an empty string (process.env)', ct => { + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + const parsed = dotenvExpand.expand(dotenv).parsed + + ct.equal(parsed.UNDEFINED_EXPAND, '') + + ct.end() +}) + t.test('expands environment variables existing already on the machine even with a default', ct => { process.env.MACHINE = 'machine' @@ -421,3 +455,12 @@ t.test('expands self without a recursive call stack error', ct => { ct.end() }) + +t.test('expands DOMAIN with ${HOST}', ct => { + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + const parsed = dotenvExpand.expand(dotenv).parsed + + ct.equal(parsed.DOMAIN, 'https://something') + + ct.end() +}) From 88c70141ca8698186dd847ea58b0d419bfe5e487 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 10:35:17 -0800 Subject: [PATCH 2/6] fix HOST, DOMAIN example failing test --- lib/main.js | 7 +++++-- tests/main.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/main.js b/lib/main.js index ca783f8..3027229 100644 --- a/lib/main.js +++ b/lib/main.js @@ -47,8 +47,11 @@ function expand (options) { for (const key in options.parsed) { let value = options.parsed[key] - // don't interpolate if it exists already in processEnv - if (Object.prototype.hasOwnProperty.call(processEnv, key)) { + // don't interpolate if it exists already in processEnv AND not in options.parsed + // console.log('exists in process.env?', Object.prototype.hasOwnProperty.call(processEnv, key)) + // console.log('exists in options.parsed?', Object.prototype.hasOwnProperty.call(options.parsed, key)) + + if (Object.prototype.hasOwnProperty.call(processEnv, key) && !Object.prototype.hasOwnProperty.call(options.parsed, key)) { value = processEnv[key] } else { value = interpolate(value, processEnv, options.parsed) diff --git a/tests/main.js b/tests/main.js index 6645ead..0c46c8f 100644 --- a/tests/main.js +++ b/tests/main.js @@ -5,7 +5,7 @@ const t = require('tap') const dotenvExpand = require('../lib/main') t.beforeEach((ct) => { - // Clear process.env before each test + // important, clear process.env before each test process.env = {} }) From 21840a290e3ce06bbbec1e69b355543819d86b83 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 11:00:27 -0800 Subject: [PATCH 3/6] further cover HOST, DOMAIN issue with a few other edge cases left to handle --- lib/main.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/main.js b/lib/main.js index 3027229..4b7e827 100644 --- a/lib/main.js +++ b/lib/main.js @@ -47,13 +47,23 @@ function expand (options) { for (const key in options.parsed) { let value = options.parsed[key] - // don't interpolate if it exists already in processEnv AND not in options.parsed - // console.log('exists in process.env?', Object.prototype.hasOwnProperty.call(processEnv, key)) - // console.log('exists in options.parsed?', Object.prototype.hasOwnProperty.call(options.parsed, key)) + const inProcessEnv = Object.prototype.hasOwnProperty.call(processEnv, key) + const inParsed = Object.prototype.hasOwnProperty.call(options.parsed, key) - if (Object.prototype.hasOwnProperty.call(processEnv, key) && !Object.prototype.hasOwnProperty.call(options.parsed, key)) { - value = processEnv[key] + if (inProcessEnv) { + if (inParsed) { + // if the values are the same, assume the processEnv value came from the .env file and attempt interpolation + if (processEnv[key] === options.parsed[key]) { + value = interpolate(value, processEnv, options.parsed) + } else { + value = processEnv[key] + } + } else { + // pre-existed in processEnv so do not attempt interpolation. example: 'pas$word' + value = processEnv[key] + } } else { + // not inProcessEnv so assume interpolation for this .env key value = interpolate(value, processEnv, options.parsed) } From bd1b226eb89ef045537a08d5f0b22abfc8d2aca7 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 12:12:12 -0800 Subject: [PATCH 4/6] bug fix --- lib/main.js | 6 +++++- tests/main.js | 28 ++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/main.js b/lib/main.js index 4b7e827..0d11293 100644 --- a/lib/main.js +++ b/lib/main.js @@ -25,6 +25,10 @@ function interpolate (value, processEnv, parsed) { return processEnv[key] } + if (parsed[key]) { + return parsed[key] + } + if (defaultValue) { if (defaultValue.startsWith('$')) { return interpolate(defaultValue, processEnv, parsed) @@ -33,7 +37,7 @@ function interpolate (value, processEnv, parsed) { } } - return parsed[key] || '' + return '' } }) } diff --git a/tests/main.js b/tests/main.js index 0c46c8f..a39612b 100644 --- a/tests/main.js +++ b/tests/main.js @@ -269,8 +269,18 @@ t.test('expands environent variables and concats with default nested', ct => { const dotenv = require('dotenv').config({ path: 'tests/.env.test', processEnv: {} }) const parsed = dotenvExpand.expand(dotenv).parsed - ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE, 'machinedefault') - ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE2, 'machinedefault') + ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE, 'machine_envdefault') + ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE2, 'machine_envdefault') + + ct.end() +}) + +t.test('expands environent variables and concats with default nested', ct => { + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + const parsed = dotenvExpand.expand(dotenv).parsed + + ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE, 'machine_envdefault') + ct.equal(parsed.EXPAND_DEFAULT_NESTED_TWICE2, 'machine_envdefault') ct.end() }) @@ -364,8 +374,18 @@ t.test('expands environment variables existing already on the machine even with const dotenv = require('dotenv').config({ path: 'tests/.env.test', processEnv: {} }) const parsed = dotenvExpand.expand(dotenv).parsed - ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS, 'machine') - ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS2, 'machine') + ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS, 'machine_env') + ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS2, 'machine_env') + + ct.end() +}) + +t.test('expands environment variables existing already on the machine even with a default with special characters (process.env)', ct => { + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + const parsed = dotenvExpand.expand(dotenv).parsed + + ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS, 'machine_env') + ct.equal(parsed.EXPAND_DEFAULT_SPECIAL_CHARACTERS2, 'machine_env') ct.end() }) From efd1ed27dde9db66bc99d0cf9e3ba3339894d195 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 12:20:44 -0800 Subject: [PATCH 5/6] adjust to support processEnv empty object or from process.env --- lib/main.js | 13 ++++--------- tests/main.js | 12 ++++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/main.js b/lib/main.js index 0d11293..511b1c3 100644 --- a/lib/main.js +++ b/lib/main.js @@ -52,18 +52,13 @@ function expand (options) { let value = options.parsed[key] const inProcessEnv = Object.prototype.hasOwnProperty.call(processEnv, key) - const inParsed = Object.prototype.hasOwnProperty.call(options.parsed, key) if (inProcessEnv) { - if (inParsed) { - // if the values are the same, assume the processEnv value came from the .env file and attempt interpolation - if (processEnv[key] === options.parsed[key]) { - value = interpolate(value, processEnv, options.parsed) - } else { - value = processEnv[key] - } + if (processEnv[key] === options.parsed[key]) { + // assume was set to processEnv from the .env file if the values match and therefore interpolate + value = interpolate(value, processEnv, options.parsed) } else { - // pre-existed in processEnv so do not attempt interpolation. example: 'pas$word' + // do not interpolate - assume processEnv had the intended value even if containing a $. value = processEnv[key] } } else { diff --git a/tests/main.js b/tests/main.js index a39612b..cf2486d 100644 --- a/tests/main.js +++ b/tests/main.js @@ -480,7 +480,19 @@ t.test('expands DOMAIN with ${HOST}', ct => { const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) const parsed = dotenvExpand.expand(dotenv).parsed + ct.equal(parsed.HOST, 'something') ct.equal(parsed.DOMAIN, 'https://something') ct.end() }) + +t.test('does not attempt to expand password if already existed in processEnv', ct => { + process.env.PASSWORD = 'pas$word' + + const dotenv = require('dotenv').config({ path: 'tests/.env.test' }) + dotenvExpand.expand(dotenv) + + ct.equal(process.env.PASSWORD, 'pas$word') + + ct.end() +}) From 7dc56d1eaf041df9e262cba32fdcf464b7604d53 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sun, 11 Feb 2024 12:23:22 -0800 Subject: [PATCH 6/6] =?UTF-8?q?changelog=20=F0=9F=AA=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96251ce..586dda8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ 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-expand/compare/v11.0.2...master) +## [Unreleased](https://github.com/motdotla/dotenv-expand/compare/v11.0.3...master) + +## [11.0.3](https://github.com/motdotla/dotenv-expand/compare/v11.0.2...v11.0.3) (2024-02-11) + +### Changed + +- 🐞 bug fix when `processEnv` set to process.env rather than empty object (also test fixes which hid the bug) ([#113](https://github.com/motdotla/dotenv-expand/pull/113)) ## [11.0.2](https://github.com/motdotla/dotenv-expand/compare/v11.0.1...v11.0.2) (2024-02-10)