@dotenvx/dotenvx 2.19.2 → 2.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/CHANGELOG.md +13 -3
- package/README.md +24 -0
- package/package.json +3 -3
- package/src/cli/actions/del.js +56 -0
- package/src/cli/dotenvx.js +13 -1
- package/src/cli/help.js +12 -0
- package/src/lib/services/precommit.js +5 -2
- package/src/lib/transforms/del.js +58 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,23 @@
|
|
|
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.20.1...main)
|
|
6
6
|
|
|
7
|
-
## [2.
|
|
7
|
+
## [2.20.1](https://github.com/dotenvx/dotenvx/compare/v2.20.0...v2.20.1) (2026-08-07)
|
|
8
8
|
|
|
9
9
|
### Changed
|
|
10
10
|
|
|
11
|
-
*
|
|
11
|
+
* Handle accent marks for `ext precommit` ([#936](https://github.com/dotenvx/dotenvx/pull/936))
|
|
12
|
+
|
|
13
|
+
## [2.20.0](https://github.com/dotenvx/dotenvx/compare/v2.19.1...v2.20.0) (2026-08-07)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* Add `dotenvx del` command for deleting a key from a .env file ([#939](https://github.com/dotenvx/dotenvx/pull/939))
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
* Exempt `.env.vault` from `precommit`/`prebuild` sealed checks (vault-format ciphertext is already safe to commit)
|
|
12
22
|
|
|
13
23
|
## [2.19.1](https://github.com/dotenvx/dotenvx/compare/v2.19.0...v2.19.1) (2026-07-29)
|
|
14
24
|
|
package/README.md
CHANGED
|
@@ -2211,6 +2211,30 @@ Do not create a missing `.env` file.
|
|
|
2211
2211
|
$ dotenvx set HELLO World -f .env.production --no-create
|
|
2212
2212
|
```
|
|
2213
2213
|
|
|
2214
|
+
</details>
|
|
2215
|
+
<details><summary>`del KEY`</summary><br>
|
|
2216
|
+
|
|
2217
|
+
Delete a key from your `.env` file.
|
|
2218
|
+
|
|
2219
|
+
```sh
|
|
2220
|
+
$ echo "HELLO=World" > .env
|
|
2221
|
+
|
|
2222
|
+
$ dotenvx del HELLO
|
|
2223
|
+
◇ removed HELLO (.env)
|
|
2224
|
+
```
|
|
2225
|
+
|
|
2226
|
+
</details>
|
|
2227
|
+
<details><summary>`del KEY -f`</summary><br>
|
|
2228
|
+
|
|
2229
|
+
Delete a key from another `.env` file.
|
|
2230
|
+
|
|
2231
|
+
```sh
|
|
2232
|
+
$ echo "HELLO=production" > .env.production
|
|
2233
|
+
|
|
2234
|
+
$ dotenvx del HELLO -f .env.production
|
|
2235
|
+
◇ removed HELLO (.env.production)
|
|
2236
|
+
```
|
|
2237
|
+
|
|
2214
2238
|
</details>
|
|
2215
2239
|
<details><summary>`encrypt`</summary><br>
|
|
2216
2240
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.20.1",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"funding": "https://dotenvx.com",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@dotenvx/primitives": "^2.
|
|
55
|
+
"@dotenvx/primitives": "^2.2.0",
|
|
56
56
|
"@dotenvx/tooling": "^1.0.3",
|
|
57
57
|
"yocto-spinner": "^1.2.1"
|
|
58
58
|
},
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"tap": "^21.7.4"
|
|
68
68
|
},
|
|
69
69
|
"overrides": {
|
|
70
|
-
"brace-expansion": "^5.0.
|
|
70
|
+
"brace-expansion": "^5.0.9",
|
|
71
71
|
"minimatch": "^10.2.5"
|
|
72
72
|
},
|
|
73
73
|
"publishConfig": {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const fsx = require('./../../lib/helpers/fsx')
|
|
2
|
+
const { logger } = require('./../../shared/logger')
|
|
3
|
+
|
|
4
|
+
const delTransform = require('./../../lib/transforms/del')
|
|
5
|
+
|
|
6
|
+
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
7
|
+
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
|
+
|
|
9
|
+
async function del (key) {
|
|
10
|
+
const options = this.opts()
|
|
11
|
+
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
12
|
+
const spinner = await createSpinner({ ...spinnerOptions, ...options, text: 'deleting' })
|
|
13
|
+
|
|
14
|
+
logger.debug(`key: ${key}`)
|
|
15
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
16
|
+
|
|
17
|
+
const envs = this.envs || []
|
|
18
|
+
|
|
19
|
+
let errorCount = 0
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const { processedEnvs, changedFilepaths, unchangedFilepaths } = await delTransform({ envs, key })
|
|
23
|
+
|
|
24
|
+
if (spinner) spinner.stop()
|
|
25
|
+
for (const processedEnv of processedEnvs) {
|
|
26
|
+
logger.verbose(`deleting for ${processedEnv.envFilepath}`)
|
|
27
|
+
if (processedEnv.error) {
|
|
28
|
+
errorCount += 1
|
|
29
|
+
logger.error(processedEnv.error.messageWithHelp || processedEnv.error.message)
|
|
30
|
+
} else if (processedEnv.changed) {
|
|
31
|
+
await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
32
|
+
logger.verbose(`${processedEnv.key} deleted (${processedEnv.envFilepath})`)
|
|
33
|
+
} else {
|
|
34
|
+
logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (changedFilepaths.length > 0) {
|
|
39
|
+
logger.success(`◇ removed ${key} (${changedFilepaths.join(',')})`)
|
|
40
|
+
} else if (unchangedFilepaths.length > 0) {
|
|
41
|
+
logger.info(`○ no change (${unchangedFilepaths})`)
|
|
42
|
+
} else {
|
|
43
|
+
// do nothing - scenario when no .env files found
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (errorCount > 0) {
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (spinner) spinner.stop()
|
|
51
|
+
catchAndLog(error)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = del
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -129,6 +129,18 @@ program.command('set')
|
|
|
129
129
|
return require('./actions/set').apply(this, args)
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
+
// dotenvx del
|
|
133
|
+
program.command('del')
|
|
134
|
+
.usage('<KEY> [options]')
|
|
135
|
+
.description('delete a single environment variable')
|
|
136
|
+
.addHelpText('after', help.del)
|
|
137
|
+
.argument('KEY', 'KEY')
|
|
138
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
139
|
+
.action(function (...args) {
|
|
140
|
+
this.envs = envs
|
|
141
|
+
return require('./actions/del').apply(this, args)
|
|
142
|
+
})
|
|
143
|
+
|
|
132
144
|
// dotenvx encrypt
|
|
133
145
|
program.command('encrypt')
|
|
134
146
|
.description('encrypt .env file(s)')
|
|
@@ -317,7 +329,7 @@ program.addHelpText('after', 'Professional Security: ')
|
|
|
317
329
|
program.addHelpText('after', ' lock ⊡ lock private keys with a local passphrase')
|
|
318
330
|
program.addHelpText('after', ' native ⌥ move private keys into your OS secret store')
|
|
319
331
|
program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
|
|
320
|
-
program.addHelpText('after', ' curl ⛨ call authenticated api
|
|
332
|
+
program.addHelpText('after', ' curl ⛨ call authenticated api Dotenvx Armor [www.dotenvx.com/armor]')
|
|
321
333
|
|
|
322
334
|
// dotenvx native
|
|
323
335
|
require('./commands/native')(program.command('native', { hidden: true }))
|
package/src/cli/help.js
CHANGED
|
@@ -90,6 +90,17 @@ Examples:
|
|
|
90
90
|
`
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
const del = function () {
|
|
94
|
+
return `
|
|
95
|
+
Examples:
|
|
96
|
+
|
|
97
|
+
\`\`\`
|
|
98
|
+
$ dotenvx del KEY
|
|
99
|
+
$ dotenvx del KEY -f .env.production
|
|
100
|
+
\`\`\`
|
|
101
|
+
`
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
const curl = function () {
|
|
94
105
|
return `
|
|
95
106
|
Endpoints:
|
|
@@ -130,5 +141,6 @@ module.exports = {
|
|
|
130
141
|
prebuild,
|
|
131
142
|
gitignore,
|
|
132
143
|
set,
|
|
144
|
+
del,
|
|
133
145
|
curl
|
|
134
146
|
}
|
|
@@ -123,8 +123,11 @@ class Precommit {
|
|
|
123
123
|
return true
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
// -z separates paths with NUL and prints them verbatim. without it git c-quotes
|
|
127
|
+
// any path holding non-ascii or special characters ("caf\303\251/.env"), which
|
|
128
|
+
// never matches filePath - the file would be skipped and its secrets committed.
|
|
129
|
+
const output = childProcess.execSync('git diff HEAD --name-only -z').toString()
|
|
130
|
+
const files = output.split('\0')
|
|
128
131
|
return files.includes(filePath)
|
|
129
132
|
} catch (error) {
|
|
130
133
|
// consider file to be committed if there is an error (not using git)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const fsx = require('./../helpers/fsx')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { remove } = require('@dotenvx/primitives')
|
|
4
|
+
|
|
5
|
+
const TYPE_ENV_FILE = 'envFile'
|
|
6
|
+
|
|
7
|
+
const Errors = require('./../helpers/errors')
|
|
8
|
+
const { determine } = require('./../helpers/envResolution')
|
|
9
|
+
const detectEncoding = require('./../helpers/detectEncoding')
|
|
10
|
+
|
|
11
|
+
async function delTransform (options = {}) {
|
|
12
|
+
const envs = options.envs || []
|
|
13
|
+
const key = options.key
|
|
14
|
+
|
|
15
|
+
const processedEnvs = []
|
|
16
|
+
const changedFilepaths = []
|
|
17
|
+
const unchangedFilepaths = []
|
|
18
|
+
|
|
19
|
+
for (const env of determine(envs, process.env)) {
|
|
20
|
+
if (env.type !== TYPE_ENV_FILE) {
|
|
21
|
+
continue
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const envFilepath = env.envFilepath || env.value
|
|
25
|
+
const filepath = env.filepath || path.resolve(envFilepath)
|
|
26
|
+
const row = { key, type: TYPE_ENV_FILE, filepath, envFilepath, changed: false }
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const encoding = await detectEncoding(filepath)
|
|
30
|
+
const before = await fsx.readFileX(filepath, { encoding })
|
|
31
|
+
const after = remove(before, key)
|
|
32
|
+
|
|
33
|
+
row.envSrc = after
|
|
34
|
+
if (after !== before) {
|
|
35
|
+
row.changed = true
|
|
36
|
+
changedFilepaths.push(envFilepath)
|
|
37
|
+
} else {
|
|
38
|
+
unchangedFilepaths.push(envFilepath)
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error.code === 'ENOENT') {
|
|
42
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
43
|
+
} else {
|
|
44
|
+
row.error = error
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
processedEnvs.push(row)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
processedEnvs,
|
|
53
|
+
changedFilepaths,
|
|
54
|
+
unchangedFilepaths
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = delTransform
|