@dotenvx/dotenvx 0.19.1 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli/actions/run.js +40 -40
- package/src/cli/dotenvx.js +1 -0
- package/src/lib/services/runDefault.js +155 -0
package/package.json
CHANGED
package/src/cli/actions/run.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
2
3
|
const execa = require('execa')
|
|
3
4
|
const logger = require('./../../shared/logger')
|
|
4
5
|
const helpers = require('./../helpers')
|
|
5
6
|
const main = require('./../../lib/main')
|
|
6
7
|
const parseEncryptionKeyFromDotenvKey = require('./../../lib/helpers/parseEncryptionKeyFromDotenvKey')
|
|
7
8
|
|
|
9
|
+
const RunDefault = require('./../../lib/services/runDefault')
|
|
10
|
+
|
|
8
11
|
const ENCODING = 'utf8'
|
|
9
12
|
const REPORT_ISSUE_LINK = 'https://github.com/dotenvx/dotenvx/issues/new'
|
|
10
13
|
|
|
@@ -36,7 +39,11 @@ const executeCommand = async function (commandArgs, env) {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
try {
|
|
39
|
-
|
|
42
|
+
const systemCommandPath = execa.sync('which', [commandArgs[0]]).stdout
|
|
43
|
+
logger.debug(`system command path [${systemCommandPath}]`)
|
|
44
|
+
|
|
45
|
+
// commandProcess = execa(commandArgs[0], commandArgs.slice(1), {
|
|
46
|
+
commandProcess = execa(systemCommandPath, commandArgs.slice(1), {
|
|
40
47
|
stdio: 'inherit',
|
|
41
48
|
env: { ...process.env, ...env }
|
|
42
49
|
})
|
|
@@ -156,55 +163,48 @@ async function run () {
|
|
|
156
163
|
}
|
|
157
164
|
}
|
|
158
165
|
} else {
|
|
159
|
-
|
|
160
|
-
let optionEnvFile = options.envFile
|
|
161
|
-
if (!Array.isArray(optionEnvFile)) {
|
|
162
|
-
optionEnvFile = [optionEnvFile]
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const readableFilepaths = new Set()
|
|
166
|
-
const injected = new Set()
|
|
166
|
+
const { files, readableFilepaths, uniqueInjectedKeys } = new RunDefault(options.envFile, options.env, options.overload).run()
|
|
167
167
|
|
|
168
|
-
for (const
|
|
169
|
-
const filepath =
|
|
168
|
+
for (const file of files) {
|
|
169
|
+
const filepath = file.filepath
|
|
170
170
|
|
|
171
|
-
logger.verbose(`loading env from ${filepath}`)
|
|
171
|
+
logger.verbose(`loading env from ${filepath} (${path.resolve(filepath)})`)
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
173
|
+
if (file.error) {
|
|
174
|
+
if (file.error.code === 'MISSING_ENV_FILE') {
|
|
175
|
+
logger.warnv(file.error)
|
|
176
|
+
logger.help(`? in development: add one with [echo "HELLO=World" > ${filepath}] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
|
|
177
|
+
logger.help('? for production: set [DOTENV_KEY] on your server and re-deploy')
|
|
178
|
+
logger.help('? for ci: set [DOTENV_KEY] on your ci and re-build')
|
|
179
|
+
} else {
|
|
180
|
+
logger.warnv(file.error)
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
// debug parsed
|
|
184
|
+
const parsed = file.parsed
|
|
185
|
+
logger.debug(parsed)
|
|
186
|
+
|
|
187
|
+
// verbose/debug injected key/value
|
|
188
|
+
const injected = file.injected
|
|
189
|
+
for (const [key, value] of Object.entries(injected)) {
|
|
190
|
+
logger.verbose(`${key} set`)
|
|
191
|
+
logger.debug(`${key} set to ${value}`)
|
|
186
192
|
}
|
|
187
193
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
logger.help('? for production: set [DOTENV_KEY] on your server and re-deploy')
|
|
194
|
-
logger.help('? for ci: set [DOTENV_KEY] on your ci and re-build')
|
|
195
|
-
break
|
|
196
|
-
|
|
197
|
-
// unhandled error
|
|
198
|
-
default:
|
|
199
|
-
logger.warn(e)
|
|
200
|
-
break
|
|
194
|
+
// verbose/debug preExisted key/value
|
|
195
|
+
const preExisted = file.preExisted
|
|
196
|
+
for (const [key, value] of Object.entries(preExisted)) {
|
|
197
|
+
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
198
|
+
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
|
|
201
199
|
}
|
|
202
200
|
}
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
let msg = `injecting env (${uniqueInjectedKeys.length})`
|
|
204
|
+
if (readableFilepaths.length > 0) {
|
|
205
|
+
msg += ` from ${readableFilepaths}`
|
|
207
206
|
}
|
|
207
|
+
logger.successv(msg)
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// Extract command and arguments after '--'
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -56,6 +56,7 @@ program.command('run')
|
|
|
56
56
|
.addHelpText('after', examples.run)
|
|
57
57
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
58
58
|
.option('-fv, --env-vault-file <path>', 'path to your .env.vault file', '.env.vault')
|
|
59
|
+
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")')
|
|
59
60
|
.option('-o, --overload', 'override existing env variables')
|
|
60
61
|
.action(require('./actions/run'))
|
|
61
62
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const dotenv = require('dotenv')
|
|
4
|
+
const dotenvExpand = require('dotenv-expand')
|
|
5
|
+
|
|
6
|
+
const ENCODING = 'utf8'
|
|
7
|
+
|
|
8
|
+
class RunDefault {
|
|
9
|
+
constructor (envFile = '.env', env = [], overload = false) {
|
|
10
|
+
this.envFile = envFile
|
|
11
|
+
this.env = env
|
|
12
|
+
this.overload = overload
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
run () {
|
|
16
|
+
const strings = []
|
|
17
|
+
const files = []
|
|
18
|
+
const readableFilepaths = new Set()
|
|
19
|
+
const uniqueInjectedKeys = new Set()
|
|
20
|
+
|
|
21
|
+
const envs = this._envs()
|
|
22
|
+
for (const env of envs) {
|
|
23
|
+
const row = {}
|
|
24
|
+
row.string = env
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const parsed = this._parseExpand(env)
|
|
28
|
+
row.parsed = parsed
|
|
29
|
+
|
|
30
|
+
const { injected, preExisted } = this._inject(process.env, parsed)
|
|
31
|
+
row.injected = injected
|
|
32
|
+
row.preExisted = preExisted
|
|
33
|
+
|
|
34
|
+
for (const key of Object.keys(injected)) {
|
|
35
|
+
uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
row.error = e
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
strings.push(row)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const envFilepaths = this._envFilepaths()
|
|
45
|
+
for (const envFilepath of envFilepaths) {
|
|
46
|
+
const row = {}
|
|
47
|
+
row.filepath = envFilepath
|
|
48
|
+
|
|
49
|
+
const filepath = path.resolve(envFilepath)
|
|
50
|
+
try {
|
|
51
|
+
const src = fs.readFileSync(filepath, { encoding: ENCODING })
|
|
52
|
+
readableFilepaths.add(envFilepath)
|
|
53
|
+
|
|
54
|
+
const parsed = this._parseExpand(src)
|
|
55
|
+
row.parsed = parsed
|
|
56
|
+
|
|
57
|
+
const { injected, preExisted } = this._inject(process.env, parsed)
|
|
58
|
+
row.injected = injected
|
|
59
|
+
row.preExisted = preExisted
|
|
60
|
+
|
|
61
|
+
for (const key of Object.keys(injected)) {
|
|
62
|
+
uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
if (e.code === 'ENOENT') {
|
|
66
|
+
const error = new Error(`missing ${envFilepath} file (${filepath})`)
|
|
67
|
+
error.code = 'MISSING_ENV_FILE'
|
|
68
|
+
|
|
69
|
+
row.error = error
|
|
70
|
+
} else {
|
|
71
|
+
row.error = e
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
files.push(row)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
files,
|
|
80
|
+
strings,
|
|
81
|
+
readableFilepaths: [...readableFilepaths], // array
|
|
82
|
+
uniqueInjectedKeys: [...uniqueInjectedKeys]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_envFilepaths () {
|
|
87
|
+
if (!Array.isArray(this.envFile)) {
|
|
88
|
+
return [this.envFile]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.envFile
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_envs () {
|
|
95
|
+
if (!Array.isArray(this.env)) {
|
|
96
|
+
return [this.env]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this.env
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_parseExpand (src) {
|
|
103
|
+
const parsed = dotenv.parse(src)
|
|
104
|
+
|
|
105
|
+
// consider moving this logic straight into dotenv-expand
|
|
106
|
+
let inputParsed = {}
|
|
107
|
+
if (this.overload) {
|
|
108
|
+
inputParsed = { ...process.env, ...parsed }
|
|
109
|
+
} else {
|
|
110
|
+
inputParsed = { ...parsed, ...process.env }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const expandPlease = {
|
|
114
|
+
processEnv: {},
|
|
115
|
+
parsed: inputParsed
|
|
116
|
+
}
|
|
117
|
+
const expanded = dotenvExpand.expand(expandPlease).parsed
|
|
118
|
+
|
|
119
|
+
// but then for logging only log the original keys existing in parsed. this feels unnecessarily complex - like dotenv-expand should support the ability to inject additional `process.env` or objects as it sees fit to the object it wants to expand
|
|
120
|
+
const result = {}
|
|
121
|
+
for (const key in parsed) {
|
|
122
|
+
result[key] = expanded[key]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return result
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_inject (processEnv = {}, parsed = {}) {
|
|
129
|
+
const injected = {}
|
|
130
|
+
const preExisted = {}
|
|
131
|
+
|
|
132
|
+
// set processEnv
|
|
133
|
+
for (const key of Object.keys(parsed)) {
|
|
134
|
+
if (processEnv[key]) {
|
|
135
|
+
if (this.overload === true) {
|
|
136
|
+
processEnv[key] = parsed[key]
|
|
137
|
+
|
|
138
|
+
injected[key] = parsed[key] // track injected key/value
|
|
139
|
+
} else {
|
|
140
|
+
preExisted[key] = processEnv[key] // track preExisted key/value
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
processEnv[key] = parsed[key]
|
|
144
|
+
injected[key] = parsed[key] // track injected key/value
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
injected,
|
|
150
|
+
preExisted
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = RunDefault
|