@dotenvx/dotenvx 2.12.0 → 2.14.0
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/CHANGELOG.md +18 -1
- package/README.md +50 -1
- package/package.json +2 -2
- package/src/cli/actions/get.js +3 -2
- package/src/cli/actions/run.js +15 -0
- package/src/cli/actions/validate.js +84 -0
- package/src/cli/dotenvx.js +25 -7
- package/src/lib/helpers/errors.js +26 -0
- package/src/lib/helpers/escape.js +2 -1
- package/src/lib/helpers/validate.js +40 -0
- package/src/lib/helpers/validateEnvExample.js +24 -0
- package/src/lib/main.js +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
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.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.14.0...main)
|
|
6
|
+
|
|
7
|
+
## [2.14.0](https://github.com/dotenvx/dotenvx/compare/v2.13.0...v2.14.0) (2026-07-16)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add `dotenvx run --validate` for simple env validation against your `.env.example` ([#906](https://github.com/dotenvx/dotenvx/pull/906))
|
|
12
|
+
* Add `dotenvx validate` for a pre-check of validation against your `.env.example` file ([#906](https://github.com/dotenvx/dotenvx/pull/906))
|
|
13
|
+
|
|
14
|
+
## [2.13.0](https://github.com/dotenvx/dotenvx/compare/v2.12.0...v2.13.0) (2026-07-16)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
* Add `get --eval-export` flag ([#904](https://github.com/dotenvx/dotenvx/pull/904))
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
* Make `get --eval` posix safe ([#904](https://github.com/dotenvx/dotenvx/pull/904))
|
|
6
23
|
|
|
7
24
|
## [2.12.0](https://github.com/dotenvx/dotenvx/compare/v2.11.3...v2.12.0) (2026-07-16)
|
|
8
25
|
|
package/README.md
CHANGED
|
@@ -1453,6 +1453,54 @@ Hello production
|
|
|
1453
1453
|
|
|
1454
1454
|
Available log levels are `error, warn, info, verbose, debug, silly` ([source](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
1455
1455
|
|
|
1456
|
+
</details>
|
|
1457
|
+
<details><summary>`run --validate`</summary><br>
|
|
1458
|
+
|
|
1459
|
+
Validate your environment against `.env.example`.
|
|
1460
|
+
|
|
1461
|
+
```ini
|
|
1462
|
+
# .env.example
|
|
1463
|
+
DATABASE_URL=
|
|
1464
|
+
API_KEY=
|
|
1465
|
+
SENTRY_DSN= # optional
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
```sh
|
|
1469
|
+
$ dotenvx run --validate -- node index.js
|
|
1470
|
+
[VALIDATION_FAILED] missing required (DATABASE_URL, API_KEY). fix: [https://github.com/dotenvx/dotenvx/issues/907]
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
Validation errors are reported without stopping your command. Combine `--validate` with `--strict` to exit with code `1` before the command runs.
|
|
1474
|
+
|
|
1475
|
+
```sh
|
|
1476
|
+
$ dotenvx run --validate --strict -- node index.js
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
Any inline comment containing the word `optional` marks that key as optional. If `.env.example` is missing, dotenvx reports `MISSING_ENV_EXAMPLE`. An empty `.env.example` is valid and declares no required variables.
|
|
1480
|
+
|
|
1481
|
+
</details>
|
|
1482
|
+
<details><summary>`validate`</summary><br>
|
|
1483
|
+
|
|
1484
|
+
Validate `.env` file(s) against `.env.example` without running a command.
|
|
1485
|
+
|
|
1486
|
+
```ini
|
|
1487
|
+
# .env.example
|
|
1488
|
+
DATABASE_URL=
|
|
1489
|
+
API_KEY=
|
|
1490
|
+
SENTRY_DSN= # optional
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1493
|
+
```sh
|
|
1494
|
+
$ dotenvx validate
|
|
1495
|
+
[VALIDATION_FAILED] missing required (DATABASE_URL, API_KEY). fix: [https://github.com/dotenvx/dotenvx/issues/907]
|
|
1496
|
+
```
|
|
1497
|
+
|
|
1498
|
+
Use `-f` and `-fk` to validate a specific env file and keys file. The command exits with code `1` when validation fails and prints errors to stderr. On success, it exits with code `0`.
|
|
1499
|
+
|
|
1500
|
+
```sh
|
|
1501
|
+
$ dotenvx validate -f .env.production -fk .env.keys
|
|
1502
|
+
```
|
|
1503
|
+
|
|
1456
1504
|
</details>
|
|
1457
1505
|
<details><summary>`run --strict`</summary><br>
|
|
1458
1506
|
|
|
@@ -2825,9 +2873,10 @@ Commands:
|
|
|
2825
2873
|
decrypt decrypt .env file(s)
|
|
2826
2874
|
keypair [KEY] print public/private keys for .env file(s)
|
|
2827
2875
|
ls [directory] print all .env files in a tree structure
|
|
2876
|
+
gitignore append to .gitignore
|
|
2828
2877
|
genexample [directory]
|
|
2829
2878
|
generate .env.example
|
|
2830
|
-
|
|
2879
|
+
validate validate .env file(s) against .env.example
|
|
2831
2880
|
precommit [directory]
|
|
2832
2881
|
prevent committing .env files to code
|
|
2833
2882
|
prebuild [directory]
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.14.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"funding": "https://dotenvx.com",
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@dotenvx/primitives": "^2.
|
|
46
|
+
"@dotenvx/primitives": "^2.1.0",
|
|
47
47
|
"@dotenvx/tooling": "^1.0.2",
|
|
48
48
|
"yocto-spinner": "^1.2.1"
|
|
49
49
|
},
|
package/src/cli/actions/get.js
CHANGED
|
@@ -76,10 +76,11 @@ async function get (key) {
|
|
|
76
76
|
console.log(single)
|
|
77
77
|
}
|
|
78
78
|
} else {
|
|
79
|
-
if (options.format === 'eval') {
|
|
79
|
+
if (options.format === 'eval' || options.format === 'eval-export') {
|
|
80
|
+
const prefix = options.format === 'eval-export' ? 'export ' : ''
|
|
80
81
|
let inline = ''
|
|
81
82
|
for (const [key, value] of Object.entries(parsed)) {
|
|
82
|
-
inline += `${key}=${escape(value)}\n`
|
|
83
|
+
inline += `${prefix}${key}=${escape(value)}\n`
|
|
83
84
|
}
|
|
84
85
|
inline = inline.trim()
|
|
85
86
|
|
package/src/cli/actions/run.js
CHANGED
|
@@ -15,6 +15,7 @@ const maskEnvSrc = require('../../lib/helpers/maskEnvSrc')
|
|
|
15
15
|
const maskProcessedEnvs = require('../../lib/helpers/maskProcessedEnvs')
|
|
16
16
|
const redactedValues = require('../../lib/helpers/redactedValues')
|
|
17
17
|
const { redactOutput } = require('../../lib/helpers/redactOutput')
|
|
18
|
+
const validateEnvExample = require('../../lib/helpers/validateEnvExample')
|
|
18
19
|
|
|
19
20
|
const { determine } = require('./../../lib/helpers/envResolution')
|
|
20
21
|
|
|
@@ -135,6 +136,20 @@ async function run () {
|
|
|
135
136
|
maskProcessedEnvs(processedEnvs, commandEnv, showChar)
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
if (options.validate) {
|
|
140
|
+
const error = validateEnvExample(process.env)
|
|
141
|
+
|
|
142
|
+
if (error) {
|
|
143
|
+
if (ignore.includes(error.code)) {
|
|
144
|
+
logger.verbose(`ignored: ${error.message}`)
|
|
145
|
+
} else if (options.strict) {
|
|
146
|
+
throw error
|
|
147
|
+
} else {
|
|
148
|
+
logger.error(error.messageWithHelp || error.message)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
138
153
|
for (const processedEnv of processedEnvs) {
|
|
139
154
|
if (processedEnv.type === 'envFile') {
|
|
140
155
|
logger.verbose(`loading env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const { logger } = require('./../../shared/logger')
|
|
2
|
+
|
|
3
|
+
const envsResolver = require('./../../lib/resolvers/envs')
|
|
4
|
+
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
5
|
+
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
6
|
+
const Session = require('../../db/session')
|
|
7
|
+
const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
|
|
8
|
+
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
9
|
+
const buildCommandEnvs = require('../../lib/helpers/buildCommandEnvs')
|
|
10
|
+
const resolveEnvKeysFile = require('../../lib/helpers/resolveEnvKeysFile')
|
|
11
|
+
const validateEnvExample = require('../../lib/helpers/validateEnvExample')
|
|
12
|
+
|
|
13
|
+
const { determine } = require('./../../lib/helpers/envResolution')
|
|
14
|
+
|
|
15
|
+
async function validate () {
|
|
16
|
+
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
|
|
17
|
+
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
18
|
+
const spinner = await createSpinner({ ...spinnerOptions, ...options, text: 'validating' })
|
|
19
|
+
const ignore = options.ignore || []
|
|
20
|
+
const validateEnv = { ...process.env }
|
|
21
|
+
let errorCount = 0
|
|
22
|
+
|
|
23
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
let envs = buildCommandEnvs(this.envs, options.convention)
|
|
27
|
+
envs = determine(envs, process.env)
|
|
28
|
+
|
|
29
|
+
const sesh = new Session()
|
|
30
|
+
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
31
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
32
|
+
|
|
33
|
+
const { processedEnvs } = await envsResolver({
|
|
34
|
+
envs,
|
|
35
|
+
overload: options.overload,
|
|
36
|
+
processEnv: validateEnv,
|
|
37
|
+
envKeysFile: resolveEnvKeysFile(options.envKeysFile),
|
|
38
|
+
noArmor,
|
|
39
|
+
noKeychain,
|
|
40
|
+
token: options.token,
|
|
41
|
+
onStatus: (text) => {
|
|
42
|
+
if (spinner && text) {
|
|
43
|
+
spinner.text = text
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
for (const processedEnv of processedEnvs) {
|
|
49
|
+
for (const error of processedEnv.errors || []) {
|
|
50
|
+
if (ignore.includes(error.code)) {
|
|
51
|
+
logger.verbose(`ignored: ${error.message}`)
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
errorCount += 1
|
|
56
|
+
logger.error(error.messageWithHelp || error.message)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const validationError = validateEnvExample(validateEnv)
|
|
61
|
+
if (validationError) {
|
|
62
|
+
if (ignore.includes(validationError.code)) {
|
|
63
|
+
logger.verbose(`ignored: ${validationError.message}`)
|
|
64
|
+
} else {
|
|
65
|
+
errorCount += 1
|
|
66
|
+
logger.error(validationError.messageWithHelp || validationError.message)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (spinner) spinner.stop()
|
|
71
|
+
|
|
72
|
+
if (errorCount > 0) {
|
|
73
|
+
process.exit(1)
|
|
74
|
+
} else {
|
|
75
|
+
logger.success('▣ validated')
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (spinner) spinner.stop()
|
|
79
|
+
catchAndLog(error)
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = validate
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -66,6 +66,7 @@ program.command('run')
|
|
|
66
66
|
.option('-fk, --env-keys-file <path>', 'path(s) to your .env.keys file(s) (default: same path as your env file)', collectEnvKeys)
|
|
67
67
|
.option('--redact', 'redact injected values except keys ending in _PLAIN', false)
|
|
68
68
|
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
69
|
+
.option('--validate', 'validate against .env.example', false)
|
|
69
70
|
.option('--strict', 'process.exit(1) on any errors', false)
|
|
70
71
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
|
|
71
72
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
@@ -94,7 +95,7 @@ program.command('get')
|
|
|
94
95
|
.option('--mask [characters]', 'mask values, optionally setting visible characters')
|
|
95
96
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
96
97
|
.option('--pp', 'pretty print output (alias)')
|
|
97
|
-
.option('--format <type>', 'format of the output (json, shell, colon, eval)', 'json')
|
|
98
|
+
.option('--format <type>', 'format of the output (json, shell, colon, eval, eval-export)', 'json')
|
|
98
99
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
99
100
|
.option('--no-native', 'disable OS secret store features')
|
|
100
101
|
.action(function (...args) {
|
|
@@ -182,6 +183,15 @@ program.command('ls')
|
|
|
182
183
|
return require('./actions/ls').apply(this, args)
|
|
183
184
|
})
|
|
184
185
|
|
|
186
|
+
// dotenvx gitignore
|
|
187
|
+
program.command('gitignore')
|
|
188
|
+
.description('append to .gitignore')
|
|
189
|
+
.addHelpText('after', examples.gitignore)
|
|
190
|
+
.option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
|
|
191
|
+
.action(function (...args) {
|
|
192
|
+
return require('./actions/ext/gitignore').apply(this, args)
|
|
193
|
+
})
|
|
194
|
+
|
|
185
195
|
// dotenvx genexample
|
|
186
196
|
program.command('genexample')
|
|
187
197
|
.description('generate .env.example')
|
|
@@ -191,13 +201,21 @@ program.command('genexample')
|
|
|
191
201
|
return require('./actions/ext/genexample').apply(this, args)
|
|
192
202
|
})
|
|
193
203
|
|
|
194
|
-
// dotenvx
|
|
195
|
-
program.command('
|
|
196
|
-
.description('
|
|
197
|
-
.
|
|
198
|
-
.option('--
|
|
204
|
+
// dotenvx validate
|
|
205
|
+
program.command('validate')
|
|
206
|
+
.description('validate .env file(s) against .env.example')
|
|
207
|
+
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
208
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
209
|
+
.option('-fk, --env-keys-file <path>', 'path(s) to your .env.keys file(s) (default: same path as your env file)', collectEnvKeys)
|
|
210
|
+
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
211
|
+
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
|
|
212
|
+
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
213
|
+
.option('--token <token>', 'set Armor ⛨ token')
|
|
214
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
215
|
+
.option('--no-native', 'disable OS secret store features')
|
|
199
216
|
.action(function (...args) {
|
|
200
|
-
|
|
217
|
+
this.envs = envs
|
|
218
|
+
return require('./actions/validate').apply(this, args)
|
|
201
219
|
})
|
|
202
220
|
|
|
203
221
|
// dotenvx precommit
|
|
@@ -12,6 +12,7 @@ const ISSUE_BY_CODE = {
|
|
|
12
12
|
MALFORMED_ENCRYPTED_DATA: 'https://github.com/dotenvx/dotenvx/issues/467',
|
|
13
13
|
MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
|
|
14
14
|
MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
|
|
15
|
+
MISSING_ENV_EXAMPLE: 'https://github.com/dotenvx/dotenvx/issues/905',
|
|
15
16
|
MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
|
|
16
17
|
MISSING_ENV_KEYS_FILE: 'https://github.com/dotenvx/dotenvx/issues/775',
|
|
17
18
|
MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
|
|
@@ -22,6 +23,7 @@ const ISSUE_BY_CODE = {
|
|
|
22
23
|
MISSING_VALUE: 'https://github.com/dotenvx/dotenvx/issues/864',
|
|
23
24
|
FILE_NOT_WRITABLE: 'https://github.com/dotenvx/dotenvx/issues/890',
|
|
24
25
|
PRECOMMIT_HOOK_MODIFY_FAILED: 'try again or report error',
|
|
26
|
+
VALIDATION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/907',
|
|
25
27
|
WRONG_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/466'
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -207,6 +209,18 @@ class Errors {
|
|
|
207
209
|
return e
|
|
208
210
|
}
|
|
209
211
|
|
|
212
|
+
missingEnvExample () {
|
|
213
|
+
const code = 'MISSING_ENV_EXAMPLE'
|
|
214
|
+
const message = `[${code}] missing .env.example file`
|
|
215
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
216
|
+
|
|
217
|
+
const e = new Error(message)
|
|
218
|
+
e.code = code
|
|
219
|
+
e.help = help
|
|
220
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
221
|
+
return e
|
|
222
|
+
}
|
|
223
|
+
|
|
210
224
|
missingEnvKeysFile () {
|
|
211
225
|
const code = 'MISSING_ENV_KEYS_FILE'
|
|
212
226
|
const envKeysFilepath = this.envKeysFilepath || '.env.keys'
|
|
@@ -305,6 +319,18 @@ class Errors {
|
|
|
305
319
|
return e
|
|
306
320
|
}
|
|
307
321
|
|
|
322
|
+
validationFailed () {
|
|
323
|
+
const code = 'VALIDATION_FAILED'
|
|
324
|
+
const message = `[${code}] ${this.message}`
|
|
325
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
326
|
+
|
|
327
|
+
const e = new Error(message)
|
|
328
|
+
e.code = code
|
|
329
|
+
e.help = help
|
|
330
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
331
|
+
return e
|
|
332
|
+
}
|
|
333
|
+
|
|
308
334
|
precommitHookModifyFailed () {
|
|
309
335
|
const code = 'PRECOMMIT_HOOK_MODIFY_FAILED'
|
|
310
336
|
const message = `[${code}] failed to modify pre-commit hook: ${this.error.message}`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function optionalKeys (comments) {
|
|
2
|
+
const keys = new Set()
|
|
3
|
+
|
|
4
|
+
for (const [key, values] of Object.entries(comments)) {
|
|
5
|
+
if (values.some(comment => comment && /\boptional\b/i.test(comment))) {
|
|
6
|
+
keys.add(key)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return keys
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function validate (example = {}, env = {}, options = {}) {
|
|
14
|
+
const errors = []
|
|
15
|
+
const missingRequired = []
|
|
16
|
+
const optional = optionalKeys(options.comments || {})
|
|
17
|
+
|
|
18
|
+
for (const key of Object.keys(example)) {
|
|
19
|
+
const value = env[key]
|
|
20
|
+
const missing = !Object.prototype.hasOwnProperty.call(env, key) || value.trim() === ''
|
|
21
|
+
if (!optional.has(key) && missing) {
|
|
22
|
+
missingRequired.push(key)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (missingRequired.length > 0) {
|
|
27
|
+
errors.push({
|
|
28
|
+
code: 'MISSING_REQUIRED',
|
|
29
|
+
keys: missingRequired,
|
|
30
|
+
message: `missing required (${missingRequired.join(', ')})`
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
valid: errors.length === 0,
|
|
36
|
+
errors
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = validate
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const { scan } = require('@dotenvx/primitives')
|
|
3
|
+
|
|
4
|
+
const Errors = require('./errors')
|
|
5
|
+
const validate = require('./validate')
|
|
6
|
+
|
|
7
|
+
function validateEnvExample (env = process.env, options = {}) {
|
|
8
|
+
const filepath = options.filepath || '.env.example'
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(filepath)) {
|
|
11
|
+
return new Errors().missingEnvExample()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const exampleSrc = fs.readFileSync(filepath, 'utf8')
|
|
15
|
+
const { parsed: example, comments } = scan(exampleSrc)
|
|
16
|
+
const validation = validate(example, env, { comments })
|
|
17
|
+
|
|
18
|
+
if (!validation.valid) {
|
|
19
|
+
const message = validation.errors.map(error => error.message).join('; ')
|
|
20
|
+
return new Errors({ message }).validationFailed()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = validateEnvExample
|
package/src/lib/main.js
CHANGED
|
@@ -20,6 +20,7 @@ const setTransform = require('./transforms/set')
|
|
|
20
20
|
const buildEnvs = require('./helpers/buildEnvs')
|
|
21
21
|
const buildConfigEnvs = require('./helpers/buildConfigEnvs')
|
|
22
22
|
const { determine } = require('./helpers/envResolution')
|
|
23
|
+
const escape = require('./helpers/escape')
|
|
23
24
|
const fsx = require('./helpers/fsx')
|
|
24
25
|
const decryptKeyValue = require('./helpers/cryptography/decryptKeyValue')
|
|
25
26
|
const Errors = require('./helpers/errors')
|
|
@@ -358,10 +359,11 @@ const get = async function (key, options = {}) {
|
|
|
358
359
|
return single
|
|
359
360
|
}
|
|
360
361
|
} else {
|
|
361
|
-
if (options.format === 'eval') {
|
|
362
|
+
if (options.format === 'eval' || options.format === 'eval-export') {
|
|
363
|
+
const prefix = options.format === 'eval-export' ? 'export ' : ''
|
|
362
364
|
let inline = ''
|
|
363
365
|
for (const [key, value] of Object.entries(parsed)) {
|
|
364
|
-
inline += `${key}=${escape(value)}\n`
|
|
366
|
+
inline += `${prefix}${key}=${escape(value)}\n`
|
|
365
367
|
}
|
|
366
368
|
inline = inline.trim()
|
|
367
369
|
|