@dotenvx/dotenvx 2.0.0 → 2.1.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 +22 -1
- package/package.json +1 -1
- package/src/cli/actions/get.js +6 -0
- package/src/cli/actions/run.js +2 -1
- package/src/cli/actions/set.js +36 -0
- package/src/cli/dotenvx.js +4 -3
- package/src/lib/helpers/cryptography/index.js +2 -1
- package/src/lib/helpers/cryptography/{isPublicKey.js → isDotenvPublicKey.js} +2 -2
- package/src/lib/helpers/cryptography/isPlainKey.js +7 -0
- package/src/lib/helpers/errors.js +14 -1
- package/src/lib/helpers/normalizeDotenvConfigQuiet.js +9 -0
- package/src/lib/helpers/prompts.js +46 -0
- package/src/lib/main.js +6 -1
- package/src/lib/transforms/encrypt.js +2 -3
- package/src/lib/transforms/set.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,28 @@
|
|
|
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.1.1...main)
|
|
6
|
+
|
|
7
|
+
## [2.1.1](https://github.com/dotenvx/dotenvx/compare/v2.1.0...v2.1.1) (2026-07-02)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Support `DOTENV_CONFIG_QUIET=true` as synonym for `--quiet` flag ([#866](https://github.com/dotenvx/dotenvx/pull/866))
|
|
12
|
+
|
|
13
|
+
## [2.1.0](https://github.com/dotenvx/dotenvx/compare/v2.0.0...v2.1.0) (2026-07-02)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* Add interactive input mode for `set` ([#862](https://github.com/dotenvx/dotenvx/pull/862))
|
|
18
|
+
* Support leaving `_PLAIN` suffixed keys unencrypted during `set` and `encrypt` ([#862](https://github.com/dotenvx/dotenvx/pull/862))
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
* Return exit code 1 from `get` when decryption errors are reported ([#862](https://github.com/dotenvx/dotenvx/pull/862))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
* Add Windows PowerShell coverage for npm shim encryption of `.env.local` files
|
|
6
27
|
|
|
7
28
|
## [2.0.0](https://github.com/dotenvx/dotenvx/compare/v1.76.0...v2.0.0) (2026-06-30)
|
|
8
29
|
|
package/package.json
CHANGED
package/src/cli/actions/get.js
CHANGED
|
@@ -19,6 +19,7 @@ async function get (key) {
|
|
|
19
19
|
|
|
20
20
|
const prettyPrint = options.prettyPrint || options.pp
|
|
21
21
|
const ignore = options.ignore || []
|
|
22
|
+
let errorCount = 0
|
|
22
23
|
|
|
23
24
|
let envs = []
|
|
24
25
|
// handle shorthand conventions - like --convention=nextjs
|
|
@@ -52,6 +53,7 @@ async function get (key) {
|
|
|
52
53
|
continue // ignore error
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
errorCount += 1
|
|
55
57
|
logger.error(error.messageWithHelp)
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -97,6 +99,10 @@ async function get (key) {
|
|
|
97
99
|
console.log(JSON.stringify(parsed, null, space))
|
|
98
100
|
}
|
|
99
101
|
}
|
|
102
|
+
|
|
103
|
+
if (errorCount > 0) {
|
|
104
|
+
process.exit(1)
|
|
105
|
+
}
|
|
100
106
|
} catch (error) {
|
|
101
107
|
if (spinner) spinner.stop()
|
|
102
108
|
catchAndLog(error)
|
package/src/cli/actions/run.js
CHANGED
|
@@ -7,6 +7,7 @@ const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
|
7
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
8
|
const Session = require('../../db/session')
|
|
9
9
|
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
|
+
const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
|
|
10
11
|
|
|
11
12
|
const conventions = require('./../../lib/helpers/conventions')
|
|
12
13
|
const { determine } = require('./../../lib/helpers/envResolution')
|
|
@@ -45,7 +46,7 @@ function uniqueInjectedKeys (processedEnvs) {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
async function run () {
|
|
48
|
-
const options = normalizeArmorAliases(this.opts())
|
|
49
|
+
const options = normalizeDotenvConfigQuiet(normalizeArmorAliases(this.opts()))
|
|
49
50
|
|
|
50
51
|
let commandArgs = this.args
|
|
51
52
|
if (commandArgs.length < 1) {
|
package/src/cli/actions/set.js
CHANGED
|
@@ -5,6 +5,8 @@ const setTransform = require('./../../lib/transforms/set')
|
|
|
5
5
|
|
|
6
6
|
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
7
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
|
+
const Errors = require('../../lib/helpers/errors')
|
|
9
|
+
const prompts = require('../../lib/helpers/prompts')
|
|
8
10
|
const Session = require('../../db/session')
|
|
9
11
|
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
12
|
|
|
@@ -13,9 +15,43 @@ async function set (key, value) {
|
|
|
13
15
|
|
|
14
16
|
let encrypt = true
|
|
15
17
|
let settingMessage = 'encrypting'
|
|
18
|
+
let settingSymbol = '◈'
|
|
16
19
|
if (options.plain) {
|
|
17
20
|
encrypt = false
|
|
18
21
|
settingMessage = 'setting'
|
|
22
|
+
settingSymbol = '◇'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof value === 'undefined') {
|
|
26
|
+
if (!process.stdin.isTTY) {
|
|
27
|
+
catchAndLog(new Errors({ key }).missingValue())
|
|
28
|
+
process.exit(1)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
value = await prompts.password({
|
|
34
|
+
message: key,
|
|
35
|
+
prefix: settingSymbol,
|
|
36
|
+
separator: '='
|
|
37
|
+
}, {
|
|
38
|
+
input: process.stdin,
|
|
39
|
+
output: process.stderr
|
|
40
|
+
})
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.code === 'PROMPT_CANCELLED') {
|
|
43
|
+
process.exit(130)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (value === '') {
|
|
52
|
+
catchAndLog(new Errors({ key }).missingValue())
|
|
53
|
+
process.exit(1)
|
|
54
|
+
return
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
const spinner = await createSpinner({ ...options, text: settingMessage })
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -12,6 +12,7 @@ const getCommanderVersion = require('./../lib/helpers/getCommanderVersion')
|
|
|
12
12
|
const executeDynamic = require('./../lib/helpers/executeDynamic')
|
|
13
13
|
const removeDynamicHelpSection = require('./../lib/helpers/removeDynamicHelpSection')
|
|
14
14
|
const removeOptionsHelpParts = require('./../lib/helpers/removeOptionsHelpParts')
|
|
15
|
+
const normalizeDotenvConfigQuiet = require('./../lib/helpers/normalizeDotenvConfigQuiet')
|
|
15
16
|
|
|
16
17
|
// for use with run
|
|
17
18
|
const envs = []
|
|
@@ -38,7 +39,7 @@ program
|
|
|
38
39
|
.option('-v, --verbose', 'sets log level to verbose')
|
|
39
40
|
.option('-d, --debug', 'sets log level to debug')
|
|
40
41
|
.hook('preAction', (thisCommand, actionCommand) => {
|
|
41
|
-
const options = thisCommand.opts()
|
|
42
|
+
const options = normalizeDotenvConfigQuiet(thisCommand.opts())
|
|
42
43
|
|
|
43
44
|
setLogLevel(options)
|
|
44
45
|
})
|
|
@@ -103,12 +104,12 @@ program.command('get')
|
|
|
103
104
|
|
|
104
105
|
// dotenvx set
|
|
105
106
|
program.command('set')
|
|
106
|
-
.usage('<KEY>
|
|
107
|
+
.usage('<KEY> [value] [options]')
|
|
107
108
|
.description('encrypt a single environment variable')
|
|
108
109
|
.addHelpText('after', examples.set)
|
|
109
110
|
.allowUnknownOption()
|
|
110
111
|
.argument('KEY', 'KEY')
|
|
111
|
-
.argument('value', 'value')
|
|
112
|
+
.argument('[value]', 'value')
|
|
112
113
|
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
113
114
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
114
115
|
.option('-c, --encrypt', 'encrypt value', true)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
decryptKeyValue: require('./decryptKeyValue'),
|
|
3
|
-
|
|
3
|
+
isDotenvPublicKey: require('./isDotenvPublicKey'),
|
|
4
|
+
isPlainKey: require('./isPlainKey'),
|
|
4
5
|
mutateKeysSrc: require('./mutateKeysSrc'),
|
|
5
6
|
mutateSrc: require('./mutateSrc')
|
|
6
7
|
}
|
|
@@ -18,7 +18,8 @@ const ISSUE_BY_CODE = {
|
|
|
18
18
|
MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
|
|
19
19
|
MISSING_LOG_LEVEL: 'must be valid log level',
|
|
20
20
|
MISSING_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/464',
|
|
21
|
-
MISSING_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/
|
|
21
|
+
MISSING_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/865',
|
|
22
|
+
MISSING_VALUE: 'https://github.com/dotenvx/dotenvx/issues/864',
|
|
22
23
|
PRECOMMIT_HOOK_MODIFY_FAILED: 'try again or report error',
|
|
23
24
|
WRONG_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/466'
|
|
24
25
|
}
|
|
@@ -278,6 +279,18 @@ class Errors {
|
|
|
278
279
|
return e
|
|
279
280
|
}
|
|
280
281
|
|
|
282
|
+
missingValue () {
|
|
283
|
+
const code = 'MISSING_VALUE'
|
|
284
|
+
const message = `[${code}] missing value (${this.key})`
|
|
285
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
286
|
+
|
|
287
|
+
const e = new Error(message)
|
|
288
|
+
e.code = code
|
|
289
|
+
e.help = help
|
|
290
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
291
|
+
return e
|
|
292
|
+
}
|
|
293
|
+
|
|
281
294
|
precommitHookModifyFailed () {
|
|
282
295
|
const code = 'PRECOMMIT_HOOK_MODIFY_FAILED'
|
|
283
296
|
const message = `[${code}] failed to modify pre-commit hook: ${this.error.message}`
|
|
@@ -26,6 +26,18 @@ function enquirerOptions (context = {}) {
|
|
|
26
26
|
return options
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function clearLastLine (stream) {
|
|
30
|
+
if (stream && typeof stream.moveCursor === 'function' && typeof stream.clearLine === 'function') {
|
|
31
|
+
stream.moveCursor(0, -1)
|
|
32
|
+
stream.clearLine(0)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (stream && typeof stream.write === 'function') {
|
|
37
|
+
stream.write('\x1B[1A\x1B[2K')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
async function select ({ message, choices }, context) {
|
|
30
42
|
const answer = await enquirer.prompt({
|
|
31
43
|
type: 'select',
|
|
@@ -38,6 +50,40 @@ async function select ({ message, choices }, context) {
|
|
|
38
50
|
return answer.value
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
async function password ({ message, prefix, separator }, context) {
|
|
54
|
+
const output = (context && context.output) || process.stderr
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const answer = await enquirer.prompt({
|
|
58
|
+
type: 'password',
|
|
59
|
+
name: 'value',
|
|
60
|
+
message,
|
|
61
|
+
symbols: {
|
|
62
|
+
prefix: {
|
|
63
|
+
pending: prefix,
|
|
64
|
+
submitted: prefix,
|
|
65
|
+
cancelled: prefix
|
|
66
|
+
},
|
|
67
|
+
separator: {
|
|
68
|
+
pending: separator,
|
|
69
|
+
submitted: separator,
|
|
70
|
+
cancelled: separator
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
...enquirerOptions(context)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
clearLastLine(output)
|
|
77
|
+
return answer.value
|
|
78
|
+
} catch (error) {
|
|
79
|
+
clearLastLine(output)
|
|
80
|
+
const e = new Error('prompt cancelled')
|
|
81
|
+
e.code = 'PROMPT_CANCELLED'
|
|
82
|
+
throw e
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
41
86
|
module.exports = {
|
|
87
|
+
password,
|
|
42
88
|
select
|
|
43
89
|
}
|
package/src/lib/main.js
CHANGED
|
@@ -22,6 +22,7 @@ const { determine } = require('./helpers/envResolution')
|
|
|
22
22
|
const fsx = require('./helpers/fsx')
|
|
23
23
|
const decryptKeyValue = require('./helpers/cryptography/decryptKeyValue')
|
|
24
24
|
const Errors = require('./helpers/errors')
|
|
25
|
+
const normalizeDotenvConfigQuiet = require('./helpers/normalizeDotenvConfigQuiet')
|
|
25
26
|
|
|
26
27
|
function uniqueInjectedKeys (processedEnvs) {
|
|
27
28
|
const result = new Set()
|
|
@@ -35,6 +36,8 @@ function uniqueInjectedKeys (processedEnvs) {
|
|
|
35
36
|
|
|
36
37
|
/** @type {import('./main').config} */
|
|
37
38
|
const config = function (options = {}) {
|
|
39
|
+
options = normalizeDotenvConfigQuiet(options)
|
|
40
|
+
|
|
38
41
|
// allow user to set processEnv to write to
|
|
39
42
|
let processEnv = process.env
|
|
40
43
|
if (options && options.processEnv != null) {
|
|
@@ -130,7 +133,7 @@ const config = function (options = {}) {
|
|
|
130
133
|
if (readableFilepaths.length > 0) {
|
|
131
134
|
msg += ` from ${readableFilepaths.join(', ')}`
|
|
132
135
|
}
|
|
133
|
-
logger.
|
|
136
|
+
logger.success(`⟐ ${msg}`)
|
|
134
137
|
|
|
135
138
|
if (lastError) {
|
|
136
139
|
return { parsed: parsedAll, error: lastError }
|
|
@@ -202,6 +205,8 @@ const parse = function (src, options = {}) {
|
|
|
202
205
|
|
|
203
206
|
/* @type {import('./main').set} */
|
|
204
207
|
const set = async function (key, value, options = {}) {
|
|
208
|
+
options = normalizeDotenvConfigQuiet(options)
|
|
209
|
+
|
|
205
210
|
// encrypt
|
|
206
211
|
let encrypt = true
|
|
207
212
|
if (options.plain) {
|
|
@@ -8,7 +8,7 @@ const SAMPLE_ENV_KIT = require('../helpers/kits/sample')
|
|
|
8
8
|
const Errors = require('../helpers/errors')
|
|
9
9
|
const { determine } = require('./../helpers/envResolution')
|
|
10
10
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
11
|
-
const {
|
|
11
|
+
const { isDotenvPublicKey, isPlainKey, mutateSrc, mutateKeysSrc } = require('../helpers/cryptography')
|
|
12
12
|
const keynames = require('../conventions/keynames')
|
|
13
13
|
const PostArmorUp = require('../api/postArmorUp')
|
|
14
14
|
const prompts = require('../helpers/prompts')
|
|
@@ -129,8 +129,7 @@ async function encryptTransform (options = {}) {
|
|
|
129
129
|
const { parsed } = scan(row.envSrc, { ik, ek })
|
|
130
130
|
|
|
131
131
|
for (const [key, values] of Object.entries(parsed)) {
|
|
132
|
-
|
|
133
|
-
if (isPublicKey(key)) {
|
|
132
|
+
if (isDotenvPublicKey(key) || isPlainKey(key)) {
|
|
134
133
|
continue
|
|
135
134
|
}
|
|
136
135
|
|
|
@@ -7,7 +7,7 @@ const TYPE_ENV_FILE = 'envFile'
|
|
|
7
7
|
const getResolver = require('./../resolvers/get')
|
|
8
8
|
const { determine } = require('./../helpers/envResolution')
|
|
9
9
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
10
|
-
const { mutateSrc, mutateKeysSrc } = require('../helpers/cryptography')
|
|
10
|
+
const { isPlainKey, mutateSrc, mutateKeysSrc } = require('../helpers/cryptography')
|
|
11
11
|
const keynames = require('../conventions/keynames')
|
|
12
12
|
const Errors = require('../helpers/errors')
|
|
13
13
|
const PostArmorUp = require('../api/postArmorUp')
|
|
@@ -37,7 +37,7 @@ async function setTransform (options = {}) {
|
|
|
37
37
|
const fk = options.fk || '.env.keys'
|
|
38
38
|
let noArmor = options.noArmor // key storage selector below
|
|
39
39
|
const noCreate = options.noCreate
|
|
40
|
-
const noEncrypt = !options.encrypt
|
|
40
|
+
const noEncrypt = !options.encrypt || isPlainKey(key)
|
|
41
41
|
|
|
42
42
|
const processedEnvs = []
|
|
43
43
|
const changedFilepaths = []
|