From d3eb196ae10d4272eb7e03259b765ebaba81b4d7 Mon Sep 17 00:00:00 2001 From: Andi Bade Date: Fri, 14 Oct 2022 21:09:59 +0200 Subject: [PATCH 1/3] manual loading and parsing of dotenv in watch mode #558 --- lib/watch/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/watch/index.js b/lib/watch/index.js index 81de0b2f..934ce996 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -3,6 +3,7 @@ const path = require('path') const cp = require('child_process') const chalk = require('chalk') +const { readFileSync, existsSync } = require('fs') const { arrayToRegExp, logWatchVerbose } = require('./utils') const { GRACEFUL_SHUT } = require('./constants.js') @@ -39,7 +40,15 @@ const watch = function (args, ignoreWatch, verboseWatch) { const run = (event) => { const childEvent = { childEvent: event } - const env = Object.assign({}, process.env, childEvent, require('dotenv').config().parsed) + + // in order to not override existing env vars, we have to load and parse + // dotenv without modifying process.env + let dotenvParsed = {} + const dotenvPath = path.resolve(process.cwd(), '.env') + if (existsSync(dotenvPath)) { + dotenvParsed = require('dotenv').parse(readFileSync(dotenvPath, { encoding: 'utf-8' })) + } + const env = Object.assign({}, process.env, childEvent, dotenvParsed) const _child = cp.fork(forkPath, args, { env, From 31929a5807edc3d42e59dff301f1d6abc36b2fb1 Mon Sep 17 00:00:00 2001 From: Andi Bade Date: Sat, 15 Oct 2022 10:48:54 +0200 Subject: [PATCH 2/3] added unit-test for #558 --- lib/watch/index.js | 9 ++++++++- test/start.test.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/watch/index.js b/lib/watch/index.js index 934ce996..5e465562 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -47,8 +47,15 @@ const watch = function (args, ignoreWatch, verboseWatch) { const dotenvPath = path.resolve(process.cwd(), '.env') if (existsSync(dotenvPath)) { dotenvParsed = require('dotenv').parse(readFileSync(dotenvPath, { encoding: 'utf-8' })) + Object.keys(dotenvParsed).forEach(function (key) { + if (Object.prototype.hasOwnProperty.call(process.env, key)) { + // we do not want to overrideexisting env-vars + delete dotenvParsed[key] + } + }) } - const env = Object.assign({}, process.env, childEvent, dotenvParsed) + // const env = Object.assign({}, process.env, childEvent, dotenvParsed) + const env = Object.assign({}, dotenvParsed, process.env, childEvent) const _child = cp.fork(forkPath, args, { env, diff --git a/test/start.test.js b/test/start.test.js index 59299776..c8489f41 100644 --- a/test/start.test.js +++ b/test/start.test.js @@ -677,6 +677,38 @@ test('should read env variables from .env file', async (t) => { await fastify.close() }) +test('should read env variables from .env file and not override existing env vars in watch mode', async (t) => { + process.env.GREETING = 'planet' + const testdir = t.testdir({ + '.env': 'GREETING=world', + 'plugin.js': await readFile(path.join(__dirname, '../examples/plugin-with-env.js')) + }) + + const cwd = process.cwd() + + process.chdir(testdir) + + const port = getPort() + const argv = ['-p', port, '-w', path.join(testdir, 'plugin.js')] + const fastifyEmitter = await requireUncached('../start').start(argv) + + t.teardown(() => { + process.chdir(cwd) + }) + + await once(fastifyEmitter, 'ready') + + const r1 = await sget({ + method: 'GET', + url: `http://localhost:${port}` + }) + + t.equal(r1.response.statusCode, 200) + t.same(JSON.parse(r1.body), { hello: 'planet' }) + + await fastifyEmitter.stop() +}) + test('crash on unhandled rejection', t => { t.plan(1) From dbd7b70d90699d80cc519db62a5a7a00118d979f Mon Sep 17 00:00:00 2001 From: Andi Bade Date: Fri, 11 Nov 2022 11:51:45 +0100 Subject: [PATCH 3/3] analyse which env vars should not be overwritten #558 --- lib/watch/index.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/watch/index.js b/lib/watch/index.js index 5e465562..9a9e8bb9 100644 --- a/lib/watch/index.js +++ b/lib/watch/index.js @@ -37,25 +37,34 @@ const watch = function (args, ignoreWatch, verboseWatch) { }) let readyEmitted = false + const envVarsToProtect = []; const run = (event) => { const childEvent = { childEvent: event } // in order to not override existing env vars, we have to load and parse // dotenv without modifying process.env - let dotenvParsed = {} - const dotenvPath = path.resolve(process.cwd(), '.env') + let dotenvParsed = {}; + const dotenvPath = path.resolve(process.cwd(), '.env'); if (existsSync(dotenvPath)) { - dotenvParsed = require('dotenv').parse(readFileSync(dotenvPath, { encoding: 'utf-8' })) - Object.keys(dotenvParsed).forEach(function (key) { - if (Object.prototype.hasOwnProperty.call(process.env, key)) { - // we do not want to overrideexisting env-vars - delete dotenvParsed[key] - } - }) + dotenvParsed = require('dotenv').parse(readFileSync(dotenvPath, { encoding: 'utf-8' })); + if (event == 'start') { + // analyse which env-vars should not be overwritten + Object.keys(dotenvParsed).forEach(function (key) { + if (Object.prototype.hasOwnProperty.call(process.env, key)) { + // if a env var already exists with a different value + // than in parsed dotenv, it has to be protected + if (process.env[key] != dotenvParsed[key]) { + envVarsToProtect.push(key); + } + } + }); + } + for (const envVarToProtect of envVarsToProtect) { + delete dotenvParsed[envVarToProtect]; + } } - // const env = Object.assign({}, process.env, childEvent, dotenvParsed) - const env = Object.assign({}, dotenvParsed, process.env, childEvent) + const env = Object.assign({}, process.env, dotenvParsed, childEvent); const _child = cp.fork(forkPath, args, { env,