@dotenvx/dotenvx 1.57.0 → 1.57.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 +16 -2
- package/README.md +18 -7
- package/package.json +1 -1
- package/src/cli/actions/decrypt.js +2 -15
- package/src/cli/actions/encrypt.js +1 -9
- package/src/cli/actions/ext/genexample.js +2 -7
- package/src/cli/actions/ext/prebuild.js +3 -9
- package/src/cli/actions/ext/precommit.js +3 -9
- package/src/cli/actions/ext/scan.js +2 -2
- package/src/cli/actions/get.js +3 -8
- package/src/cli/actions/rotate.js +1 -9
- package/src/cli/actions/run.js +5 -15
- package/src/cli/actions/set.js +1 -9
- package/src/cli/dotenvx.js +1 -2
- package/src/lib/helpers/catchAndLog.js +1 -4
- package/src/lib/helpers/conventions.js +3 -1
- package/src/lib/helpers/cryptography/decryptKeyValue.js +1 -1
- package/src/lib/helpers/errors.js +187 -32
- package/src/lib/helpers/executeCommand.js +2 -1
- package/src/lib/helpers/executeDynamic.js +45 -17
- package/src/lib/helpers/findEnvFiles.js +2 -4
- package/src/lib/helpers/installPrecommitHook.js +2 -2
- package/src/lib/main.js +8 -29
- package/src/lib/services/encrypt.js +6 -1
- package/src/lib/services/genexample.js +1 -8
- package/src/lib/services/prebuild.js +12 -8
- package/src/lib/services/precommit.js +12 -8
- package/src/lib/services/rotate.js +6 -1
- package/src/lib/services/sets.js +5 -1
- package/src/shared/colors.js +8 -5
- package/src/shared/logger.js +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,22 @@
|
|
|
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.57.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.57.1...main)
|
|
6
|
+
|
|
7
|
+
## [1.57.1](https://github.com/dotenvx/dotenvx/compare/v1.57.0...v1.57.1) (2026-03-21)
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
|
|
11
|
+
* improved error logs and compacted most to a single line ([#755](https://github.com/dotenvx/dotenvx/pull/755))
|
|
12
|
+
* introduced leading log glyphs as a visual status language:
|
|
13
|
+
|
|
14
|
+
* `◈` success action (encrypted)
|
|
15
|
+
* `◇` success action (set plain value, decrypted)
|
|
16
|
+
* `⟳` success action (rotated)
|
|
17
|
+
* `○` informational no-op (no changes)
|
|
18
|
+
* `▣` success action for generated/updated support files
|
|
19
|
+
* `⚠` warning
|
|
20
|
+
* `☠` error
|
|
6
21
|
|
|
7
22
|
## [1.57.0](https://github.com/dotenvx/dotenvx/compare/v1.56.0...v1.57.0) (2026-03-19)
|
|
8
23
|
|
|
@@ -1783,4 +1798,3 @@ prevent committing a `.env*` file to code. append to `.gitignore`, `.dockerignor
|
|
|
1783
1798
|
## 0.3.9 and prior
|
|
1784
1799
|
|
|
1785
1800
|
Please see commit history.
|
|
1786
|
-
|
package/README.md
CHANGED
|
@@ -30,7 +30,20 @@ console.log(`Hello ${process.env.HELLO}`)
|
|
|
30
30
|
|
|
31
31
|
or install globally - *unlocks dotenv for any language, framework, or platform!*
|
|
32
32
|
|
|
33
|
-
<details><summary>with
|
|
33
|
+
<details><summary>with npm 🌍</summary><br>
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm i -g @dotenvx/dotenvx
|
|
37
|
+
dotenvx help
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
[](https://npmjs.com/@dotenvx/dotenvx)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
<details><summary>with curl 🌐</summary><br>
|
|
34
47
|
|
|
35
48
|
```sh
|
|
36
49
|
curl -sfS https://dotenvx.sh | sh
|
|
@@ -936,8 +949,7 @@ For example, when missing a custom .env file:
|
|
|
936
949
|
|
|
937
950
|
```sh
|
|
938
951
|
$ dotenvx run -f .env.missing -- echo $HELLO
|
|
939
|
-
[MISSING_ENV_FILE] missing
|
|
940
|
-
[MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484 and re-run [dotenvx run -- echo]
|
|
952
|
+
[MISSING_ENV_FILE] missing file (/Users/scottmotte/Code/dotenvx/playground/apr-16/.env.missing). fix: [echo "HELLO=World" > .env.missing]
|
|
941
953
|
```
|
|
942
954
|
|
|
943
955
|
or when missing a KEY:
|
|
@@ -945,7 +957,7 @@ or when missing a KEY:
|
|
|
945
957
|
```sh
|
|
946
958
|
$ echo "HELLO=World" > .env
|
|
947
959
|
$ dotenvx get GOODBYE
|
|
948
|
-
[MISSING_KEY] missing GOODBYE
|
|
960
|
+
[MISSING_KEY] missing key (GOODBYE)
|
|
949
961
|
```
|
|
950
962
|
|
|
951
963
|
</details>
|
|
@@ -1172,8 +1184,7 @@ Exit with code `1` if any errors are encountered - like a missing .env file or d
|
|
|
1172
1184
|
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
|
1173
1185
|
|
|
1174
1186
|
$ dotenvx run -f .env.missing --strict -- node index.js
|
|
1175
|
-
[MISSING_ENV_FILE] missing
|
|
1176
|
-
[MISSING_ENV_FILE] ? add one with [echo "HELLO=World" > .env.missing]
|
|
1187
|
+
[MISSING_ENV_FILE] missing file (/path/to/.env.missing). fix: [echo "HELLO=World" > .env.missing]
|
|
1177
1188
|
```
|
|
1178
1189
|
|
|
1179
1190
|
This can be useful in `ci` scripts where you want to fail the ci if your `.env` file could not be decrypted at runtime.
|
|
@@ -1326,7 +1337,7 @@ Exit with code `1` if any errors are encountered - like a missing key, missing .
|
|
|
1326
1337
|
|
|
1327
1338
|
```sh
|
|
1328
1339
|
$ dotenvx get DOES_NOT_EXIST --strict
|
|
1329
|
-
[MISSING_KEY] missing DOES_NOT_EXIST
|
|
1340
|
+
[MISSING_KEY] missing key (DOES_NOT_EXIST)
|
|
1330
1341
|
```
|
|
1331
1342
|
|
|
1332
1343
|
</details>
|
package/package.json
CHANGED
|
@@ -2,7 +2,6 @@ const fsx = require('./../../lib/helpers/fsx')
|
|
|
2
2
|
const { logger } = require('./../../shared/logger')
|
|
3
3
|
|
|
4
4
|
const Decrypt = require('./../../lib/services/decrypt')
|
|
5
|
-
|
|
6
5
|
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
7
6
|
|
|
8
7
|
function decrypt () {
|
|
@@ -23,10 +22,7 @@ function decrypt () {
|
|
|
23
22
|
for (const processedEnv of processedEnvs) {
|
|
24
23
|
if (processedEnv.error) {
|
|
25
24
|
errorCount += 1
|
|
26
|
-
logger.error(processedEnv.error.
|
|
27
|
-
if (processedEnv.error.help) {
|
|
28
|
-
logger.error(processedEnv.error.help)
|
|
29
|
-
}
|
|
25
|
+
logger.error(processedEnv.error.messageWithHelp)
|
|
30
26
|
} else {
|
|
31
27
|
console.log(processedEnv.envSrc)
|
|
32
28
|
}
|
|
@@ -50,16 +46,7 @@ function decrypt () {
|
|
|
50
46
|
|
|
51
47
|
if (processedEnv.error) {
|
|
52
48
|
errorCount += 1
|
|
53
|
-
|
|
54
|
-
if (processedEnv.error.code === 'MISSING_ENV_FILE') {
|
|
55
|
-
logger.error(processedEnv.error.message)
|
|
56
|
-
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx decrypt]`)
|
|
57
|
-
} else {
|
|
58
|
-
logger.error(processedEnv.error.message)
|
|
59
|
-
if (processedEnv.error.help) {
|
|
60
|
-
logger.error(processedEnv.error.help)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
49
|
+
logger.error(processedEnv.error.messageWithHelp)
|
|
63
50
|
} else if (processedEnv.changed) {
|
|
64
51
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
65
52
|
|
|
@@ -34,15 +34,7 @@ function encrypt () {
|
|
|
34
34
|
for (const processedEnv of processedEnvs) {
|
|
35
35
|
logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
36
36
|
if (processedEnv.error) {
|
|
37
|
-
|
|
38
|
-
logger.warn(processedEnv.error.message)
|
|
39
|
-
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx encrypt]`)
|
|
40
|
-
} else {
|
|
41
|
-
logger.warn(processedEnv.error.message)
|
|
42
|
-
if (processedEnv.error.help) {
|
|
43
|
-
logger.help(processedEnv.error.help)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
37
|
+
logger.warn(processedEnv.error.messageWithHelp)
|
|
46
38
|
} else if (processedEnv.changed) {
|
|
47
39
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
48
40
|
|
|
@@ -2,6 +2,7 @@ const fsx = require('./../../../lib/helpers/fsx')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const main = require('./../../../lib/main')
|
|
4
4
|
const { logger } = require('./../../../shared/logger')
|
|
5
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
5
6
|
|
|
6
7
|
function genexample (directory) {
|
|
7
8
|
logger.debug(`directory: ${directory}`)
|
|
@@ -27,13 +28,7 @@ function genexample (directory) {
|
|
|
27
28
|
logger.info('○ no changes (.env.example)')
|
|
28
29
|
}
|
|
29
30
|
} catch (error) {
|
|
30
|
-
|
|
31
|
-
if (error.help) {
|
|
32
|
-
logger.help(error.help)
|
|
33
|
-
}
|
|
34
|
-
if (error.code) {
|
|
35
|
-
logger.debug(`ERROR_CODE: ${error.code}`)
|
|
36
|
-
}
|
|
31
|
+
catchAndLog(error)
|
|
37
32
|
process.exit(1)
|
|
38
33
|
}
|
|
39
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { logger } = require('./../../../shared/logger')
|
|
2
2
|
|
|
3
3
|
const Prebuild = require('./../../../lib/services/prebuild')
|
|
4
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
4
5
|
|
|
5
6
|
function prebuild (directory) {
|
|
6
7
|
// debug args
|
|
@@ -16,19 +17,12 @@ function prebuild (directory) {
|
|
|
16
17
|
} = new Prebuild(directory, options).run()
|
|
17
18
|
|
|
18
19
|
for (const warning of warnings) {
|
|
19
|
-
logger.warn(warning.
|
|
20
|
-
if (warning.help) {
|
|
21
|
-
logger.help(warning.help)
|
|
22
|
-
}
|
|
20
|
+
logger.warn(warning.messageWithHelp)
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
logger.success(successMessage)
|
|
26
24
|
} catch (error) {
|
|
27
|
-
|
|
28
|
-
if (error.help) {
|
|
29
|
-
logger.help(error.help)
|
|
30
|
-
}
|
|
31
|
-
|
|
25
|
+
catchAndLog(error)
|
|
32
26
|
process.exit(1)
|
|
33
27
|
}
|
|
34
28
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { logger } = require('./../../../shared/logger')
|
|
2
2
|
|
|
3
3
|
const Precommit = require('./../../../lib/services/precommit')
|
|
4
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
4
5
|
|
|
5
6
|
function precommit (directory) {
|
|
6
7
|
// debug args
|
|
@@ -16,19 +17,12 @@ function precommit (directory) {
|
|
|
16
17
|
} = new Precommit(directory, options).run()
|
|
17
18
|
|
|
18
19
|
for (const warning of warnings) {
|
|
19
|
-
logger.warn(warning.
|
|
20
|
-
if (warning.help) {
|
|
21
|
-
logger.help(warning.help)
|
|
22
|
-
}
|
|
20
|
+
logger.warn(warning.messageWithHelp)
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
logger.success(successMessage)
|
|
26
24
|
} catch (error) {
|
|
27
|
-
|
|
28
|
-
if (error.help) {
|
|
29
|
-
logger.help(error.help)
|
|
30
|
-
}
|
|
31
|
-
|
|
25
|
+
catchAndLog(error)
|
|
32
26
|
process.exit(1)
|
|
33
27
|
}
|
|
34
28
|
}
|
|
@@ -12,8 +12,8 @@ function scan () {
|
|
|
12
12
|
childProcess.execSync('gitleaks version', { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
13
13
|
} catch (error) {
|
|
14
14
|
logger.error('gitleaks: command not found')
|
|
15
|
-
logger.help('
|
|
16
|
-
logger.help('
|
|
15
|
+
logger.help('fix: install gitleaks: [brew install gitleaks]')
|
|
16
|
+
logger.help('fix: other install options: [https://github.com/gitleaks/gitleaks]')
|
|
17
17
|
process.exit(1)
|
|
18
18
|
return
|
|
19
19
|
}
|
package/src/cli/actions/get.js
CHANGED
|
@@ -2,6 +2,7 @@ const { logger } = require('./../../shared/logger')
|
|
|
2
2
|
|
|
3
3
|
const conventions = require('./../../lib/helpers/conventions')
|
|
4
4
|
const escape = require('./../../lib/helpers/escape')
|
|
5
|
+
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
5
6
|
|
|
6
7
|
const Get = require('./../../lib/services/get')
|
|
7
8
|
|
|
@@ -35,10 +36,7 @@ function get (key) {
|
|
|
35
36
|
continue // ignore error
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
logger.error(error.
|
|
39
|
-
if (error.help) {
|
|
40
|
-
logger.error(error.help)
|
|
41
|
-
}
|
|
39
|
+
logger.error(error.messageWithHelp)
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
if (key) {
|
|
@@ -75,10 +73,7 @@ function get (key) {
|
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
} catch (error) {
|
|
78
|
-
|
|
79
|
-
if (error.help) {
|
|
80
|
-
logger.error(error.help)
|
|
81
|
-
}
|
|
76
|
+
catchAndLog(error)
|
|
82
77
|
process.exit(1)
|
|
83
78
|
}
|
|
84
79
|
}
|
|
@@ -38,15 +38,7 @@ function rotate () {
|
|
|
38
38
|
for (const processedEnv of processedEnvs) {
|
|
39
39
|
logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
40
40
|
if (processedEnv.error) {
|
|
41
|
-
|
|
42
|
-
logger.warn(processedEnv.error.message)
|
|
43
|
-
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx rotate]`)
|
|
44
|
-
} else {
|
|
45
|
-
logger.warn(processedEnv.error.message)
|
|
46
|
-
if (processedEnv.error.help) {
|
|
47
|
-
logger.help(processedEnv.error.help)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
41
|
+
logger.warn(processedEnv.error.messageWithHelp)
|
|
50
42
|
} else if (processedEnv.changed) {
|
|
51
43
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
52
44
|
if (processedEnv.privateKeyAdded) {
|
package/src/cli/actions/run.js
CHANGED
|
@@ -3,6 +3,7 @@ const { logger } = require('./../../shared/logger')
|
|
|
3
3
|
|
|
4
4
|
const executeCommand = require('./../../lib/helpers/executeCommand')
|
|
5
5
|
const Run = require('./../../lib/services/run')
|
|
6
|
+
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
6
7
|
|
|
7
8
|
const conventions = require('./../../lib/helpers/conventions')
|
|
8
9
|
|
|
@@ -64,18 +65,10 @@ async function run () {
|
|
|
64
65
|
|
|
65
66
|
if (options.strict) throw error // throw if strict and not ignored
|
|
66
67
|
|
|
67
|
-
if (error.code === 'MISSING_ENV_FILE') {
|
|
68
|
-
|
|
69
|
-
logger.error(error.message)
|
|
70
|
-
if (error.help) {
|
|
71
|
-
logger.error(`${error.help} and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
68
|
+
if (error.code === 'MISSING_ENV_FILE' && options.convention) { // do not output error for conventions (too noisy)
|
|
69
|
+
// intentionally quiet
|
|
74
70
|
} else {
|
|
75
|
-
logger.error(error.
|
|
76
|
-
if (error.help) {
|
|
77
|
-
logger.error(error.help)
|
|
78
|
-
}
|
|
71
|
+
logger.error(error.messageWithHelp)
|
|
79
72
|
}
|
|
80
73
|
}
|
|
81
74
|
|
|
@@ -106,10 +99,7 @@ async function run () {
|
|
|
106
99
|
|
|
107
100
|
logger.successv(msg)
|
|
108
101
|
} catch (error) {
|
|
109
|
-
|
|
110
|
-
if (error.help) {
|
|
111
|
-
logger.error(error.help)
|
|
112
|
-
}
|
|
102
|
+
catchAndLog(error)
|
|
113
103
|
process.exit(1)
|
|
114
104
|
}
|
|
115
105
|
|
package/src/cli/actions/set.js
CHANGED
|
@@ -40,15 +40,7 @@ function set (key, value) {
|
|
|
40
40
|
logger.verbose(`setting for ${processedEnv.envFilepath}`)
|
|
41
41
|
|
|
42
42
|
if (processedEnv.error) {
|
|
43
|
-
|
|
44
|
-
logger.warn(processedEnv.error.message)
|
|
45
|
-
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx set]`)
|
|
46
|
-
} else {
|
|
47
|
-
logger.warn(processedEnv.error.message)
|
|
48
|
-
if (processedEnv.error.help) {
|
|
49
|
-
logger.help(processedEnv.error.help)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
43
|
+
logger.warn(processedEnv.error.messageWithHelp)
|
|
52
44
|
} else {
|
|
53
45
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
54
46
|
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -30,8 +30,7 @@ const commanderVersion = getCommanderVersion()
|
|
|
30
30
|
if (commanderVersion && parseInt(commanderVersion.split('.')[0], 10) >= 12) {
|
|
31
31
|
const message = `dotenvx depends on commander@11.x.x but you are attempting to hoist commander@${commanderVersion}`
|
|
32
32
|
const error = new Errors({ message }).dangerousDependencyHoist()
|
|
33
|
-
logger.error(error.
|
|
34
|
-
if (error.help) logger.error(error.help)
|
|
33
|
+
logger.error(error.messageWithHelp)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// global log levels
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const Errors = require('./errors')
|
|
2
|
+
|
|
1
3
|
function conventions (convention) {
|
|
2
4
|
const env = process.env.DOTENV_ENV || process.env.NODE_ENV || 'development'
|
|
3
5
|
|
|
@@ -19,7 +21,7 @@ function conventions (convention) {
|
|
|
19
21
|
{ type: 'envFile', value: '.env.defaults' }
|
|
20
22
|
]
|
|
21
23
|
} else {
|
|
22
|
-
throw new
|
|
24
|
+
throw new Errors({ convention }).invalidConvention()
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -30,7 +30,7 @@ function decryptKeyValue (key, value, privateKeyName, privateKey) {
|
|
|
30
30
|
if (e.message === 'Invalid private key') {
|
|
31
31
|
decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
|
|
32
32
|
} else if (e.message === 'Unsupported state or unable to authenticate data') {
|
|
33
|
-
decryptionError = new Errors({ key, privateKeyName, privateKey }).
|
|
33
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey }).wrongPrivateKey()
|
|
34
34
|
} else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
|
|
35
35
|
decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
|
|
36
36
|
} else {
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
const truncate = require('./truncate')
|
|
2
2
|
|
|
3
|
+
const ISSUE_BY_CODE = {
|
|
4
|
+
COMMAND_EXITED_WITH_CODE: 'https://github.com/dotenvx/dotenvx/issues/new',
|
|
5
|
+
COMMAND_SUBSTITUTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/532',
|
|
6
|
+
DECRYPTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/757',
|
|
7
|
+
DANGEROUS_DEPENDENCY_HOIST: 'https://github.com/dotenvx/dotenvx/issues/622',
|
|
8
|
+
INVALID_COLOR: 'must be 256 colors',
|
|
9
|
+
INVALID_CONVENTION: 'https://github.com/dotenvx/dotenvx/issues/761',
|
|
10
|
+
INVALID_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/465',
|
|
11
|
+
INVALID_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/756',
|
|
12
|
+
MALFORMED_ENCRYPTED_DATA: 'https://github.com/dotenvx/dotenvx/issues/467',
|
|
13
|
+
MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
|
|
14
|
+
MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
|
|
15
|
+
MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
|
|
16
|
+
MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
|
|
17
|
+
MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
|
|
18
|
+
MISSING_LOG_LEVEL: 'must be valid log level',
|
|
19
|
+
MISSING_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/464',
|
|
20
|
+
PRECOMMIT_HOOK_MODIFY_FAILED: 'try again or report error',
|
|
21
|
+
WRONG_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/466'
|
|
22
|
+
}
|
|
23
|
+
|
|
3
24
|
class Errors {
|
|
4
25
|
constructor (options = {}) {
|
|
5
26
|
this.filepath = options.filepath
|
|
@@ -8,118 +29,252 @@ class Errors {
|
|
|
8
29
|
this.key = options.key
|
|
9
30
|
this.privateKey = options.privateKey
|
|
10
31
|
this.privateKeyName = options.privateKeyName
|
|
32
|
+
this.publicKeyName = options.publicKeyName
|
|
11
33
|
this.publicKey = options.publicKey
|
|
12
34
|
this.publicKeyExisting = options.publicKeyExisting
|
|
13
35
|
this.command = options.command
|
|
14
36
|
|
|
15
37
|
this.message = options.message
|
|
38
|
+
this.code = options.code
|
|
39
|
+
this.help = options.help
|
|
40
|
+
this.debug = options.debug
|
|
41
|
+
|
|
42
|
+
this.convention = options.convention
|
|
43
|
+
this.directory = options.directory
|
|
44
|
+
this.exitCode = options.exitCode
|
|
45
|
+
this.level = options.level
|
|
46
|
+
this.color = options.color
|
|
47
|
+
this.error = options.error
|
|
16
48
|
}
|
|
17
49
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
50
|
+
custom () {
|
|
51
|
+
const e = new Error(this.message)
|
|
52
|
+
if (this.code) e.code = this.code
|
|
53
|
+
if (this.help) e.help = this.help
|
|
54
|
+
if (this.code && !e.help) e.help = `fix: [${ISSUE_BY_CODE[this.code]}]`
|
|
55
|
+
e.messageWithHelp = `${e.message}. ${e.help}`
|
|
56
|
+
if (this.debug) e.debug = this.debug
|
|
57
|
+
return e
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
commandExitedWithCode () {
|
|
61
|
+
const code = 'COMMAND_EXITED_WITH_CODE'
|
|
62
|
+
const message = `[${code}] Command exited with exit code ${this.exitCode}`
|
|
63
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
22
64
|
|
|
23
65
|
const e = new Error(message)
|
|
24
66
|
e.code = code
|
|
25
67
|
e.help = help
|
|
68
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
26
69
|
return e
|
|
27
70
|
}
|
|
28
71
|
|
|
29
|
-
|
|
30
|
-
const code = '
|
|
31
|
-
const message = `[${code}]
|
|
72
|
+
commandSubstitutionFailed () {
|
|
73
|
+
const code = 'COMMAND_SUBSTITUTION_FAILED'
|
|
74
|
+
const message = `[${code}] could not eval ${this.key} containing command '${this.command}': ${this.message}`
|
|
75
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
32
76
|
|
|
33
77
|
const e = new Error(message)
|
|
34
78
|
e.code = code
|
|
79
|
+
e.help = help
|
|
80
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
35
81
|
return e
|
|
36
82
|
}
|
|
37
83
|
|
|
38
|
-
|
|
39
|
-
const code = '
|
|
40
|
-
const message = `[${code}]
|
|
41
|
-
const help = `[${code}]
|
|
84
|
+
decryptionFailed () {
|
|
85
|
+
const code = 'DECRYPTION_FAILED'
|
|
86
|
+
const message = `[${code}] ${this.message}`
|
|
87
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
42
88
|
|
|
43
89
|
const e = new Error(message)
|
|
44
90
|
e.code = code
|
|
45
91
|
e.help = help
|
|
92
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
93
|
+
return e
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
dangerousDependencyHoist () {
|
|
97
|
+
const code = 'DANGEROUS_DEPENDENCY_HOIST'
|
|
98
|
+
const message = `[${code}] your environment has hoisted an incompatible version of a dotenvx dependency: ${this.message}`
|
|
99
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
100
|
+
|
|
101
|
+
const e = new Error(message)
|
|
102
|
+
e.code = code
|
|
103
|
+
e.help = help
|
|
104
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
105
|
+
return e
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
invalidColor () {
|
|
109
|
+
const code = 'INVALID_COLOR'
|
|
110
|
+
const message = `[${code}] Invalid color ${this.color}`
|
|
111
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
112
|
+
|
|
113
|
+
const e = new Error(message)
|
|
114
|
+
e.code = code
|
|
115
|
+
e.help = help
|
|
116
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
117
|
+
return e
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
invalidConvention () {
|
|
121
|
+
const code = 'INVALID_CONVENTION'
|
|
122
|
+
const message = `[${code}] invalid convention (${this.convention})`
|
|
123
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
124
|
+
|
|
125
|
+
const e = new Error(message)
|
|
126
|
+
e.code = code
|
|
127
|
+
e.help = help
|
|
128
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
46
129
|
return e
|
|
47
130
|
}
|
|
48
131
|
|
|
49
132
|
invalidPrivateKey () {
|
|
50
133
|
const code = 'INVALID_PRIVATE_KEY'
|
|
51
134
|
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
52
|
-
const help = `[${code}]
|
|
135
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
53
136
|
|
|
54
137
|
const e = new Error(message)
|
|
55
138
|
e.code = code
|
|
56
139
|
e.help = help
|
|
140
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
141
|
+
return e
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
invalidPublicKey () {
|
|
145
|
+
const code = 'INVALID_PUBLIC_KEY'
|
|
146
|
+
const message = `[${code}] could not encrypt using public key '${this.publicKeyName}=${truncate(this.publicKey)}'`
|
|
147
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
148
|
+
|
|
149
|
+
const e = new Error(message)
|
|
150
|
+
e.code = code
|
|
151
|
+
e.help = help
|
|
152
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
153
|
+
return e
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
malformedEncryptedData () {
|
|
157
|
+
const code = 'MALFORMED_ENCRYPTED_DATA'
|
|
158
|
+
const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
|
|
159
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
160
|
+
|
|
161
|
+
const e = new Error(message)
|
|
162
|
+
e.code = code
|
|
163
|
+
e.help = help
|
|
164
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
57
165
|
return e
|
|
58
166
|
}
|
|
59
167
|
|
|
60
168
|
mispairedPrivateKey () {
|
|
61
169
|
const code = 'MISPAIRED_PRIVATE_KEY'
|
|
62
170
|
const message = `[${code}] private key's derived public key (${truncate(this.publicKey)}) does not match the existing public key (${truncate(this.publicKeyExisting)})`
|
|
63
|
-
const help = `[${code}]
|
|
171
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
64
172
|
|
|
65
173
|
const e = new Error(message)
|
|
66
174
|
e.code = code
|
|
67
175
|
e.help = help
|
|
176
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
68
177
|
return e
|
|
69
178
|
}
|
|
70
179
|
|
|
71
|
-
|
|
72
|
-
const code = '
|
|
73
|
-
const message = `[${code}]
|
|
74
|
-
const help = `[${code}]
|
|
180
|
+
missingDirectory () {
|
|
181
|
+
const code = 'MISSING_DIRECTORY'
|
|
182
|
+
const message = `[${code}] missing directory (${this.directory})`
|
|
183
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
75
184
|
|
|
76
185
|
const e = new Error(message)
|
|
77
186
|
e.code = code
|
|
78
187
|
e.help = help
|
|
188
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
79
189
|
return e
|
|
80
190
|
}
|
|
81
191
|
|
|
82
|
-
|
|
83
|
-
const code = '
|
|
84
|
-
const
|
|
85
|
-
const
|
|
192
|
+
missingEnvFile () {
|
|
193
|
+
const code = 'MISSING_ENV_FILE'
|
|
194
|
+
const envFilepath = this.envFilepath || '.env'
|
|
195
|
+
const message = `[${code}] missing file (${envFilepath})`
|
|
196
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
86
197
|
|
|
87
198
|
const e = new Error(message)
|
|
88
199
|
e.code = code
|
|
89
200
|
e.help = help
|
|
201
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
90
202
|
return e
|
|
91
203
|
}
|
|
92
204
|
|
|
93
|
-
|
|
94
|
-
const code = '
|
|
95
|
-
const message =
|
|
205
|
+
missingEnvFiles () {
|
|
206
|
+
const code = 'MISSING_ENV_FILES'
|
|
207
|
+
const message = `[${code}] no .env* files found`
|
|
208
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
96
209
|
|
|
97
210
|
const e = new Error(message)
|
|
98
211
|
e.code = code
|
|
212
|
+
e.help = help
|
|
213
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
99
214
|
return e
|
|
100
215
|
}
|
|
101
216
|
|
|
102
|
-
|
|
103
|
-
const code = '
|
|
104
|
-
const message = `[${code}]
|
|
105
|
-
const help = `[${code}]
|
|
217
|
+
missingKey () {
|
|
218
|
+
const code = 'MISSING_KEY'
|
|
219
|
+
const message = `[${code}] missing key (${this.key})`
|
|
220
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
106
221
|
|
|
107
222
|
const e = new Error(message)
|
|
108
223
|
e.code = code
|
|
109
224
|
e.help = help
|
|
225
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
110
226
|
return e
|
|
111
227
|
}
|
|
112
228
|
|
|
113
|
-
|
|
114
|
-
const code = '
|
|
115
|
-
const message = `[${code}]
|
|
116
|
-
const help = `[${code}]
|
|
229
|
+
missingLogLevel () {
|
|
230
|
+
const code = 'MISSING_LOG_LEVEL'
|
|
231
|
+
const message = `[${code}] missing log level '${this.level}'. implement in logger`
|
|
232
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
233
|
+
|
|
234
|
+
const e = new Error(message)
|
|
235
|
+
e.code = code
|
|
236
|
+
e.help = help
|
|
237
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
238
|
+
return e
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
missingPrivateKey () {
|
|
242
|
+
const code = 'MISSING_PRIVATE_KEY'
|
|
243
|
+
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
244
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
245
|
+
|
|
246
|
+
const e = new Error(message)
|
|
247
|
+
e.code = code
|
|
248
|
+
e.help = help
|
|
249
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
250
|
+
return e
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
precommitHookModifyFailed () {
|
|
254
|
+
const code = 'PRECOMMIT_HOOK_MODIFY_FAILED'
|
|
255
|
+
const message = `[${code}] failed to modify pre-commit hook: ${this.error.message}`
|
|
256
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
117
257
|
|
|
118
258
|
const e = new Error(message)
|
|
119
259
|
e.code = code
|
|
120
260
|
e.help = help
|
|
261
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
262
|
+
return e
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
wrongPrivateKey () {
|
|
266
|
+
const code = 'WRONG_PRIVATE_KEY'
|
|
267
|
+
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
268
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
269
|
+
|
|
270
|
+
const e = new Error(message)
|
|
271
|
+
e.code = code
|
|
272
|
+
e.help = help
|
|
273
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
121
274
|
return e
|
|
122
275
|
}
|
|
123
276
|
}
|
|
124
277
|
|
|
278
|
+
Errors.ISSUE_BY_CODE = ISSUE_BY_CODE
|
|
279
|
+
|
|
125
280
|
module.exports = Errors
|
|
@@ -2,6 +2,7 @@ const path = require('path')
|
|
|
2
2
|
const which = require('which')
|
|
3
3
|
const execute = require('./../../lib/helpers/execute')
|
|
4
4
|
const { logger } = require('./../../shared/logger')
|
|
5
|
+
const Errors = require('./errors')
|
|
5
6
|
|
|
6
7
|
async function executeCommand (commandArgs, env) {
|
|
7
8
|
const signals = [
|
|
@@ -91,7 +92,7 @@ async function executeCommand (commandArgs, env) {
|
|
|
91
92
|
|
|
92
93
|
if (exitCode !== 0) {
|
|
93
94
|
logger.debug(`received exitCode ${exitCode}`)
|
|
94
|
-
throw new
|
|
95
|
+
throw new Errors({ exitCode }).commandExitedWithCode()
|
|
95
96
|
}
|
|
96
97
|
} catch (error) {
|
|
97
98
|
// no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
|
|
@@ -1,7 +1,50 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
2
3
|
const childProcess = require('child_process')
|
|
3
4
|
const { logger } = require('../../shared/logger')
|
|
4
5
|
|
|
6
|
+
function installCommandForOps () {
|
|
7
|
+
const userAgent = process.env.npm_config_user_agent || ''
|
|
8
|
+
if (userAgent.startsWith('pnpm/')) return 'pnpm add -g @dotenvx/dotenvx-ops'
|
|
9
|
+
if (userAgent.startsWith('yarn/')) return 'yarn global add @dotenvx/dotenvx-ops'
|
|
10
|
+
if (userAgent.startsWith('npm/')) return 'npm i -g @dotenvx/dotenvx-ops'
|
|
11
|
+
|
|
12
|
+
const cwd = process.cwd()
|
|
13
|
+
if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm add -g @dotenvx/dotenvx-ops'
|
|
14
|
+
if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn global add @dotenvx/dotenvx-ops'
|
|
15
|
+
if (
|
|
16
|
+
fs.existsSync(path.join(cwd, 'package-lock.json')) ||
|
|
17
|
+
fs.existsSync(path.join(cwd, 'npm-shrinkwrap.json'))
|
|
18
|
+
) return 'npm i -g @dotenvx/dotenvx-ops'
|
|
19
|
+
|
|
20
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) return 'npm i -g @dotenvx/dotenvx-ops'
|
|
21
|
+
|
|
22
|
+
return 'curl -sfS https://dotenvx.sh/ops | sh'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function opsBanner (installCommand) {
|
|
26
|
+
const lines = [
|
|
27
|
+
'',
|
|
28
|
+
' ██████╗ ██████╗ ███████╗',
|
|
29
|
+
' ██╔═══██╗██╔══██╗██╔════╝',
|
|
30
|
+
' ██║ ██║██████╔╝███████╗',
|
|
31
|
+
' ██║ ██║██╔═══╝ ╚════██║',
|
|
32
|
+
' ╚██████╔╝██║ ███████║',
|
|
33
|
+
' ╚═════╝ ╚═╝ ╚══════╝',
|
|
34
|
+
'',
|
|
35
|
+
' KEYS OFF COMPUTER: Add hardened key protection with dotenvx-ops.',
|
|
36
|
+
` Install now: [${installCommand}]`,
|
|
37
|
+
' Learn more: [https://dotenvx.com/ops]'
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const innerWidth = Math.max(67, ...lines.map((line) => line.length))
|
|
41
|
+
const top = ` ${'_'.repeat(innerWidth)}`
|
|
42
|
+
const middle = lines.map((line) => `|${line.padEnd(innerWidth)}|`).join('\n')
|
|
43
|
+
const bottom = `|${'_'.repeat(innerWidth)}|`
|
|
44
|
+
|
|
45
|
+
return `${top}\n${middle}\n${bottom}`
|
|
46
|
+
}
|
|
47
|
+
|
|
5
48
|
function executeDynamic (program, command, rawArgs) {
|
|
6
49
|
if (!command) {
|
|
7
50
|
program.outputHelp()
|
|
@@ -23,23 +66,8 @@ function executeDynamic (program, command, rawArgs) {
|
|
|
23
66
|
const result = childProcess.spawnSync(`dotenvx-${command}`, forwardedArgs, { stdio: 'inherit', env })
|
|
24
67
|
if (result.error) {
|
|
25
68
|
if (command === 'ops') {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
| ██████╗ ██████╗ ███████╗ |
|
|
29
|
-
| ██╔═══██╗██╔══██╗██╔════╝ |
|
|
30
|
-
| ██║ ██║██████╔╝███████╗ |
|
|
31
|
-
| ██║ ██║██╔═══╝ ╚════██║ |
|
|
32
|
-
| ╚██████╔╝██║ ███████║ |
|
|
33
|
-
| ╚═════╝ ╚═╝ ╚══════╝ |
|
|
34
|
-
| |
|
|
35
|
-
| Learn more at [https://dotenvx.com/ops] |
|
|
36
|
-
|___________________________________________________________________|`
|
|
37
|
-
|
|
38
|
-
console.log(ops)
|
|
39
|
-
console.log('')
|
|
40
|
-
logger.warn(`[INSTALLATION_NEEDED] install dotenvx-${command} to use [dotenvx ${command}] 🛡️`)
|
|
41
|
-
logger.help('⮕ next run: [curl -sfS https://dotenvx.sh/ops | sh]')
|
|
42
|
-
logger.help('⮕ see more: [https://dotenvx.com/ops]')
|
|
69
|
+
const installCommand = installCommandForOps()
|
|
70
|
+
console.log(opsBanner(installCommand))
|
|
43
71
|
} else {
|
|
44
72
|
logger.info(`error: unknown command '${command}'`)
|
|
45
73
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fsx = require('./fsx')
|
|
2
|
+
const Errors = require('./errors')
|
|
2
3
|
|
|
3
4
|
const RESERVED_ENV_FILES = ['.env.project', '.env.keys', '.env.me', '.env.x', '.env.example']
|
|
4
5
|
|
|
@@ -14,10 +15,7 @@ function findEnvFiles (directory) {
|
|
|
14
15
|
return envFiles
|
|
15
16
|
} catch (e) {
|
|
16
17
|
if (e.code === 'ENOENT') {
|
|
17
|
-
|
|
18
|
-
error.code = 'MISSING_DIRECTORY'
|
|
19
|
-
|
|
20
|
-
throw error
|
|
18
|
+
throw new Errors({ directory }).missingDirectory()
|
|
21
19
|
} else {
|
|
22
20
|
throw e
|
|
23
21
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fsx = require('./fsx')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const Errors = require('./errors')
|
|
3
4
|
|
|
4
5
|
const HOOK_SCRIPT = `#!/bin/sh
|
|
5
6
|
|
|
@@ -45,8 +46,7 @@ class InstallPrecommitHook {
|
|
|
45
46
|
successMessage
|
|
46
47
|
}
|
|
47
48
|
} catch (err) {
|
|
48
|
-
|
|
49
|
-
throw error
|
|
49
|
+
throw new Errors({ error: err }).precommitHookModifyFailed()
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
package/src/lib/main.js
CHANGED
|
@@ -81,16 +81,10 @@ const config = function (options = {}) {
|
|
|
81
81
|
|
|
82
82
|
if (error.code === 'MISSING_ENV_FILE') {
|
|
83
83
|
if (!options.convention) { // do not output error for conventions (too noisy)
|
|
84
|
-
logger.error(error.
|
|
85
|
-
if (error.help) {
|
|
86
|
-
logger.error(error.help)
|
|
87
|
-
}
|
|
84
|
+
logger.error(error.messageWithHelp)
|
|
88
85
|
}
|
|
89
86
|
} else {
|
|
90
|
-
logger.error(error.
|
|
91
|
-
if (error.help) {
|
|
92
|
-
logger.error(error.help)
|
|
93
|
-
}
|
|
87
|
+
logger.error(error.messageWithHelp)
|
|
94
88
|
}
|
|
95
89
|
}
|
|
96
90
|
|
|
@@ -127,10 +121,7 @@ const config = function (options = {}) {
|
|
|
127
121
|
} catch (error) {
|
|
128
122
|
if (strict) throw error // throw immediately if strict
|
|
129
123
|
|
|
130
|
-
logger.error(error.
|
|
131
|
-
if (error.help) {
|
|
132
|
-
logger.help(error.help)
|
|
133
|
-
}
|
|
124
|
+
logger.error(error.messageWithHelp)
|
|
134
125
|
|
|
135
126
|
return { parsed: {}, error }
|
|
136
127
|
}
|
|
@@ -154,10 +145,7 @@ const parse = function (src, options = {}) {
|
|
|
154
145
|
|
|
155
146
|
// display any errors
|
|
156
147
|
for (const error of errors) {
|
|
157
|
-
logger.error(error.
|
|
158
|
-
if (error.help) {
|
|
159
|
-
logger.error(error.help)
|
|
160
|
-
}
|
|
148
|
+
logger.error(error.messageWithHelp)
|
|
161
149
|
}
|
|
162
150
|
|
|
163
151
|
return parsed
|
|
@@ -199,15 +187,9 @@ const set = function (key, value, options = {}) {
|
|
|
199
187
|
logger.verbose(`setting for ${processedEnv.envFilepath}`)
|
|
200
188
|
|
|
201
189
|
if (processedEnv.error) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
} else {
|
|
206
|
-
logger.warn(processedEnv.error.message)
|
|
207
|
-
if (processedEnv.error.help) {
|
|
208
|
-
logger.help(processedEnv.error.help)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
190
|
+
const error = processedEnv.error
|
|
191
|
+
const message = error.messageWithHelp || (error.help ? `${error.message}. ${error.help}` : error.message)
|
|
192
|
+
logger.warn(message)
|
|
211
193
|
} else {
|
|
212
194
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
213
195
|
|
|
@@ -260,10 +242,7 @@ const get = function (key, options = {}) {
|
|
|
260
242
|
|
|
261
243
|
if (options.strict) throw error // throw immediately if strict
|
|
262
244
|
|
|
263
|
-
logger.error(error.
|
|
264
|
-
if (error.help) {
|
|
265
|
-
logger.error(error.help)
|
|
266
|
-
}
|
|
245
|
+
logger.error(error.messageWithHelp)
|
|
267
246
|
}
|
|
268
247
|
|
|
269
248
|
if (key) {
|
|
@@ -122,7 +122,12 @@ class Encrypt {
|
|
|
122
122
|
if (!encrypted) {
|
|
123
123
|
row.keys.push(key) // track key(s)
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
let encryptedValue
|
|
126
|
+
try {
|
|
127
|
+
encryptedValue = encryptValue(value, publicKey)
|
|
128
|
+
} catch {
|
|
129
|
+
throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
|
|
130
|
+
}
|
|
126
131
|
|
|
127
132
|
// once newSrc is built write it out
|
|
128
133
|
envSrc = replace(envSrc, key, encryptedValue)
|
|
@@ -17,14 +17,7 @@ class Genexample {
|
|
|
17
17
|
|
|
18
18
|
run () {
|
|
19
19
|
if (this.envFile.length < 1) {
|
|
20
|
-
|
|
21
|
-
const message = 'no .env* files found'
|
|
22
|
-
const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx genexample]'
|
|
23
|
-
|
|
24
|
-
const error = new Error(message)
|
|
25
|
-
error.code = code
|
|
26
|
-
error.help = help
|
|
27
|
-
throw error
|
|
20
|
+
throw new Errors().missingEnvFiles()
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
const keys = new Set()
|
|
@@ -4,6 +4,7 @@ const path = require('path')
|
|
|
4
4
|
const ignore = require('ignore')
|
|
5
5
|
|
|
6
6
|
const Ls = require('../services/ls')
|
|
7
|
+
const Errors = require('../helpers/errors')
|
|
7
8
|
|
|
8
9
|
const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
|
|
9
10
|
const packageJson = require('./../helpers/packageJson')
|
|
@@ -24,7 +25,10 @@ class Prebuild {
|
|
|
24
25
|
|
|
25
26
|
// 1. check for .dockerignore file
|
|
26
27
|
if (!fsx.existsSync('.dockerignore')) {
|
|
27
|
-
const warning = new
|
|
28
|
+
const warning = new Errors({
|
|
29
|
+
message: `[dotenvx@${packageJson.version}][prebuild] .dockerignore missing`,
|
|
30
|
+
help: 'fix: [touch .dockerignore]'
|
|
31
|
+
}).custom()
|
|
28
32
|
warnings.push(warning)
|
|
29
33
|
} else {
|
|
30
34
|
dockerignore = fsx.readFileX('.dockerignore')
|
|
@@ -42,8 +46,10 @@ class Prebuild {
|
|
|
42
46
|
// check if that file is being ignored
|
|
43
47
|
if (ig.ignores(file)) {
|
|
44
48
|
if (file === '.env.example' || file === '.env.x') {
|
|
45
|
-
const warning = new
|
|
46
|
-
|
|
49
|
+
const warning = new Errors({
|
|
50
|
+
message: `[dotenvx@${packageJson.version}][prebuild] ${file} (currently ignored but should not be)`,
|
|
51
|
+
help: `fix: [dotenvx ext gitignore --pattern !${file}]`
|
|
52
|
+
}).custom()
|
|
47
53
|
warnings.push(warning)
|
|
48
54
|
}
|
|
49
55
|
} else {
|
|
@@ -54,15 +60,13 @@ class Prebuild {
|
|
|
54
60
|
// if contents are encrypted don't raise an error
|
|
55
61
|
if (!encrypted) {
|
|
56
62
|
let errorMsg = `[dotenvx@${packageJson.version}][prebuild] ${file} not protected (encrypted or dockerignored)`
|
|
57
|
-
let errorHelp = `
|
|
63
|
+
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
|
|
58
64
|
if (file.includes('.env.keys')) {
|
|
59
65
|
errorMsg = `[dotenvx@${packageJson.version}][prebuild] ${file} not protected (dockerignored)`
|
|
60
|
-
errorHelp = `
|
|
66
|
+
errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
error.help = errorHelp
|
|
65
|
-
throw error
|
|
69
|
+
throw new Errors({ message: errorMsg, help: errorHelp }).custom()
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
}
|
|
@@ -8,6 +8,7 @@ const Ls = require('../services/ls')
|
|
|
8
8
|
const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
|
|
9
9
|
const packageJson = require('./../helpers/packageJson')
|
|
10
10
|
const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
|
|
11
|
+
const Errors = require('./../helpers/errors')
|
|
11
12
|
const childProcess = require('child_process')
|
|
12
13
|
const MISSING_GITIGNORE = '.env.keys' // by default only ignore .env.keys. all other .env* files COULD be included - as long as they are encrypted
|
|
13
14
|
|
|
@@ -37,7 +38,10 @@ class Precommit {
|
|
|
37
38
|
|
|
38
39
|
// 1. check for .gitignore file
|
|
39
40
|
if (!fsx.existsSync('.gitignore')) {
|
|
40
|
-
const warning = new
|
|
41
|
+
const warning = new Errors({
|
|
42
|
+
message: `[dotenvx@${packageJson.version}][precommit] .gitignore missing`,
|
|
43
|
+
help: 'fix: [touch .gitignore]'
|
|
44
|
+
}).custom()
|
|
41
45
|
warnings.push(warning)
|
|
42
46
|
} else {
|
|
43
47
|
gitignore = fsx.readFileX('.gitignore')
|
|
@@ -58,8 +62,10 @@ class Precommit {
|
|
|
58
62
|
// check if that file is being ignored
|
|
59
63
|
if (ig.ignores(file)) {
|
|
60
64
|
if (file === '.env.example' || file === '.env.x') {
|
|
61
|
-
const warning = new
|
|
62
|
-
|
|
65
|
+
const warning = new Errors({
|
|
66
|
+
message: `[dotenvx@${packageJson.version}][precommit] ${file} (currently ignored but should not be)`,
|
|
67
|
+
help: `fix: [dotenvx ext gitignore --pattern !${file}]`
|
|
68
|
+
}).custom()
|
|
63
69
|
warnings.push(warning)
|
|
64
70
|
}
|
|
65
71
|
} else {
|
|
@@ -70,15 +76,13 @@ class Precommit {
|
|
|
70
76
|
// if contents are encrypted don't raise an error
|
|
71
77
|
if (!encrypted) {
|
|
72
78
|
let errorMsg = `[dotenvx@${packageJson.version}][precommit] ${file} not protected (encrypted or gitignored)`
|
|
73
|
-
let errorHelp = `
|
|
79
|
+
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
|
|
74
80
|
if (file.includes('.env.keys')) {
|
|
75
81
|
errorMsg = `[dotenvx@${packageJson.version}][precommit] ${file} not protected (gitignored)`
|
|
76
|
-
errorHelp = `
|
|
82
|
+
errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
|
|
77
83
|
}
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
error.help = errorHelp
|
|
81
|
-
throw error
|
|
85
|
+
throw new Errors({ message: errorMsg, help: errorHelp }).custom()
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
}
|
|
@@ -131,7 +131,12 @@ class Rotate {
|
|
|
131
131
|
row.keys.push(key) // track key(s)
|
|
132
132
|
|
|
133
133
|
const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKeyValue) // get decrypted value
|
|
134
|
-
|
|
134
|
+
let encryptedValue
|
|
135
|
+
try {
|
|
136
|
+
encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
|
|
137
|
+
} catch {
|
|
138
|
+
throw new Errors({ publicKeyName, publicKey: newPublicKey }).invalidPublicKey()
|
|
139
|
+
}
|
|
135
140
|
|
|
136
141
|
envSrc = replace(envSrc, key, encryptedValue)
|
|
137
142
|
}
|
package/src/lib/services/sets.js
CHANGED
|
@@ -109,7 +109,11 @@ class Sets {
|
|
|
109
109
|
|
|
110
110
|
row.publicKey = publicKey
|
|
111
111
|
row.privateKey = privateKey
|
|
112
|
-
|
|
112
|
+
try {
|
|
113
|
+
row.encryptedValue = encryptValue(this.value, publicKey)
|
|
114
|
+
} catch {
|
|
115
|
+
throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
|
|
116
|
+
}
|
|
113
117
|
row.privateKeyName = privateKeyName
|
|
114
118
|
}
|
|
115
119
|
|
package/src/shared/colors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const depth = require('../lib/helpers/colorDepth')
|
|
2
|
+
const Errors = require('../lib/helpers/errors')
|
|
2
3
|
|
|
3
4
|
const colors16 = new Map([
|
|
4
5
|
['amber', 33],
|
|
@@ -6,7 +7,7 @@ const colors16 = new Map([
|
|
|
6
7
|
['gray', 37],
|
|
7
8
|
['green', 32],
|
|
8
9
|
['olive', 33],
|
|
9
|
-
['orangered',
|
|
10
|
+
['orangered', 33], // mapped to yellow/brown
|
|
10
11
|
['plum', 35], // mapped to magenta
|
|
11
12
|
['red', 31],
|
|
12
13
|
['electricblue', 36],
|
|
@@ -19,21 +20,23 @@ const colors256 = new Map([
|
|
|
19
20
|
['gray', 244],
|
|
20
21
|
['green', 34],
|
|
21
22
|
['olive', 142],
|
|
22
|
-
['orangered',
|
|
23
|
+
['orangered', 130], // burnished copper
|
|
23
24
|
['plum', 182],
|
|
24
|
-
['red',
|
|
25
|
+
['red', 124], // brighter garnet
|
|
25
26
|
['electricblue', 45],
|
|
26
27
|
['dodgerblue', 33]
|
|
27
28
|
])
|
|
28
29
|
|
|
29
30
|
const colorsTrueColor = new Map([
|
|
30
|
-
['amber', [236, 213, 63]]
|
|
31
|
+
['amber', [236, 213, 63]],
|
|
32
|
+
['orangered', [138, 90, 43]], // #8A5A2B burnished copper
|
|
33
|
+
['red', [140, 35, 50]] // #8C2332 brighter garnet
|
|
31
34
|
])
|
|
32
35
|
|
|
33
36
|
function getColor (color) {
|
|
34
37
|
const colorDepth = depth.getColorDepth()
|
|
35
38
|
if (!colors256.has(color)) {
|
|
36
|
-
throw new
|
|
39
|
+
throw new Errors({ color }).invalidColor()
|
|
37
40
|
}
|
|
38
41
|
if (colorDepth >= 24 && colorsTrueColor.has(color)) {
|
|
39
42
|
const [r, g, b] = colorsTrueColor.get(color)
|
package/src/shared/logger.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const packageJson = require('../lib/helpers/packageJson')
|
|
2
|
+
const Errors = require('../lib/helpers/errors')
|
|
2
3
|
const { getColor, bold } = require('./colors')
|
|
3
4
|
|
|
4
5
|
const levels = {
|
|
@@ -13,8 +14,8 @@ const levels = {
|
|
|
13
14
|
silly: 6
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const error = (m) => bold(getColor('red')(m))
|
|
17
|
-
const warn = getColor('orangered')
|
|
17
|
+
const error = (m) => bold(getColor('red')(`☠ ${m}`))
|
|
18
|
+
const warn = (m) => getColor('orangered')(`⚠ ${m}`)
|
|
18
19
|
const success = getColor('amber')
|
|
19
20
|
const successv = getColor('amber')
|
|
20
21
|
const info = getColor('gray')
|
|
@@ -33,7 +34,7 @@ function stderr (level, message) {
|
|
|
33
34
|
|
|
34
35
|
function stdout (level, message) {
|
|
35
36
|
if (levels[level] === undefined) {
|
|
36
|
-
throw new
|
|
37
|
+
throw new Errors({ level }).missingLogLevel()
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
if (levels[level] <= currentLevel) {
|