@dotenvx/dotenvx 2.17.1 → 2.17.3
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 +13 -1
- package/README.md +52 -22
- package/package.json +1 -1
- package/src/cli/actions/get.js +2 -1
- package/src/cli/actions/run.js +2 -1
- package/src/cli/actions/validate.js +2 -1
- package/src/lib/helpers/normalizeDotenvConfigConvention.js +3 -1
- package/src/lib/helpers/normalizeDotenvConfigIgnore.js +17 -0
- package/src/lib/helpers/normalizeDotenvConfigQuiet.js +3 -1
- package/src/lib/main.js +3 -0
- package/src/lib/resolvers/envs.js +12 -8
- package/src/lib/services/precommit.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
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.17.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.17.3...main)
|
|
6
|
+
|
|
7
|
+
## [2.17.3](https://github.com/dotenvx/dotenvx/compare/v2.17.2...v2.17.3) (2026-07-23)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Exempt `.env.example` in subdirectories from precommit ([#920](https://github.com/dotenvx/dotenvx/pull/920))
|
|
12
|
+
|
|
13
|
+
## [2.17.2](https://github.com/dotenvx/dotenvx/compare/v2.17.1...v2.17.2) (2026-07-22)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* Add support for `DOTENV_CONFIG_IGNORE` global convenience ([#919](https://github.com/dotenvx/dotenvx/pull/919))
|
|
6
18
|
|
|
7
19
|
## [2.17.1](https://github.com/dotenvx/dotenvx/compare/v2.17.0...v2.17.1) (2026-07-21)
|
|
8
20
|
|
package/README.md
CHANGED
|
@@ -955,24 +955,6 @@ $ dotenvx encrypt --stdout > .env.encrypted
|
|
|
955
955
|
> If your organization's compliance department requires [NIST approved curves](https://csrc.nist.gov/projects/elliptic-curve-cryptography) or other curves like `curve25519`, please reach out at [security@dotenvx.com](mailto:security@dotenvx.com).
|
|
956
956
|
|
|
957
957
|
</details>
|
|
958
|
-
<details><summary>agents</summary><br>
|
|
959
|
-
|
|
960
|
-
After encryption, `DOTENV_PUBLIC_KEY` lives in your encrypted `.env` file. This means agents and automation can keep running `dotenvx set` and `dotenvx encrypt` without reading `.env.keys`.
|
|
961
|
-
|
|
962
|
-
```sh
|
|
963
|
-
$ chmod a-r .env.keys
|
|
964
|
-
|
|
965
|
-
$ dotenvx set HELLO World
|
|
966
|
-
◈ encrypted HELLO (.env)
|
|
967
|
-
|
|
968
|
-
$ dotenvx encrypt
|
|
969
|
-
◈ encrypted (.env)
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
Keep `.env.keys` unreadable by agents, while still letting them safely update encrypted values.
|
|
973
|
-
|
|
974
|
-
</details>
|
|
975
|
-
|
|
976
958
|
|
|
977
959
|
|
|
978
960
|
## Advanced
|
|
@@ -1586,6 +1568,11 @@ $ dotenvx run -f .env.missing --ignore=MISSING_ENV_FILE -- node index.js
|
|
|
1586
1568
|
...
|
|
1587
1569
|
```
|
|
1588
1570
|
|
|
1571
|
+
You can also set `DOTENV_CONFIG_IGNORE="MISSING_ENV_FILE"`. It is parsed as a comma-separated list.
|
|
1572
|
+
|
|
1573
|
+
```sh
|
|
1574
|
+
$ DOTENV_CONFIG_IGNORE="MISSING_ENV_FILE,OTHER_ERROR_CODE" dotenvx run -f .env.missing -- node index.js
|
|
1575
|
+
```
|
|
1589
1576
|
</details>
|
|
1590
1577
|
<details><summary>`run --convention=nextjs`</summary><br>
|
|
1591
1578
|
|
|
@@ -2016,8 +2003,6 @@ $ dotenvx set HELLO World
|
|
|
2016
2003
|
set HELLO with encryption (.env)
|
|
2017
2004
|
```
|
|
2018
2005
|
|
|
2019
|
-
Works with unreadable `.env.keys` when `.env` already contains `DOTENV_PUBLIC_KEY`.
|
|
2020
|
-
|
|
2021
2006
|
</details>
|
|
2022
2007
|
<details><summary>`set KEY value -f`</summary><br>
|
|
2023
2008
|
|
|
@@ -2129,8 +2114,6 @@ $ dotenvx encrypt
|
|
|
2129
2114
|
⮕ next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
|
|
2130
2115
|
```
|
|
2131
2116
|
|
|
2132
|
-
Works with unreadable `.env.keys` when `.env` already contains `DOTENV_PUBLIC_KEY`.
|
|
2133
|
-
|
|
2134
2117
|
</details>
|
|
2135
2118
|
<details><summary>`encrypt -f`</summary><br>
|
|
2136
2119
|
|
|
@@ -2596,6 +2579,27 @@ Use `-f` and `-fk` to validate a specific env file and keys file. The command ex
|
|
|
2596
2579
|
$ dotenvx validate -f .env.production -fk .env.keys
|
|
2597
2580
|
```
|
|
2598
2581
|
|
|
2582
|
+
</details>
|
|
2583
|
+
<details><summary>`validate --ignore`</summary><br>
|
|
2584
|
+
|
|
2585
|
+
Ignore specific validation error codes.
|
|
2586
|
+
|
|
2587
|
+
```sh
|
|
2588
|
+
$ dotenvx validate --ignore=MISSING_ENV_EXAMPLE
|
|
2589
|
+
```
|
|
2590
|
+
|
|
2591
|
+
Ignore multiple error codes by separating them with spaces.
|
|
2592
|
+
|
|
2593
|
+
```sh
|
|
2594
|
+
$ dotenvx validate --ignore=MISSING_ENV_FILE MISSING_ENV_EXAMPLE
|
|
2595
|
+
```
|
|
2596
|
+
|
|
2597
|
+
You can also set `DOTENV_CONFIG_IGNORE`. Its value is a comma-separated list.
|
|
2598
|
+
|
|
2599
|
+
```sh
|
|
2600
|
+
$ DOTENV_CONFIG_IGNORE=MISSING_ENV_FILE,OTHER dotenvx validate
|
|
2601
|
+
```
|
|
2602
|
+
|
|
2599
2603
|
</details>
|
|
2600
2604
|
<details><summary>`genexample`</summary><br>
|
|
2601
2605
|
|
|
@@ -3039,6 +3043,8 @@ X.X.X
|
|
|
3039
3043
|
|
|
3040
3044
|
</details>
|
|
3041
3045
|
|
|
3046
|
+
|
|
3047
|
+
|
|
3042
3048
|
### Library 📦
|
|
3043
3049
|
|
|
3044
3050
|
Use dotenvx directly in code.
|
|
@@ -3491,6 +3497,29 @@ Set `mask: 0` to fully mask values.
|
|
|
3491
3497
|
|
|
3492
3498
|
|
|
3493
3499
|
|
|
3500
|
+
### Settings ⚙️
|
|
3501
|
+
|
|
3502
|
+
There are global settings available that can be configured as environment variables.
|
|
3503
|
+
|
|
3504
|
+
<details><summary>Environment variables</summary><br>
|
|
3505
|
+
|
|
3506
|
+
```ini
|
|
3507
|
+
# dotenvx native settings
|
|
3508
|
+
DOTENV_CONFIG_CONVENTION= # set to a default convention like 'nextjs' or 'flow'
|
|
3509
|
+
DOTENV_CONFIG_IGNORE= # MISSING_ENV_FILE,OTHER
|
|
3510
|
+
DOTENV_CONFIG_QUIET= # set to "true" to default to --quiet
|
|
3511
|
+
DOTENVX_NO_ARMOR= # set to "true" to turn off Armor support
|
|
3512
|
+
DOTENVX_NO_1PASSWORD= # set to "true" to turn off 1Password support
|
|
3513
|
+
DOTENVX_NO_BITWARDEN= # set to "true" to turn off Bitwarden support
|
|
3514
|
+
|
|
3515
|
+
# other machine settings respected by dotenvx
|
|
3516
|
+
BW_SESSION= # set to bitwarden session token to bypass password prompt
|
|
3517
|
+
```
|
|
3518
|
+
|
|
3519
|
+
</details>
|
|
3520
|
+
|
|
3521
|
+
|
|
3522
|
+
|
|
3494
3523
|
## Armor ⛨
|
|
3495
3524
|
|
|
3496
3525
|
[](https://dotenvx.com/armor)
|
|
@@ -3513,6 +3542,7 @@ Set `mask: 0` to fully mask values.
|
|
|
3513
3542
|
* [Claude](https://dotenvx.com/docs/secrets-in-claude)
|
|
3514
3543
|
* [Codex](https://dotenvx.com/docs/secrets-in-codex)
|
|
3515
3544
|
* [1Password](https://dotenvx.com/docs/secrets-in-1password)
|
|
3545
|
+
* [Bitwarden](https://dotenvx.com/docs/secrets-in-bitwarden)
|
|
3516
3546
|
* [Node.js](https://dotenvx.com/docs/secrets-in-nodejs)
|
|
3517
3547
|
* [Next.js](https://dotenvx.com/docs/secrets-in-nextjs)
|
|
3518
3548
|
* [Express](https://dotenvx.com/docs/secrets-in-express)
|
package/package.json
CHANGED
package/src/cli/actions/get.js
CHANGED
|
@@ -6,13 +6,14 @@ const createSpinner = require('../../lib/helpers/createSpinner')
|
|
|
6
6
|
const Session = require('../../db/session')
|
|
7
7
|
const getResolver = require('./../../lib/resolvers/get')
|
|
8
8
|
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
9
|
+
const normalizeDotenvConfigIgnore = require('../../lib/helpers/normalizeDotenvConfigIgnore')
|
|
9
10
|
const buildCommandEnvs = require('../../lib/helpers/buildCommandEnvs')
|
|
10
11
|
const resolveEnvKeysFile = require('../../lib/helpers/resolveEnvKeysFile')
|
|
11
12
|
const mask = require('../../lib/helpers/mask')
|
|
12
13
|
const filterKeys = require('../../lib/helpers/filterKeys')
|
|
13
14
|
|
|
14
15
|
async function get (key) {
|
|
15
|
-
const options = normalizeDotenvConfigConvention(this.opts())
|
|
16
|
+
const options = normalizeDotenvConfigIgnore(normalizeDotenvConfigConvention(this.opts()))
|
|
16
17
|
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
17
18
|
const spinner = await createSpinner({ ...spinnerOptions, ...options, text: 'decrypting' })
|
|
18
19
|
|
package/src/cli/actions/run.js
CHANGED
|
@@ -8,6 +8,7 @@ const createSpinner = require('../../lib/helpers/createSpinner')
|
|
|
8
8
|
const Session = require('../../db/session')
|
|
9
9
|
const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
|
|
10
10
|
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
11
|
+
const normalizeDotenvConfigIgnore = require('../../lib/helpers/normalizeDotenvConfigIgnore')
|
|
11
12
|
const buildCommandEnvs = require('../../lib/helpers/buildCommandEnvs')
|
|
12
13
|
const resolveEnvKeysFile = require('../../lib/helpers/resolveEnvKeysFile')
|
|
13
14
|
const mask = require('../../lib/helpers/mask')
|
|
@@ -53,7 +54,7 @@ function uniqueInjectedKeys (processedEnvs) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
async function run () {
|
|
56
|
-
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
|
|
57
|
+
const options = normalizeDotenvConfigIgnore(normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts())))
|
|
57
58
|
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
58
59
|
const maskEnabled = options.mask !== undefined
|
|
59
60
|
const redactEnabled = options.redact === true
|
|
@@ -6,6 +6,7 @@ const createSpinner = require('../../lib/helpers/createSpinner')
|
|
|
6
6
|
const Session = require('../../db/session')
|
|
7
7
|
const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
|
|
8
8
|
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
9
|
+
const normalizeDotenvConfigIgnore = require('../../lib/helpers/normalizeDotenvConfigIgnore')
|
|
9
10
|
const buildCommandEnvs = require('../../lib/helpers/buildCommandEnvs')
|
|
10
11
|
const resolveEnvKeysFile = require('../../lib/helpers/resolveEnvKeysFile')
|
|
11
12
|
const validateEnvExample = require('../../lib/helpers/validateEnvExample')
|
|
@@ -13,7 +14,7 @@ const validateEnvExample = require('../../lib/helpers/validateEnvExample')
|
|
|
13
14
|
const { determine } = require('./../../lib/helpers/envResolution')
|
|
14
15
|
|
|
15
16
|
async function validate () {
|
|
16
|
-
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
|
|
17
|
+
const options = normalizeDotenvConfigIgnore(normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts())))
|
|
17
18
|
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
18
19
|
const spinner = await createSpinner({ ...spinnerOptions, ...options, text: 'validating' })
|
|
19
20
|
const ignore = options.ignore || []
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
function normalizeDotenvConfigConvention (
|
|
1
|
+
function normalizeDotenvConfigConvention (
|
|
2
|
+
/** @type {import('../main').DotenvConfigOptions} */ options
|
|
3
|
+
) {
|
|
2
4
|
if (!options.convention && process.env.DOTENV_CONFIG_CONVENTION) {
|
|
3
5
|
options.convention = process.env.DOTENV_CONFIG_CONVENTION
|
|
4
6
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function normalizeDotenvConfigIgnore (
|
|
2
|
+
/** @type {import('../main').DotenvConfigOptions} */ options
|
|
3
|
+
) {
|
|
4
|
+
const configIgnore = (process.env.DOTENV_CONFIG_IGNORE || '')
|
|
5
|
+
.split(',')
|
|
6
|
+
.map(code => code.trim())
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
|
|
9
|
+
if (configIgnore.length < 1) return options
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
...options,
|
|
13
|
+
ignore: [...(options.ignore || []), ...configIgnore]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = normalizeDotenvConfigIgnore
|
package/src/lib/main.js
CHANGED
|
@@ -26,6 +26,7 @@ const decryptKeyValue = require('./helpers/cryptography/decryptKeyValue')
|
|
|
26
26
|
const Errors = require('./helpers/errors')
|
|
27
27
|
const normalizeDotenvConfigQuiet = require('./helpers/normalizeDotenvConfigQuiet')
|
|
28
28
|
const normalizeDotenvConfigConvention = require('./helpers/normalizeDotenvConfigConvention')
|
|
29
|
+
const normalizeDotenvConfigIgnore = require('./helpers/normalizeDotenvConfigIgnore')
|
|
29
30
|
const mask = require('./helpers/mask')
|
|
30
31
|
const maskProcessedEnvs = require('./helpers/maskProcessedEnvs')
|
|
31
32
|
|
|
@@ -43,6 +44,7 @@ function uniqueInjectedKeys (processedEnvs) {
|
|
|
43
44
|
const config = function (options = {}) {
|
|
44
45
|
options = normalizeDotenvConfigQuiet(options)
|
|
45
46
|
options = normalizeDotenvConfigConvention(options)
|
|
47
|
+
options = normalizeDotenvConfigIgnore(options)
|
|
46
48
|
|
|
47
49
|
// allow user to set processEnv to write to
|
|
48
50
|
let processEnv = process.env
|
|
@@ -318,6 +320,7 @@ const set = async function (key, value, options = {}) {
|
|
|
318
320
|
/* @type {import('./main').get} */
|
|
319
321
|
const get = async function (key, options = {}) {
|
|
320
322
|
options = normalizeDotenvConfigConvention(options)
|
|
323
|
+
options = normalizeDotenvConfigIgnore(options)
|
|
321
324
|
|
|
322
325
|
const envs = buildEnvs(options)
|
|
323
326
|
const noArmor = resolveNoArmor(options)
|
|
@@ -301,6 +301,8 @@ async function envs (options = {}) {
|
|
|
301
301
|
const readableFilepaths = new Set()
|
|
302
302
|
const processEnv = options.processEnv || process.env
|
|
303
303
|
const envKeysFilepath = options.envKeysFilepath || options.envKeysFile || null
|
|
304
|
+
const no1Password = options.no1Password === true || process.env.DOTENVX_NO_1PASSWORD === 'true'
|
|
305
|
+
const noBitwarden = options.noBitwarden === true || process.env.DOTENVX_NO_BITWARDEN === 'true'
|
|
304
306
|
const provider = await providers(options)
|
|
305
307
|
const decryptor = await decryptors(options)
|
|
306
308
|
for (const env of options.envs || []) {
|
|
@@ -313,8 +315,8 @@ async function envs (options = {}) {
|
|
|
313
315
|
provider,
|
|
314
316
|
decryptor,
|
|
315
317
|
readableFilepaths,
|
|
316
|
-
no1Password
|
|
317
|
-
noBitwarden
|
|
318
|
+
no1Password,
|
|
319
|
+
noBitwarden,
|
|
318
320
|
onStatus: options.onStatus
|
|
319
321
|
}))
|
|
320
322
|
} else if (env.type === TYPE_ENV) {
|
|
@@ -325,8 +327,8 @@ async function envs (options = {}) {
|
|
|
325
327
|
envKeysFilepath,
|
|
326
328
|
provider,
|
|
327
329
|
decryptor,
|
|
328
|
-
no1Password
|
|
329
|
-
noBitwarden
|
|
330
|
+
no1Password,
|
|
331
|
+
noBitwarden,
|
|
330
332
|
onStatus: options.onStatus
|
|
331
333
|
}))
|
|
332
334
|
}
|
|
@@ -343,6 +345,8 @@ function envsSync (options = {}) {
|
|
|
343
345
|
const readableFilepaths = new Set()
|
|
344
346
|
const processEnv = options.processEnv || process.env
|
|
345
347
|
const envKeysFilepath = options.envKeysFilepath || options.envKeysFile || null
|
|
348
|
+
const no1Password = options.no1Password === true || process.env.DOTENVX_NO_1PASSWORD === 'true'
|
|
349
|
+
const noBitwarden = options.noBitwarden === true || process.env.DOTENVX_NO_BITWARDEN === 'true'
|
|
346
350
|
const provider = providers.sync(options)
|
|
347
351
|
const decryptor = decryptors.sync(options)
|
|
348
352
|
|
|
@@ -356,8 +360,8 @@ function envsSync (options = {}) {
|
|
|
356
360
|
provider,
|
|
357
361
|
decryptor,
|
|
358
362
|
readableFilepaths,
|
|
359
|
-
no1Password
|
|
360
|
-
noBitwarden
|
|
363
|
+
no1Password,
|
|
364
|
+
noBitwarden
|
|
361
365
|
}))
|
|
362
366
|
} else if (env.type === TYPE_ENV) {
|
|
363
367
|
processedEnvs.push(injectEnvSync({
|
|
@@ -367,8 +371,8 @@ function envsSync (options = {}) {
|
|
|
367
371
|
envKeysFilepath,
|
|
368
372
|
provider,
|
|
369
373
|
decryptor,
|
|
370
|
-
no1Password
|
|
371
|
-
noBitwarden
|
|
374
|
+
no1Password,
|
|
375
|
+
noBitwarden
|
|
372
376
|
}))
|
|
373
377
|
}
|
|
374
378
|
}
|
|
@@ -54,12 +54,13 @@ class Precommit {
|
|
|
54
54
|
count += 1
|
|
55
55
|
|
|
56
56
|
const file = path.join(this.directory, _file) // to handle when directory argument passed
|
|
57
|
+
const filename = path.basename(file) // exemptions match on filename, so they apply at any depth
|
|
57
58
|
|
|
58
59
|
// check if file is going to be committed
|
|
59
60
|
if (this._isFileToBeCommitted(file)) {
|
|
60
61
|
// check if that file is being ignored
|
|
61
62
|
if (ig.ignores(file)) {
|
|
62
|
-
if (
|
|
63
|
+
if (filename === '.env.example' || filename === '.env.x') {
|
|
63
64
|
const warning = new Errors({
|
|
64
65
|
message: `${file} ignored (should not be)`,
|
|
65
66
|
help: `fix: [dotenvx gitignore --pattern !${file}]`
|
|
@@ -67,7 +68,7 @@ class Precommit {
|
|
|
67
68
|
warnings.push(warning)
|
|
68
69
|
}
|
|
69
70
|
} else {
|
|
70
|
-
if (
|
|
71
|
+
if (filename !== '.env.example' && filename !== '.env.x') {
|
|
71
72
|
const src = fsx.readFileXSync(file)
|
|
72
73
|
const encrypted = sealed(src)
|
|
73
74
|
|