@dotenvx/dotenvx 1.7.0 → 1.9.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 +34 -1
- package/README.md +14 -14
- package/package.json +4 -6
- package/src/cli/actions/decrypt.js +2 -2
- package/src/cli/actions/encrypt.js +2 -2
- package/src/cli/actions/ext/genexample.js +0 -3
- package/src/cli/actions/ext/vault/status.js +1 -1
- package/src/cli/actions/run.js +9 -0
- package/src/cli/commands/ext.js +0 -2
- package/src/cli/dotenvx.js +3 -42
- package/src/lib/helpers/colorDepth.js +4 -0
- package/src/lib/helpers/decryptValue.js +16 -5
- package/src/lib/helpers/dotenvExpand.js +9 -12
- package/src/lib/helpers/execute.js +2 -8
- package/src/lib/helpers/executeCommand.js +15 -7
- package/src/lib/helpers/executeExtension.js +4 -3
- package/src/lib/helpers/inject.js +4 -4
- package/src/lib/helpers/packageJson.js +2 -7
- package/src/lib/helpers/parseDecryptEvalExpand.js +36 -5
- package/src/lib/helpers/truncate.js +6 -0
- package/src/lib/main.js +4 -4
- package/src/lib/services/decrypt.js +29 -10
- package/src/lib/services/encrypt.js +31 -14
- package/src/lib/services/genexample.js +42 -24
- package/src/lib/services/ls.js +9 -7
- package/src/lib/services/run.js +11 -9
- package/src/lib/services/status.js +3 -3
- package/src/lib/services/vaultDecrypt.js +1 -1
- package/src/shared/colors.js +50 -0
- package/src/shared/logger.js +75 -35
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,40 @@
|
|
|
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/v1.
|
|
5
|
+
## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.9.0...main)
|
|
6
|
+
|
|
7
|
+
## 1.9.0
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* add `--exclude-key` (`-ek`) option to `dotenvx encrypt` and `dotenvx decrypt` ([#344](https://github.com/dotenvx/dotenvx/pull/344))
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
* preserve comments and spacing on first-time generation of .env.example file ([#346](https://github.com/dotenvx/dotenvx/pull/346))
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
|
|
19
|
+
* removed `winston` - logger simplified to use `console.log` going forward ([#347](https://github.com/dotenvx/dotenvx/pull/347))
|
|
20
|
+
|
|
21
|
+
## 1.8.0
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
* warn when decryption fails on `run` ([#339](https://github.com/dotenvx/dotenvx/pull/339))
|
|
26
|
+
* decrypt expanded values as necessary ([#336](https://github.com/dotenvx/dotenvx/pull/336))
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
* use `ansi` colors over `rgb` - for wider terminal coverage ([#340](https://github.com/dotenvx/dotenvx/pull/340))
|
|
31
|
+
* replace `chalk` with `picocolors` and `color-name` - cutting down on 5 dependencies ([#335](https://github.com/dotenvx/dotenvx/pull/335))
|
|
32
|
+
* replace `execa` with `tinyexec` - cutting down on 15 dependencies ([#328](https://github.com/dotenvx/dotenvx/pull/328))
|
|
33
|
+
* optimize `Ls._filepaths` ([#317](https://github.com/dotenvx/dotenvx/pull/317/))
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
* remove `picocolors` and `color-name` - cutting down on 2 dependencies ([#340](https://github.com/dotenvx/dotenvx/pull/340))
|
|
38
|
+
* remove `ext hub` from extension list (you can still install it as an extension [here](https://github.com/dotenvx/dotenvx-ext-hub)) ([#337](https://github.com/dotenvx/dotenvx/pull/337))
|
|
6
39
|
|
|
7
40
|
## 1.7.0
|
|
8
41
|
|
package/README.md
CHANGED
|
@@ -27,31 +27,31 @@ console.log(`Hello ${process.env.HELLO}`)
|
|
|
27
27
|
|
|
28
28
|
or install globally - *unlocks dotenv for any language, framework, or platform!*
|
|
29
29
|
|
|
30
|
-
<details><summary>with
|
|
30
|
+
<details><summary>with curl 🌐 </summary><br>
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
|
|
33
|
+
curl -sfS https://dotenvx.sh | sh
|
|
34
34
|
dotenvx help
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
[](https://github.com/dotenvx/dotenvx.sh/blob/main/install.sh)
|
|
38
|
+
[](https://github.com/dotenvx/dotenvx.sh/blob/main/install.sh)
|
|
39
|
+
[](https://github.com/dotenvx/dotenvx.sh/blob/main/install.sh)
|
|
40
|
+
<sup>*curl installs sourced from npm binary packages - <a href="https://www.npmjs.com/package/@dotenvx/dotenvx-linux-x86_64">example</a></sup>
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
</details>
|
|
43
45
|
|
|
44
|
-
<details><summary>with
|
|
46
|
+
<details><summary>with brew 🍺</summary><br>
|
|
45
47
|
|
|
46
48
|
```sh
|
|
47
|
-
|
|
49
|
+
brew install dotenvx/brew/dotenvx
|
|
48
50
|
dotenvx help
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
[](https://github.com/dotenvx/dotenvx.sh/blob/main/install.sh)
|
|
54
|
-
<sup>*curl installs sourced from npm binary packages - <a href="https://www.npmjs.com/package/@dotenvx/dotenvx-linux-x86_64">example</a></sup>
|
|
53
|
+
[](https://github.com/dotenvx/homebrew-brew/blob/main/Formula/dotenvx.rb)
|
|
54
|
+
<sup>*homebrew installs sourced from github releases - <a href="https://github.com/dotenvx/homebrew-brew/blob/main/Formula/dotenvx.rb">formula</a></sup>
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
|
|
@@ -866,7 +866,7 @@ More examples
|
|
|
866
866
|
</details>
|
|
867
867
|
* <details><summary>`run --verbose`</summary><br>
|
|
868
868
|
|
|
869
|
-
Set log level to `verbose`. ([log levels](https://
|
|
869
|
+
Set log level to `verbose`. ([log levels](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
870
870
|
|
|
871
871
|
```sh
|
|
872
872
|
$ echo "HELLO=production" > .env.production
|
|
@@ -882,7 +882,7 @@ More examples
|
|
|
882
882
|
</details>
|
|
883
883
|
* <details><summary>`run --debug`</summary><br>
|
|
884
884
|
|
|
885
|
-
Set log level to `debug`. ([log levels](https://
|
|
885
|
+
Set log level to `debug`. ([log levels](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
886
886
|
|
|
887
887
|
```sh
|
|
888
888
|
$ echo "HELLO=production" > .env.production
|
|
@@ -904,7 +904,7 @@ More examples
|
|
|
904
904
|
</details>
|
|
905
905
|
* <details><summary>`run --quiet`</summary><br>
|
|
906
906
|
|
|
907
|
-
Use `--quiet` to suppress all output (except errors). ([log levels](https://
|
|
907
|
+
Use `--quiet` to suppress all output (except errors). ([log levels](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
908
908
|
|
|
909
909
|
```sh
|
|
910
910
|
$ echo "HELLO=production" > .env.production
|
|
@@ -927,7 +927,7 @@ More examples
|
|
|
927
927
|
Hello production
|
|
928
928
|
```
|
|
929
929
|
|
|
930
|
-
Available log levels are `error, warn, info, verbose, debug, silly` ([source](https://
|
|
930
|
+
Available log levels are `error, warn, info, verbose, debug, silly` ([source](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
931
931
|
|
|
932
932
|
</details>
|
|
933
933
|
* <details><summary>`run --convention=nextjs`</summary><br>
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.9.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a better dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -36,18 +36,16 @@
|
|
|
36
36
|
},
|
|
37
37
|
"funding": "https://dotenvx.com",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"chalk": "^4.1.2",
|
|
40
39
|
"commander": "^11.1.0",
|
|
41
40
|
"diff": "^5.2.0",
|
|
42
41
|
"dotenv": "^16.4.5",
|
|
43
42
|
"eciesjs": "^0.4.6",
|
|
44
|
-
"
|
|
45
|
-
"fdir": "^6.1.1",
|
|
43
|
+
"fdir": "^6.2.0",
|
|
46
44
|
"ignore": "^5.3.0",
|
|
47
45
|
"object-treeify": "1.1.33",
|
|
48
|
-
"picomatch": "^
|
|
46
|
+
"picomatch": "^4.0.2",
|
|
47
|
+
"tinyexec": "^0.2.0",
|
|
49
48
|
"which": "^4.0.0",
|
|
50
|
-
"winston": "^3.11.0",
|
|
51
49
|
"xxhashjs": "^0.2.2"
|
|
52
50
|
},
|
|
53
51
|
"devDependencies": {
|
|
@@ -13,7 +13,7 @@ function decrypt () {
|
|
|
13
13
|
if (options.stdout) {
|
|
14
14
|
const {
|
|
15
15
|
processedEnvFiles
|
|
16
|
-
} = main.decrypt(options.envFile, options.key)
|
|
16
|
+
} = main.decrypt(options.envFile, options.key, options.excludeKey)
|
|
17
17
|
|
|
18
18
|
for (const processedEnvFile of processedEnvFiles) {
|
|
19
19
|
process.stdout.write(processedEnvFile.envSrc)
|
|
@@ -25,7 +25,7 @@ function decrypt () {
|
|
|
25
25
|
processedEnvFiles,
|
|
26
26
|
changedFilepaths,
|
|
27
27
|
unchangedFilepaths
|
|
28
|
-
} = main.decrypt(options.envFile, options.key)
|
|
28
|
+
} = main.decrypt(options.envFile, options.key, options.excludeKey)
|
|
29
29
|
|
|
30
30
|
for (const processedEnvFile of processedEnvFiles) {
|
|
31
31
|
logger.verbose(`decrypting ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
|
|
@@ -15,7 +15,7 @@ function encrypt () {
|
|
|
15
15
|
if (options.stdout) {
|
|
16
16
|
const {
|
|
17
17
|
processedEnvFiles
|
|
18
|
-
} = main.encrypt(options.envFile, options.key)
|
|
18
|
+
} = main.encrypt(options.envFile, options.key, options.excludeKey)
|
|
19
19
|
|
|
20
20
|
for (const processedEnvFile of processedEnvFiles) {
|
|
21
21
|
process.stdout.write(processedEnvFile.envSrc)
|
|
@@ -27,7 +27,7 @@ function encrypt () {
|
|
|
27
27
|
processedEnvFiles,
|
|
28
28
|
changedFilepaths,
|
|
29
29
|
unchangedFilepaths
|
|
30
|
-
} = main.encrypt(options.envFile, options.key)
|
|
30
|
+
} = main.encrypt(options.envFile, options.key, options.excludeKey)
|
|
31
31
|
|
|
32
32
|
for (const processedEnvFile of processedEnvFiles) {
|
|
33
33
|
logger.verbose(`encrypting ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
|
|
@@ -20,9 +20,6 @@ function genexample (directory) {
|
|
|
20
20
|
|
|
21
21
|
logger.verbose(`loading env from ${envFile}`)
|
|
22
22
|
|
|
23
|
-
// TODO: display pre-existing
|
|
24
|
-
// TODO: display added/appended/injected
|
|
25
|
-
|
|
26
23
|
fs.writeFileSync(exampleFilepath, envExampleFile, ENCODING)
|
|
27
24
|
|
|
28
25
|
if (addedKeys.length > 0) {
|
|
@@ -58,7 +58,7 @@ function status (directory) {
|
|
|
58
58
|
|
|
59
59
|
if (untrackedFilenames.length > 0) {
|
|
60
60
|
logger.warn(`untracked (${untrackedFilenames.join(', ')})`)
|
|
61
|
-
logger.help(`? track them with [dotenvx encrypt ${directory}]`)
|
|
61
|
+
logger.help(`? track them with [dotenvx ext vault encrypt ${directory}]`)
|
|
62
62
|
}
|
|
63
63
|
} catch (error) {
|
|
64
64
|
logger.error(error.message)
|
package/src/cli/actions/run.js
CHANGED
|
@@ -60,6 +60,15 @@ async function run () {
|
|
|
60
60
|
logger.warnv(processedEnv.error.message)
|
|
61
61
|
}
|
|
62
62
|
} else {
|
|
63
|
+
if (processedEnv.warnings) {
|
|
64
|
+
for (const warning of processedEnv.warnings) {
|
|
65
|
+
logger.warn(warning.message)
|
|
66
|
+
if (warning.help) {
|
|
67
|
+
logger.help(warning.help)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
63
72
|
// debug parsed
|
|
64
73
|
const parsed = processedEnv.parsed
|
|
65
74
|
logger.debug(parsed)
|
package/src/cli/commands/ext.js
CHANGED
package/src/cli/dotenvx.js
CHANGED
|
@@ -100,6 +100,7 @@ program.command('encrypt')
|
|
|
100
100
|
.description('convert .env file(s) to encrypted .env file(s)')
|
|
101
101
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
102
102
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
103
|
+
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
103
104
|
.option('--stdout', 'send to stdout')
|
|
104
105
|
.action(encryptAction)
|
|
105
106
|
|
|
@@ -109,6 +110,7 @@ program.command('decrypt')
|
|
|
109
110
|
.description('convert encrypted .env file(s) to plain .env file(s)')
|
|
110
111
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
111
112
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
113
|
+
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
112
114
|
.option('--stdout', 'send to stdout')
|
|
113
115
|
.action(decryptAction)
|
|
114
116
|
|
|
@@ -132,40 +134,8 @@ program.command('help [command]')
|
|
|
132
134
|
program.addCommand(require('./commands/ext'))
|
|
133
135
|
|
|
134
136
|
//
|
|
135
|
-
//
|
|
137
|
+
// MOVED
|
|
136
138
|
//
|
|
137
|
-
const lsAction = require('./actions/ext/ls')
|
|
138
|
-
program.command('ls')
|
|
139
|
-
.description('DEPRECATED: moved to [dotenvx ext ls]')
|
|
140
|
-
.argument('[directory]', 'directory to list .env files from', '.')
|
|
141
|
-
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
142
|
-
.action(function (...args) {
|
|
143
|
-
logger.warn('DEPRECATION NOTICE: [ls] has moved to [dotenvx ext ls]')
|
|
144
|
-
|
|
145
|
-
lsAction.apply(this, args)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
const genexampleAction = require('./actions/ext/genexample')
|
|
149
|
-
program.command('genexample')
|
|
150
|
-
.description('DEPRECATED: moved to [dotenvx ext genexample]')
|
|
151
|
-
.argument('[directory]', 'directory to generate from', '.')
|
|
152
|
-
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
153
|
-
.action(function (...args) {
|
|
154
|
-
logger.warn('DEPRECATION NOTICE: [genexample] has moved to [dotenvx ext genexample]')
|
|
155
|
-
|
|
156
|
-
genexampleAction.apply(this, args)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
const gitignoreAction = require('./actions/ext/gitignore')
|
|
160
|
-
program.command('gitignore')
|
|
161
|
-
.description('DEPRECATED: moved to [dotenvx ext gitignore]')
|
|
162
|
-
.addHelpText('after', examples.gitignore)
|
|
163
|
-
.action(function (...args) {
|
|
164
|
-
logger.warn('DEPRECATION NOTICE: [gitignore] has moved to [dotenvx ext gitignore]')
|
|
165
|
-
|
|
166
|
-
gitignoreAction.apply(this, args)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
139
|
const prebuildAction = require('./actions/ext/prebuild')
|
|
170
140
|
program.command('prebuild')
|
|
171
141
|
.description('DEPRECATED: moved to [dotenvx ext prebuild]')
|
|
@@ -187,15 +157,6 @@ program.command('precommit')
|
|
|
187
157
|
precommitAction.apply(this, args)
|
|
188
158
|
})
|
|
189
159
|
|
|
190
|
-
const scanAction = require('./actions/ext/scan')
|
|
191
|
-
program.command('scan')
|
|
192
|
-
.description('DEPRECATED: moved to [dotenvx ext scan]')
|
|
193
|
-
.action(function (...args) {
|
|
194
|
-
logger.warn('DEPRECATION NOTICE: [scan] has moved to [dotenvx ext scan]')
|
|
195
|
-
|
|
196
|
-
scanAction.apply(this, args)
|
|
197
|
-
})
|
|
198
|
-
|
|
199
160
|
// overide helpInformation to hide DEPRECATED commands
|
|
200
161
|
program.helpInformation = function () {
|
|
201
162
|
const originalHelp = Command.prototype.helpInformation.call(this)
|
|
@@ -10,6 +10,7 @@ function decryptValue (value, privateKey) {
|
|
|
10
10
|
const privateKeys = privateKey.split(',')
|
|
11
11
|
|
|
12
12
|
let decryptedValue
|
|
13
|
+
let decryptionError
|
|
13
14
|
for (const key of privateKeys) {
|
|
14
15
|
const secret = Buffer.from(key, 'hex')
|
|
15
16
|
const encoded = value.substring(PREFIX.length)
|
|
@@ -17,15 +18,25 @@ function decryptValue (value, privateKey) {
|
|
|
17
18
|
|
|
18
19
|
try {
|
|
19
20
|
decryptedValue = decrypt(secret, ciphertext).toString()
|
|
21
|
+
decryptionError = null // reset to null error (scenario for multiple private keys)
|
|
20
22
|
break
|
|
21
|
-
} catch (
|
|
22
|
-
|
|
23
|
+
} catch (e) {
|
|
24
|
+
if (e.message === 'Invalid private key') {
|
|
25
|
+
decryptionError = new Error('private key looks invalid')
|
|
26
|
+
} else if (e.message === 'Unsupported state or unable to authenticate data') {
|
|
27
|
+
decryptionError = new Error('private key looks wrong')
|
|
28
|
+
} else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
|
|
29
|
+
decryptionError = new Error('encrypted data looks malformed')
|
|
30
|
+
} else {
|
|
31
|
+
decryptionError = new Error(`${e.message}`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
decryptionError.code = 'DECRYPTION_FAILED'
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return value
|
|
38
|
+
if (decryptionError) {
|
|
39
|
+
throw decryptionError
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
return decryptedValue
|
|
@@ -42,30 +42,27 @@ function interpolate (value, lookups) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function expand (options) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
processEnv = options.processEnv
|
|
48
|
-
}
|
|
45
|
+
const processEnv = options.processEnv || {}
|
|
46
|
+
const parsed = options.parsed || {}
|
|
49
47
|
|
|
50
|
-
const combined = { ...processEnv, ...
|
|
51
|
-
const combinedReversed = { ...
|
|
48
|
+
const combined = { ...processEnv, ...parsed }
|
|
49
|
+
const combinedReversed = { ...parsed, ...processEnv }
|
|
52
50
|
|
|
53
|
-
for (const key in
|
|
54
|
-
const value =
|
|
51
|
+
for (const key in parsed) {
|
|
52
|
+
const value = parsed[key]
|
|
55
53
|
|
|
56
54
|
// interpolate using both file and processEnv (file interpolation wins. used for --overload later)
|
|
57
55
|
const fileValue = _resolveEscapeSequences(interpolate(value, combined))
|
|
58
|
-
|
|
56
|
+
parsed[key] = fileValue
|
|
59
57
|
|
|
60
58
|
if (fileValue === _resolveEscapeSequences(value)) {
|
|
61
59
|
continue // no change means no expansion, move on
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
if (processEnv[key]) {
|
|
65
|
-
continue // already has a value in
|
|
63
|
+
continue // already has a value in processEnv, move on
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
// interpolate with processEnv only (used for default no overload)
|
|
69
66
|
const processEnvValue = interpolate(value, combinedReversed) // could be empty string ''
|
|
70
67
|
if (processEnvValue) {
|
|
71
68
|
processEnv[key] = _resolveEscapeSequences(processEnvValue) // set it
|
|
@@ -73,7 +70,7 @@ function expand (options) {
|
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
return {
|
|
76
|
-
parsed
|
|
73
|
+
parsed,
|
|
77
74
|
processEnv
|
|
78
75
|
}
|
|
79
76
|
}
|
|
@@ -13,6 +13,8 @@ async function executeCommand (commandArgs, env) {
|
|
|
13
13
|
|
|
14
14
|
// handler for SIGINT
|
|
15
15
|
let commandProcess
|
|
16
|
+
// workaround until error.signal gets added https://github.com/tinylibs/tinyexec/issues/28
|
|
17
|
+
let signal
|
|
16
18
|
const sigintHandler = () => {
|
|
17
19
|
logger.debug('received SIGINT')
|
|
18
20
|
logger.debug('checking command process')
|
|
@@ -20,6 +22,7 @@ async function executeCommand (commandArgs, env) {
|
|
|
20
22
|
|
|
21
23
|
if (commandProcess) {
|
|
22
24
|
logger.debug('sending SIGINT to command process')
|
|
25
|
+
signal = 'SIGINT'
|
|
23
26
|
commandProcess.kill('SIGINT') // Send SIGINT to the command process
|
|
24
27
|
/* c8 ignore start */
|
|
25
28
|
} else {
|
|
@@ -37,6 +40,7 @@ async function executeCommand (commandArgs, env) {
|
|
|
37
40
|
|
|
38
41
|
if (commandProcess) {
|
|
39
42
|
logger.debug('sending SIGTERM to command process')
|
|
43
|
+
signal = 'SIGTERM'
|
|
40
44
|
commandProcess.kill('SIGTERM') // Send SIGTEM to the command process
|
|
41
45
|
} else {
|
|
42
46
|
logger.debug('no command process to send SIGTERM to')
|
|
@@ -73,9 +77,11 @@ async function executeCommand (commandArgs, env) {
|
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
commandProcess = execute.
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
commandProcess = execute.exec(commandArgs[0], commandArgs.slice(1), {
|
|
81
|
+
nodeOptions: {
|
|
82
|
+
stdio: 'inherit',
|
|
83
|
+
env: { ...process.env, ...env }
|
|
84
|
+
}
|
|
79
85
|
})
|
|
80
86
|
|
|
81
87
|
process.on('SIGINT', sigintHandler)
|
|
@@ -86,7 +92,9 @@ async function executeCommand (commandArgs, env) {
|
|
|
86
92
|
})
|
|
87
93
|
|
|
88
94
|
// Wait for the command process to finish
|
|
89
|
-
|
|
95
|
+
// exitCode is not in the awaited result, see https://github.com/tinylibs/tinyexec/issues/27
|
|
96
|
+
await commandProcess
|
|
97
|
+
const { exitCode } = commandProcess
|
|
90
98
|
|
|
91
99
|
if (exitCode !== 0) {
|
|
92
100
|
logger.debug(`received exitCode ${exitCode}`)
|
|
@@ -94,11 +102,11 @@ async function executeCommand (commandArgs, env) {
|
|
|
94
102
|
}
|
|
95
103
|
} catch (error) {
|
|
96
104
|
// no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
|
|
97
|
-
if (
|
|
105
|
+
if (signal !== 'SIGINT' && signal !== 'SIGTERM') {
|
|
98
106
|
if (error.code === 'ENOENT') {
|
|
99
|
-
logger.errornocolor(`Unknown command: ${error.
|
|
107
|
+
logger.errornocolor(`Unknown command: ${error.path}${error.spawnargs ? ' ' + error.spawnargs.join(' ') : ''}`)
|
|
100
108
|
} else if (error.message.includes('Command failed with exit code 1')) {
|
|
101
|
-
logger.errornocolor(`Command exited with exit code 1: ${error.
|
|
109
|
+
logger.errornocolor(`Command exited with exit code 1: ${error.path}${error.spawnargs ? ' ' + error.spawnargs.join(' ') : ''}`)
|
|
102
110
|
} else {
|
|
103
111
|
logger.errornocolor(error.message)
|
|
104
112
|
}
|
|
@@ -22,9 +22,10 @@ function executeExtension (ext, command, rawArgs) {
|
|
|
22
22
|
|
|
23
23
|
const result = childProcess.spawnSync(`dotenvx-ext-${command}`, forwardedArgs, { stdio: 'inherit', env })
|
|
24
24
|
if (result.error) {
|
|
25
|
-
if (command === '
|
|
26
|
-
|
|
27
|
-
logger.
|
|
25
|
+
if (command === 'vault') {
|
|
26
|
+
// when ready, uncomment to deprecate ext vault
|
|
27
|
+
// logger.warn(`[INSTALLATION_NEEDED] install dotenvx-ext-${command} to use [dotenvx ext ${command}] commands`)
|
|
28
|
+
// logger.help('? see installation instructions [https://github.com/dotenvx/dotenvx-ext-vault]')
|
|
28
29
|
} else {
|
|
29
30
|
logger.info(`error: unknown command '${command}'`)
|
|
30
31
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
function inject (
|
|
1
|
+
function inject (clonedProcessEnv = {}, parsed = {}, overload = false, processEnv = process.env) {
|
|
2
2
|
const injected = {}
|
|
3
3
|
const preExisted = {}
|
|
4
4
|
|
|
5
5
|
// set processEnv
|
|
6
6
|
for (const key of Object.keys(parsed)) {
|
|
7
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
7
|
+
if (Object.prototype.hasOwnProperty.call(clonedProcessEnv, key)) {
|
|
8
8
|
if (overload === true) {
|
|
9
9
|
processEnv[key] = parsed[key]
|
|
10
|
-
|
|
11
10
|
injected[key] = parsed[key] // track injected key/value
|
|
12
11
|
} else {
|
|
13
|
-
|
|
12
|
+
processEnv[key] = clonedProcessEnv[key]
|
|
13
|
+
preExisted[key] = clonedProcessEnv[key] // track preExisted key/value
|
|
14
14
|
}
|
|
15
15
|
} else {
|
|
16
16
|
processEnv[key] = parsed[key]
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path')
|
|
1
|
+
const { name, version, description } = require('../../../package.json')
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
7
|
-
|
|
8
|
-
module.exports = packageJson
|
|
3
|
+
module.exports = { name, version, description }
|
|
@@ -2,16 +2,29 @@ const dotenv = require('dotenv')
|
|
|
2
2
|
const dotenvEval = require('./dotenvEval')
|
|
3
3
|
const dotenvExpand = require('./dotenvExpand')
|
|
4
4
|
const decryptValue = require('./decryptValue')
|
|
5
|
+
const truncate = require('./truncate')
|
|
6
|
+
|
|
7
|
+
function warning (e, key, privateKey) {
|
|
8
|
+
const warning = new Error(`[${e.code}] could not decrypt ${key} using private key ${truncate(privateKey)}`)
|
|
9
|
+
warning.code = e.code
|
|
10
|
+
warning.help = `[${e.code}] ? ${e.message}`
|
|
11
|
+
|
|
12
|
+
return warning
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
function parseDecryptEvalExpand (src, privateKey = null, processEnv = process.env) {
|
|
16
|
+
const warnings = []
|
|
17
|
+
|
|
7
18
|
// parse
|
|
8
19
|
const parsed = dotenv.parse(src)
|
|
9
|
-
|
|
10
|
-
// handle inline encrypted values
|
|
11
20
|
if (privateKey && privateKey.length > 0) {
|
|
12
21
|
for (const key in parsed) {
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
try {
|
|
23
|
+
const decryptedValue = decryptValue(parsed[key], privateKey)
|
|
24
|
+
parsed[key] = decryptedValue
|
|
25
|
+
} catch (_e) {
|
|
26
|
+
// do nothing. warnings tracked further below.
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
29
|
}
|
|
17
30
|
|
|
@@ -28,6 +41,24 @@ function parseDecryptEvalExpand (src, privateKey = null, processEnv = process.en
|
|
|
28
41
|
parsed: evaled
|
|
29
42
|
}
|
|
30
43
|
const expanded = dotenvExpand.expand(inputEvaled)
|
|
44
|
+
if (privateKey && privateKey.length > 0) {
|
|
45
|
+
for (const key in expanded.parsed) {
|
|
46
|
+
try {
|
|
47
|
+
const decryptedValue = decryptValue(expanded.parsed[key], privateKey)
|
|
48
|
+
expanded.parsed[key] = decryptedValue
|
|
49
|
+
} catch (e) {
|
|
50
|
+
warnings.push(warning(e, key, privateKey))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const key in processEnv) {
|
|
54
|
+
try {
|
|
55
|
+
const decryptedValue = decryptValue(processEnv[key], privateKey)
|
|
56
|
+
processEnv[key] = decryptedValue
|
|
57
|
+
} catch (e) {
|
|
58
|
+
warnings.push(warning(e, key, privateKey))
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
31
62
|
|
|
32
63
|
// 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
|
|
33
64
|
const result = {}
|
|
@@ -35,7 +66,7 @@ function parseDecryptEvalExpand (src, privateKey = null, processEnv = process.en
|
|
|
35
66
|
result[key] = expanded.parsed[key]
|
|
36
67
|
}
|
|
37
68
|
|
|
38
|
-
return { parsed: result, processEnv }
|
|
69
|
+
return { parsed: result, processEnv, warnings }
|
|
39
70
|
}
|
|
40
71
|
|
|
41
72
|
module.exports = parseDecryptEvalExpand
|
package/src/lib/main.js
CHANGED
|
@@ -189,13 +189,13 @@ const set = function (key, value, envFile, encrypt) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
/** @type {import('./main').encrypt} */
|
|
192
|
-
const encrypt = function (envFile, key) {
|
|
193
|
-
return new Encrypt(envFile, key).run()
|
|
192
|
+
const encrypt = function (envFile, key, excludeKey) {
|
|
193
|
+
return new Encrypt(envFile, key, excludeKey).run()
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/** @type {import('./main').encrypt} */
|
|
197
|
-
const decrypt = function (envFile, key) {
|
|
198
|
-
return new Decrypt(envFile, key).run()
|
|
197
|
+
const decrypt = function (envFile, key, excludeKey) {
|
|
198
|
+
return new Decrypt(envFile, key, excludeKey).run()
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
/** @type {import('./main').status} */
|
|
@@ -14,10 +14,12 @@ class Decrypt {
|
|
|
14
14
|
/**
|
|
15
15
|
* @param {string|string[]} [envFile]
|
|
16
16
|
* @param {string|string[]} [key]
|
|
17
|
+
* @param {string|string[]} [excludeKey]
|
|
17
18
|
**/
|
|
18
|
-
constructor (envFile = '.env', key = []) {
|
|
19
|
+
constructor (envFile = '.env', key = [], excludeKey = []) {
|
|
19
20
|
this.envFile = envFile
|
|
20
21
|
this.key = key
|
|
22
|
+
this.excludeKey = excludeKey
|
|
21
23
|
this.processedEnvFiles = []
|
|
22
24
|
this.changedFilepaths = new Set()
|
|
23
25
|
this.unchangedFilepaths = new Set()
|
|
@@ -26,6 +28,7 @@ class Decrypt {
|
|
|
26
28
|
run () {
|
|
27
29
|
const envFilepaths = this._envFilepaths()
|
|
28
30
|
const keys = this._keys()
|
|
31
|
+
const excludeKeys = this._excludeKeys()
|
|
29
32
|
for (const envFilepath of envFilepaths) {
|
|
30
33
|
const filepath = path.resolve(envFilepath)
|
|
31
34
|
|
|
@@ -49,17 +52,25 @@ class Decrypt {
|
|
|
49
52
|
// iterate over all non-encrypted values and encrypt them
|
|
50
53
|
const parsed = dotenv.parse(src)
|
|
51
54
|
for (const [key, value] of Object.entries(parsed)) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// key excluded - don't decrypt it
|
|
56
|
+
if (excludeKeys.includes(key)) {
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// key effectively excluded (by not being in the list of includes) - don't encrypt it
|
|
61
|
+
if (keys.length > 0 && !keys.includes(key)) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const encrypted = isEncrypted(key, value)
|
|
66
|
+
if (encrypted) {
|
|
67
|
+
row.keys.push(key) // track key(s)
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
const decryptedValue = decryptValue(value, privateKey)
|
|
70
|
+
// once newSrc is built write it out
|
|
71
|
+
src = replace(src, key, decryptedValue)
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
}
|
|
73
|
+
row.changed = true // track change
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
76
|
|
|
@@ -106,6 +117,14 @@ class Decrypt {
|
|
|
106
117
|
|
|
107
118
|
return this.key
|
|
108
119
|
}
|
|
120
|
+
|
|
121
|
+
_excludeKeys () {
|
|
122
|
+
if (!Array.isArray(this.excludeKey)) {
|
|
123
|
+
return [this.excludeKey]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return this.excludeKey
|
|
127
|
+
}
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
module.exports = Decrypt
|
|
@@ -15,10 +15,12 @@ class Encrypt {
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {string|string[]} [envFile]
|
|
17
17
|
* @param {string|string[]} [key]
|
|
18
|
+
* @param {string|string[]} [excludeKey]
|
|
18
19
|
**/
|
|
19
|
-
constructor (envFile = '.env', key = []) {
|
|
20
|
+
constructor (envFile = '.env', key = [], excludeKey = []) {
|
|
20
21
|
this.envFile = envFile
|
|
21
22
|
this.key = key
|
|
23
|
+
this.excludeKey = excludeKey
|
|
22
24
|
this.processedEnvFiles = []
|
|
23
25
|
this.changedFilepaths = new Set()
|
|
24
26
|
this.unchangedFilepaths = new Set()
|
|
@@ -27,6 +29,7 @@ class Encrypt {
|
|
|
27
29
|
run () {
|
|
28
30
|
const envFilepaths = this._envFilepaths()
|
|
29
31
|
const keys = this._keys()
|
|
32
|
+
const excludeKeys = this._excludeKeys()
|
|
30
33
|
for (const envFilepath of envFilepaths) {
|
|
31
34
|
const filepath = path.resolve(envFilepath)
|
|
32
35
|
|
|
@@ -63,19 +66,25 @@ class Encrypt {
|
|
|
63
66
|
// iterate over all non-encrypted values and encrypt them
|
|
64
67
|
const parsed = dotenv.parse(src)
|
|
65
68
|
for (const [key, value] of Object.entries(parsed)) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
// key excluded - don't encrypt it
|
|
70
|
+
if (excludeKeys.includes(key)) {
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// key effectively excluded (by not being in the list of includes) - don't encrypt it
|
|
75
|
+
if (keys.length > 0 && !keys.includes(key)) {
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const encrypted = isEncrypted(key, value) || isPublicKey(key, value)
|
|
80
|
+
if (!encrypted) {
|
|
81
|
+
row.keys.push(key) // track key(s)
|
|
82
|
+
|
|
83
|
+
const encryptedValue = encryptValue(value, publicKey)
|
|
84
|
+
// once newSrc is built write it out
|
|
85
|
+
src = replace(src, key, encryptedValue)
|
|
86
|
+
|
|
87
|
+
row.changed = true // track change
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
|
|
@@ -122,6 +131,14 @@ class Encrypt {
|
|
|
122
131
|
|
|
123
132
|
return this.key
|
|
124
133
|
}
|
|
134
|
+
|
|
135
|
+
_excludeKeys () {
|
|
136
|
+
if (!Array.isArray(this.excludeKey)) {
|
|
137
|
+
return [this.excludeKey]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return this.excludeKey
|
|
141
|
+
}
|
|
125
142
|
}
|
|
126
143
|
|
|
127
144
|
module.exports = Encrypt
|
|
@@ -5,11 +5,15 @@ const dotenv = require('dotenv')
|
|
|
5
5
|
const ENCODING = 'utf8'
|
|
6
6
|
|
|
7
7
|
const findEnvFiles = require('../helpers/findEnvFiles')
|
|
8
|
+
const replace = require('../helpers/replace')
|
|
8
9
|
|
|
9
10
|
class Genexample {
|
|
10
11
|
constructor (directory = '.', envFile) {
|
|
11
12
|
this.directory = directory
|
|
12
13
|
this.envFile = envFile || findEnvFiles(directory)
|
|
14
|
+
|
|
15
|
+
this.exampleFilename = '.env.example'
|
|
16
|
+
this.exampleFilepath = path.resolve(this.directory, this.exampleFilename)
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
run () {
|
|
@@ -27,6 +31,12 @@ class Genexample {
|
|
|
27
31
|
const keys = new Set()
|
|
28
32
|
const addedKeys = new Set()
|
|
29
33
|
const envFilepaths = this._envFilepaths()
|
|
34
|
+
/** @type {Record<string, string>} */
|
|
35
|
+
const injected = {}
|
|
36
|
+
/** @type {Record<string, string>} */
|
|
37
|
+
const preExisted = {}
|
|
38
|
+
|
|
39
|
+
let exampleSrc = `# ${this.exampleFilename} - generated with dotenvx\n`
|
|
30
40
|
|
|
31
41
|
for (const envFilepath of envFilepaths) {
|
|
32
42
|
const filepath = path.resolve(this.directory, envFilepath)
|
|
@@ -41,43 +51,51 @@ class Genexample {
|
|
|
41
51
|
throw error
|
|
42
52
|
}
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
// get the original src
|
|
55
|
+
let src = fs.readFileSync(filepath, { encoding: ENCODING })
|
|
56
|
+
const parsed = dotenv.parse(src)
|
|
57
|
+
for (const key in parsed) {
|
|
58
|
+
// used later
|
|
46
59
|
keys.add(key)
|
|
60
|
+
|
|
61
|
+
// once newSrc is built write it out
|
|
62
|
+
src = replace(src, key, '') // empty value
|
|
47
63
|
}
|
|
48
|
-
}
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
const exampleFilename = '.env.example'
|
|
52
|
-
const exampleFilepath = path.resolve(this.directory, exampleFilename)
|
|
53
|
-
if (!fs.existsSync(exampleFilepath)) {
|
|
54
|
-
envExampleFile += `# ${exampleFilename} - generated with dotenvx\n`
|
|
55
|
-
} else {
|
|
56
|
-
envExampleFile = fs.readFileSync(exampleFilepath, ENCODING)
|
|
65
|
+
exampleSrc += `\n${src}`
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
for (const key of [...keys]) {
|
|
66
|
-
if (key in currentEnvExample) {
|
|
67
|
-
preExisted[key] = currentEnvExample[key]
|
|
68
|
-
} else {
|
|
69
|
-
envExampleFile += `${key}=""\n`
|
|
70
|
-
|
|
68
|
+
if (!fs.existsSync(this.exampleFilepath)) {
|
|
69
|
+
// it doesn't exist so just write this first generated one
|
|
70
|
+
// exampleSrc - already written to from the prior loop
|
|
71
|
+
for (const key of [...keys]) {
|
|
72
|
+
// every key is added since it's the first time generating .env.example
|
|
71
73
|
addedKeys.add(key)
|
|
72
74
|
|
|
73
75
|
injected[key] = ''
|
|
74
76
|
}
|
|
77
|
+
} else {
|
|
78
|
+
// it already exists (which means the user might have it modified a way in which they prefer, so replace exampleSrc with their existing .env.example)
|
|
79
|
+
exampleSrc = fs.readFileSync(this.exampleFilepath, ENCODING)
|
|
80
|
+
|
|
81
|
+
const parsed = dotenv.parse(exampleSrc)
|
|
82
|
+
for (const key of [...keys]) {
|
|
83
|
+
if (key in parsed) {
|
|
84
|
+
preExisted[key] = parsed[key]
|
|
85
|
+
} else {
|
|
86
|
+
exampleSrc += `${key}=""\n`
|
|
87
|
+
|
|
88
|
+
addedKeys.add(key)
|
|
89
|
+
|
|
90
|
+
injected[key] = ''
|
|
91
|
+
}
|
|
92
|
+
}
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
return {
|
|
78
|
-
envExampleFile,
|
|
96
|
+
envExampleFile: exampleSrc,
|
|
79
97
|
envFile: this.envFile,
|
|
80
|
-
exampleFilepath,
|
|
98
|
+
exampleFilepath: this.exampleFilepath,
|
|
81
99
|
addedKeys: [...addedKeys],
|
|
82
100
|
injected,
|
|
83
101
|
preExisted
|
package/src/lib/services/ls.js
CHANGED
|
@@ -15,15 +15,17 @@ class Ls {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
_filepaths () {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const exclude = picomatch(this.ignore)
|
|
19
|
+
const include = picomatch(this._patterns(), {
|
|
20
|
+
ignore: this.ignore
|
|
21
|
+
})
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
return new Fdir()
|
|
22
24
|
.withRelativePaths()
|
|
23
|
-
.exclude((dir, path) =>
|
|
24
|
-
.filter((path) =>
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
.exclude((dir, path) => exclude(path))
|
|
26
|
+
.filter((path) => include(path))
|
|
27
|
+
.crawl(this.cwd)
|
|
28
|
+
.sync()
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
_patterns () {
|
package/src/lib/services/run.js
CHANGED
|
@@ -63,12 +63,12 @@ class Run {
|
|
|
63
63
|
row.string = env
|
|
64
64
|
|
|
65
65
|
try {
|
|
66
|
-
const { parsed } = parseDecryptEvalExpand(env, null, this.processEnv)
|
|
67
|
-
|
|
66
|
+
const { parsed, processEnv, warnings } = parseDecryptEvalExpand(env, null, this.processEnv)
|
|
68
67
|
row.parsed = parsed
|
|
68
|
+
row.warnings = warnings
|
|
69
69
|
this.readableStrings.add(env)
|
|
70
70
|
|
|
71
|
-
const { injected, preExisted } = this._inject(
|
|
71
|
+
const { injected, preExisted } = this._inject(processEnv, parsed, this.overload, this.processEnv)
|
|
72
72
|
row.injected = injected
|
|
73
73
|
row.preExisted = preExisted
|
|
74
74
|
|
|
@@ -94,10 +94,11 @@ class Run {
|
|
|
94
94
|
|
|
95
95
|
// if DOTENV_PRIVATE_KEY_* already set in process.env then use it
|
|
96
96
|
const privateKey = smartDotenvPrivateKey(envFilepath)
|
|
97
|
-
const { parsed } = parseDecryptEvalExpand(src, privateKey, this.processEnv)
|
|
97
|
+
const { parsed, processEnv, warnings } = parseDecryptEvalExpand(src, privateKey, this.processEnv)
|
|
98
98
|
row.parsed = parsed
|
|
99
|
+
row.warnings = warnings
|
|
99
100
|
|
|
100
|
-
const { injected, preExisted } = this._inject(
|
|
101
|
+
const { injected, preExisted } = this._inject(processEnv, parsed, this.overload, this.processEnv)
|
|
101
102
|
row.injected = injected
|
|
102
103
|
row.preExisted = preExisted
|
|
103
104
|
|
|
@@ -163,10 +164,11 @@ class Run {
|
|
|
163
164
|
|
|
164
165
|
try {
|
|
165
166
|
// parse this. it's the equivalent of the .env file
|
|
166
|
-
const { parsed } = parseDecryptEvalExpand(decrypted, null, this.processEnv)
|
|
167
|
+
const { parsed, processEnv, warnings } = parseDecryptEvalExpand(decrypted, null, this.processEnv)
|
|
167
168
|
row.parsed = parsed
|
|
169
|
+
row.warnings = warnings
|
|
168
170
|
|
|
169
|
-
const { injected, preExisted } = this._inject(
|
|
171
|
+
const { injected, preExisted } = this._inject(processEnv, parsed, this.overload, this.processEnv)
|
|
170
172
|
row.injected = injected
|
|
171
173
|
row.preExisted = preExisted
|
|
172
174
|
|
|
@@ -180,8 +182,8 @@ class Run {
|
|
|
180
182
|
this.processedEnvs.push(row)
|
|
181
183
|
}
|
|
182
184
|
|
|
183
|
-
_inject (
|
|
184
|
-
return inject(
|
|
185
|
+
_inject (clonedProcessEnv, parsed, overload, processEnv) {
|
|
186
|
+
return inject(clonedProcessEnv, parsed, overload, processEnv)
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
_determineEnvsFromDotenvPrivateKey () {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const diff = require('diff')
|
|
4
|
-
const chalk = require('chalk')
|
|
5
4
|
|
|
6
5
|
const Ls = require('./ls')
|
|
7
6
|
const VaultDecrypt = require('./vaultDecrypt')
|
|
8
7
|
|
|
9
8
|
const containsDirectory = require('./../helpers/containsDirectory')
|
|
10
9
|
const guessEnvironment = require('./../helpers/guessEnvironment')
|
|
10
|
+
const { getColor } = require('./../../shared/colors')
|
|
11
11
|
|
|
12
12
|
const ENCODING = 'utf8'
|
|
13
13
|
|
|
@@ -94,12 +94,12 @@ class Status {
|
|
|
94
94
|
_colorizeDiff (part) {
|
|
95
95
|
// If the part was added, color it green
|
|
96
96
|
if (part.added) {
|
|
97
|
-
return
|
|
97
|
+
return getColor('green')(part.value)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// If the part was removed, color it red
|
|
101
101
|
if (part.removed) {
|
|
102
|
-
return
|
|
102
|
+
return getColor('red')(part.value)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// No color for unchanged parts
|
|
@@ -23,7 +23,7 @@ class VaultDecrypt {
|
|
|
23
23
|
if (!fs.existsSync(this.envVaultFilepath)) {
|
|
24
24
|
const code = 'MISSING_ENV_VAULT_FILE'
|
|
25
25
|
const message = `missing .env.vault (${this.envVaultFilepath})`
|
|
26
|
-
const help = `? generate one with [dotenvx encrypt ${this.directory}]`
|
|
26
|
+
const help = `? generate one with [dotenvx ext vault encrypt ${this.directory}]`
|
|
27
27
|
|
|
28
28
|
const error = new Error(message)
|
|
29
29
|
error.code = code
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const depth = require('../lib/helpers/colorDepth')
|
|
2
|
+
|
|
3
|
+
const colors16 = new Map([
|
|
4
|
+
['blue', 34],
|
|
5
|
+
['gray', 37],
|
|
6
|
+
['green', 32],
|
|
7
|
+
['olive', 33],
|
|
8
|
+
['orangered', 31], // mapped to red
|
|
9
|
+
['plum', 35], // mapped to magenta
|
|
10
|
+
['red', 31]
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
const colors256 = new Map([
|
|
14
|
+
['blue', 21],
|
|
15
|
+
['gray', 244],
|
|
16
|
+
['green', 34],
|
|
17
|
+
['olive', 142],
|
|
18
|
+
['orangered', 202],
|
|
19
|
+
['plum', 182],
|
|
20
|
+
['red', 196]
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
function getColor (color) {
|
|
24
|
+
const colorDepth = depth.getColorDepth()
|
|
25
|
+
if (!colors256.has(color)) {
|
|
26
|
+
throw new Error(`Invalid color ${color}`)
|
|
27
|
+
}
|
|
28
|
+
if (colorDepth >= 8) {
|
|
29
|
+
const code = colors256.get(color)
|
|
30
|
+
return (message) => `\x1b[38;5;${code}m${message}\x1b[39m`
|
|
31
|
+
}
|
|
32
|
+
if (colorDepth >= 4) {
|
|
33
|
+
const code = colors16.get(color)
|
|
34
|
+
return (message) => `\x1b[${code}m${message}\x1b[39m`
|
|
35
|
+
}
|
|
36
|
+
return (message) => message
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function bold (message) {
|
|
40
|
+
if (depth.getColorDepth() >= 4) {
|
|
41
|
+
return `\x1b[1m${message}\x1b[22m`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return message
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
getColor,
|
|
49
|
+
bold
|
|
50
|
+
}
|
package/src/shared/logger.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const printf = winston.format.printf
|
|
5
|
-
const combine = winston.format.combine
|
|
6
|
-
const createLogger = winston.createLogger
|
|
7
|
-
const transports = winston.transports
|
|
8
|
-
|
|
9
|
-
const packageJson = require('./../lib/helpers/packageJson')
|
|
1
|
+
const packageJson = require('../lib/helpers/packageJson')
|
|
2
|
+
const { getColor, bold } = require('./colors')
|
|
10
3
|
|
|
11
4
|
const levels = {
|
|
12
5
|
error: 0,
|
|
@@ -26,26 +19,38 @@ const levels = {
|
|
|
26
19
|
help: 2,
|
|
27
20
|
help2: 2,
|
|
28
21
|
blank: 2,
|
|
29
|
-
http: 3,
|
|
30
22
|
verbose: 4,
|
|
31
23
|
debug: 5,
|
|
32
24
|
silly: 6
|
|
33
25
|
}
|
|
34
26
|
|
|
35
|
-
const error =
|
|
36
|
-
const warn =
|
|
37
|
-
const success =
|
|
38
|
-
const successv =
|
|
39
|
-
const help =
|
|
40
|
-
const help2 =
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
27
|
+
const error = (m) => bold(getColor('red')(m))
|
|
28
|
+
const warn = getColor('orangered')
|
|
29
|
+
const success = getColor('green')
|
|
30
|
+
const successv = getColor('olive') // yellow-ish tint that 'looks' like dotenv
|
|
31
|
+
const help = getColor('blue')
|
|
32
|
+
const help2 = getColor('gray')
|
|
33
|
+
const verbose = getColor('plum')
|
|
34
|
+
const debug = getColor('plum')
|
|
35
|
+
|
|
36
|
+
let currentLevel = levels.info // default log level
|
|
37
|
+
|
|
38
|
+
function log (level, message) {
|
|
39
|
+
if (levels[level] === undefined) {
|
|
40
|
+
throw new Error(`MISSING_LOG_LEVEL: '${level}'. implement in logger.`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (levels[level] <= currentLevel) {
|
|
44
|
+
const formattedMessage = formatMessage(level, message)
|
|
45
|
+
console.log(formattedMessage)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
function formatMessage (level, message) {
|
|
46
50
|
const formattedMessage = typeof message === 'object' ? JSON.stringify(message) : message
|
|
47
51
|
|
|
48
52
|
switch (level.toLowerCase()) {
|
|
53
|
+
// errors
|
|
49
54
|
case 'error':
|
|
50
55
|
return error(formattedMessage)
|
|
51
56
|
case 'errorv':
|
|
@@ -56,6 +61,7 @@ const dotenvxFormat = printf(({ level, message, label, timestamp }) => {
|
|
|
56
61
|
return error(`[dotenvx@${packageJson.version}][prebuild] ${formattedMessage}`)
|
|
57
62
|
case 'errornocolor':
|
|
58
63
|
return formattedMessage
|
|
64
|
+
// warns
|
|
59
65
|
case 'warn':
|
|
60
66
|
return warn(formattedMessage)
|
|
61
67
|
case 'warnv':
|
|
@@ -64,6 +70,7 @@ const dotenvxFormat = printf(({ level, message, label, timestamp }) => {
|
|
|
64
70
|
return warn(`[dotenvx@${packageJson.version}][precommit] ${formattedMessage}`)
|
|
65
71
|
case 'warnvpb':
|
|
66
72
|
return warn(`[dotenvx@${packageJson.version}][prebuild] ${formattedMessage}`)
|
|
73
|
+
// successes
|
|
67
74
|
case 'success':
|
|
68
75
|
return success(formattedMessage)
|
|
69
76
|
case 'successv': // success with 'version'
|
|
@@ -72,35 +79,66 @@ const dotenvxFormat = printf(({ level, message, label, timestamp }) => {
|
|
|
72
79
|
return success(`[dotenvx@${packageJson.version}][precommit] ${formattedMessage}`)
|
|
73
80
|
case 'successvpb': // success with 'version' and precommit
|
|
74
81
|
return success(`[dotenvx@${packageJson.version}][prebuild] ${formattedMessage}`)
|
|
82
|
+
// info
|
|
75
83
|
case 'info':
|
|
76
84
|
return formattedMessage
|
|
85
|
+
// help
|
|
77
86
|
case 'help':
|
|
78
87
|
return help(formattedMessage)
|
|
79
88
|
case 'help2':
|
|
80
89
|
return help2(formattedMessage)
|
|
81
|
-
|
|
82
|
-
return http(formattedMessage)
|
|
90
|
+
// verbose
|
|
83
91
|
case 'verbose':
|
|
84
92
|
return verbose(formattedMessage)
|
|
93
|
+
// debug
|
|
85
94
|
case 'debug':
|
|
86
95
|
return debug(formattedMessage)
|
|
96
|
+
// blank
|
|
87
97
|
case 'blank': // custom
|
|
88
98
|
return formattedMessage
|
|
89
99
|
}
|
|
90
|
-
}
|
|
100
|
+
}
|
|
91
101
|
|
|
92
|
-
const logger =
|
|
102
|
+
const logger = {
|
|
103
|
+
// track level
|
|
93
104
|
level: 'info',
|
|
94
|
-
levels,
|
|
95
|
-
format: combine(
|
|
96
|
-
dotenvxFormat
|
|
97
|
-
),
|
|
98
|
-
transports: [
|
|
99
|
-
new transports.Console()
|
|
100
|
-
]
|
|
101
|
-
})
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
// errors
|
|
107
|
+
error: (msg) => log('error', msg),
|
|
108
|
+
errorv: (msg) => log('errorv', msg),
|
|
109
|
+
errorvp: (msg) => log('errorvp', msg),
|
|
110
|
+
errorvpb: (msg) => log('errorvpb', msg),
|
|
111
|
+
errornocolor: (msg) => log('errornocolor', msg),
|
|
112
|
+
// warns
|
|
113
|
+
warn: (msg) => log('warn', msg),
|
|
114
|
+
warnv: (msg) => log('warnv', msg),
|
|
115
|
+
warnvp: (msg) => log('warnvp', msg),
|
|
116
|
+
warnvpb: (msg) => log('warnvpb', msg),
|
|
117
|
+
// success
|
|
118
|
+
success: (msg) => log('success', msg),
|
|
119
|
+
successv: (msg) => log('successv', msg),
|
|
120
|
+
successvp: (msg) => log('successvp', msg),
|
|
121
|
+
successvpb: (msg) => log('successvpb', msg),
|
|
122
|
+
// info
|
|
123
|
+
info: (msg) => log('info', msg),
|
|
124
|
+
// help
|
|
125
|
+
help: (msg) => log('help', msg),
|
|
126
|
+
help2: (msg) => log('help2', msg),
|
|
127
|
+
// verbose
|
|
128
|
+
verbose: (msg) => log('verbose', msg),
|
|
129
|
+
// debug
|
|
130
|
+
debug: (msg) => log('debug', msg),
|
|
131
|
+
// blank
|
|
132
|
+
blank: (msg) => log('blank', msg),
|
|
133
|
+
setLevel: (level) => {
|
|
134
|
+
if (levels[level] !== undefined) {
|
|
135
|
+
currentLevel = levels[level]
|
|
136
|
+
logger.level = level
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function setLogLevel (options) {
|
|
104
142
|
const logLevel = options.debug
|
|
105
143
|
? 'debug'
|
|
106
144
|
: options.verbose
|
|
@@ -110,7 +148,7 @@ const setLogLevel = options => {
|
|
|
110
148
|
: options.logLevel
|
|
111
149
|
|
|
112
150
|
if (!logLevel) return
|
|
113
|
-
logger.
|
|
151
|
+
logger.setLevel(logLevel)
|
|
114
152
|
// Only log which level it's setting if it's not set to quiet mode
|
|
115
153
|
if (!options.quiet || (options.quiet && logLevel !== 'error')) {
|
|
116
154
|
logger.debug(`Setting log level to ${logLevel}`)
|
|
@@ -119,5 +157,7 @@ const setLogLevel = options => {
|
|
|
119
157
|
|
|
120
158
|
module.exports = {
|
|
121
159
|
logger,
|
|
122
|
-
|
|
160
|
+
getColor,
|
|
161
|
+
setLogLevel,
|
|
162
|
+
levels
|
|
123
163
|
}
|