@dotenvx/dotenvx 1.24.5 → 1.25.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 +23 -1
- package/README.md +131 -0
- package/package.json +1 -1
- package/src/cli/actions/decrypt.js +2 -2
- package/src/cli/actions/ext/genexample.js +1 -1
- package/src/cli/actions/ext/scan.js +2 -2
- package/src/cli/actions/get.js +44 -27
- package/src/cli/actions/run.js +29 -37
- package/src/cli/dotenvx.js +2 -0
- package/src/lib/helpers/catchAndLog.js +1 -1
- package/src/lib/helpers/{decryptValue.js → decryptKeyValue.js} +10 -13
- package/src/lib/helpers/deprecationNotice.js +17 -0
- package/src/lib/helpers/errors.js +89 -0
- package/src/lib/helpers/parse.js +10 -18
- package/src/lib/main.js +47 -74
- package/src/lib/services/decrypt.js +4 -6
- package/src/lib/services/encrypt.js +2 -4
- package/src/lib/services/genexample.js +3 -7
- package/src/lib/services/get.js +25 -8
- package/src/lib/services/run.js +14 -14
- package/src/lib/services/sets.js +4 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,29 @@
|
|
|
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.25.1...main)
|
|
6
|
+
|
|
7
|
+
## [1.25.1](https://github.com/dotenvx/dotenvx/compare/v1.25.0...v1.25.1)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* improve helpful error messaging around decryption failures by specifying specific key and private key name ([#463](https://github.com/dotenvx/dotenvx/pull/463))
|
|
12
|
+
|
|
13
|
+
## [1.25.0](https://github.com/dotenvx/dotenvx/compare/v1.24.5...v1.25.0)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* add `run --strict` flag to exit with code `1` if any errors are encountered - like a missing `.env` file or decryption failure ([#460](https://github.com/dotenvx/dotenvx/pull/460))
|
|
18
|
+
* add `get --strict` flag to exit with code `1` if any errors are encountered - like a missing `.env` file or decryption failure ([#461](https://github.com/dotenvx/dotenvx/pull/461))
|
|
19
|
+
* add `strict` option to `config()` to throw for any errors ([#459](https://github.com/dotenvx/dotenvx/pull/459))
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
* log `MISSING_ENV_FILE` and `DECRYPTION_FAILED` errors to stderr (prior was stdout as a warning) ([#459](https://github.com/dotenvx/dotenvx/pull/459))
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
* remove `dotenvx.get()` function from `lib/main.js`. (`parse` already historically exists for this purpose) ([#461](https://github.com/dotenvx/dotenvx/pull/461))
|
|
6
28
|
|
|
7
29
|
## [1.24.5](https://github.com/dotenvx/dotenvx/compare/v1.24.4...v1.24.5)
|
|
8
30
|
|
package/README.md
CHANGED
|
@@ -968,6 +968,21 @@ More examples
|
|
|
968
968
|
|
|
969
969
|
Available log levels are `error, warn, info, verbose, debug, silly` ([source](https://docs.npmjs.com/cli/v8/using-npm/logging#setting-log-levels))
|
|
970
970
|
|
|
971
|
+
</details>
|
|
972
|
+
* <details><summary>`run --strict`</summary><br>
|
|
973
|
+
|
|
974
|
+
Exit with code `1` if any errors are encountered - like a missing .env file or decryption failure.
|
|
975
|
+
|
|
976
|
+
```sh
|
|
977
|
+
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
|
978
|
+
|
|
979
|
+
$ dotenvx run -f .env.missing --strict -- node index.js
|
|
980
|
+
[MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
|
|
981
|
+
[MISSING_ENV_FILE] ? add one with [echo "HELLO=World" > .env.missing]
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
This can be useful in `ci` scripts where you want to fail the ci if your `.env` file could not be decrypted at runtime.
|
|
985
|
+
|
|
971
986
|
</details>
|
|
972
987
|
* <details><summary>`run --convention=nextjs`</summary><br>
|
|
973
988
|
|
|
@@ -1036,6 +1051,16 @@ More examples
|
|
|
1036
1051
|
World
|
|
1037
1052
|
```
|
|
1038
1053
|
|
|
1054
|
+
</details>
|
|
1055
|
+
* <details><summary>`get KEY --strict`</summary><br>
|
|
1056
|
+
|
|
1057
|
+
Exit with code `1` if any errors are encountered - like a missing key, missing .env file, or decryption failure.
|
|
1058
|
+
|
|
1059
|
+
```sh
|
|
1060
|
+
$ dotenvx get DOES_NOT_EXIST --strict
|
|
1061
|
+
[MISSING_KEY] missing DOES_NOT_EXIST key
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1039
1064
|
</details>
|
|
1040
1065
|
* <details><summary>`get KEY --convention=nextjs`</summary><br>
|
|
1041
1066
|
|
|
@@ -1731,6 +1756,112 @@ More examples
|
|
|
1731
1756
|
|
|
1732
1757
|
</details>
|
|
1733
1758
|
|
|
1759
|
+
### config() 📦
|
|
1760
|
+
|
|
1761
|
+
* <details><summary>`config()`</summary><br>
|
|
1762
|
+
|
|
1763
|
+
Use directly in node.js code.
|
|
1764
|
+
|
|
1765
|
+
```ini
|
|
1766
|
+
# .env
|
|
1767
|
+
HELLO="World"
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
```js
|
|
1771
|
+
// index.js
|
|
1772
|
+
require('@dotenvx/dotenvx').config()
|
|
1773
|
+
|
|
1774
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
```sh
|
|
1778
|
+
$ node index.js
|
|
1779
|
+
[dotenvx@1.24.5] injecting env (1) from .env
|
|
1780
|
+
Hello World
|
|
1781
|
+
```
|
|
1782
|
+
|
|
1783
|
+
</details>
|
|
1784
|
+
* <details><summary>`config(path: ['.env.local', '.env'])` - multiple files</summary><br>
|
|
1785
|
+
|
|
1786
|
+
Specify path(s) to multiple .env files.
|
|
1787
|
+
|
|
1788
|
+
```ini
|
|
1789
|
+
# .env.local
|
|
1790
|
+
HELLO="Me"
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
```ini
|
|
1794
|
+
# .env
|
|
1795
|
+
HELLO="World"
|
|
1796
|
+
```
|
|
1797
|
+
|
|
1798
|
+
```js
|
|
1799
|
+
// index.js
|
|
1800
|
+
require('@dotenvx/dotenvx').config({path: ['.env.local', '.env']})
|
|
1801
|
+
|
|
1802
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
1803
|
+
```
|
|
1804
|
+
|
|
1805
|
+
```sh
|
|
1806
|
+
$ node index.js
|
|
1807
|
+
[dotenvx@1.24.5] injecting env (1) from .env.local, .env
|
|
1808
|
+
Hello Me
|
|
1809
|
+
```
|
|
1810
|
+
|
|
1811
|
+
</details>
|
|
1812
|
+
* <details><summary>`config(overload: true)` - overload</summary><br>
|
|
1813
|
+
|
|
1814
|
+
User `overload` to overwrite the prior set value.
|
|
1815
|
+
|
|
1816
|
+
```ini
|
|
1817
|
+
# .env.local
|
|
1818
|
+
HELLO="Me"
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
```ini
|
|
1822
|
+
# .env
|
|
1823
|
+
HELLO="World"
|
|
1824
|
+
```
|
|
1825
|
+
|
|
1826
|
+
```js
|
|
1827
|
+
// index.js
|
|
1828
|
+
require('@dotenvx/dotenvx').config({path: ['.env.local', '.env'], overload: true})
|
|
1829
|
+
|
|
1830
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
1831
|
+
```
|
|
1832
|
+
|
|
1833
|
+
```sh
|
|
1834
|
+
$ node index.js
|
|
1835
|
+
[dotenvx@1.24.5] injecting env (1) from .env.local, .env
|
|
1836
|
+
Hello World
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
</details>
|
|
1840
|
+
* <details><summary>`config(strict: true)` - strict</summary><br>
|
|
1841
|
+
|
|
1842
|
+
Use `strict` to throw if an error is encountered - like a missing .env file.
|
|
1843
|
+
|
|
1844
|
+
```ini
|
|
1845
|
+
# .env
|
|
1846
|
+
HELLO="World"
|
|
1847
|
+
```
|
|
1848
|
+
|
|
1849
|
+
```js
|
|
1850
|
+
// index.js
|
|
1851
|
+
require('@dotenvx/dotenvx').config({path: ['.env.missing', '.env'], strict: true})
|
|
1852
|
+
|
|
1853
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
1854
|
+
```
|
|
1855
|
+
|
|
1856
|
+
```sh
|
|
1857
|
+
$ node index.js
|
|
1858
|
+
Error: [MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
|
|
1859
|
+
```
|
|
1860
|
+
|
|
1861
|
+
</details>
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
|
|
1734
1865
|
|
|
1735
1866
|
|
|
1736
1867
|
## Guides
|
package/package.json
CHANGED
|
@@ -48,10 +48,10 @@ function decrypt () {
|
|
|
48
48
|
errorCount += 1
|
|
49
49
|
|
|
50
50
|
if (processedEnv.error.code === 'MISSING_ENV_FILE') {
|
|
51
|
-
|
|
51
|
+
console.error(processedEnv.error.message)
|
|
52
52
|
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx decrypt]`)
|
|
53
53
|
} else {
|
|
54
|
-
|
|
54
|
+
console.error(processedEnv.error.message)
|
|
55
55
|
}
|
|
56
56
|
} else if (processedEnv.changed) {
|
|
57
57
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
@@ -10,7 +10,7 @@ function scan () {
|
|
|
10
10
|
// redirect stderr to stdout to capture and ignore it
|
|
11
11
|
childProcess.execSync('gitleaks version', { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
12
12
|
} catch (error) {
|
|
13
|
-
|
|
13
|
+
console.error('gitleaks: command not found')
|
|
14
14
|
logger.help('? install gitleaks: [brew install gitleaks]')
|
|
15
15
|
logger.help2('? other install options: [https://github.com/gitleaks/gitleaks]')
|
|
16
16
|
process.exit(1)
|
|
@@ -21,7 +21,7 @@ function scan () {
|
|
|
21
21
|
const output = childProcess.execSync('gitleaks detect -v 2>&1', { stdio: 'pipe' }).toString() // gitleaks sends output as stderr for strange reason
|
|
22
22
|
logger.blank(output)
|
|
23
23
|
} catch (error) {
|
|
24
|
-
|
|
24
|
+
console.error(error.message)
|
|
25
25
|
|
|
26
26
|
process.exit(1)
|
|
27
27
|
}
|
package/src/cli/actions/get.js
CHANGED
|
@@ -3,7 +3,7 @@ const { logger } = require('./../../shared/logger')
|
|
|
3
3
|
const conventions = require('./../../lib/helpers/conventions')
|
|
4
4
|
const escape = require('./../../lib/helpers/escape')
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const Get = require('./../../lib/services/get')
|
|
7
7
|
|
|
8
8
|
function get (key) {
|
|
9
9
|
if (key) {
|
|
@@ -21,40 +21,57 @@ function get (key) {
|
|
|
21
21
|
envs = this.envs
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
try {
|
|
25
|
+
const { parsed, errors } = new Get(key, envs, options.overload, process.env.DOTENV_KEY, options.all).run()
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let inline = ''
|
|
29
|
-
for (const [key, value] of Object.entries(results)) {
|
|
30
|
-
inline += `${key}=${escape(value)}\n`
|
|
31
|
-
}
|
|
32
|
-
inline = inline.trim()
|
|
27
|
+
for (const error of errors || []) {
|
|
28
|
+
if (options.strict) throw error // throw immediately if strict
|
|
33
29
|
|
|
34
|
-
console.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for (const [key, value] of Object.entries(results)) {
|
|
38
|
-
inline += `${key}=${value} `
|
|
30
|
+
console.error(error.message)
|
|
31
|
+
if (error.help) {
|
|
32
|
+
console.error(error.help)
|
|
39
33
|
}
|
|
40
|
-
|
|
34
|
+
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
if (key) {
|
|
37
|
+
const single = parsed[key]
|
|
38
|
+
if (single === undefined) {
|
|
39
|
+
console.log('')
|
|
40
|
+
} else {
|
|
41
|
+
console.log(single)
|
|
47
42
|
}
|
|
43
|
+
} else {
|
|
44
|
+
if (options.format === 'eval') {
|
|
45
|
+
let inline = ''
|
|
46
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
47
|
+
inline += `${key}=${escape(value)}\n`
|
|
48
|
+
}
|
|
49
|
+
inline = inline.trim()
|
|
50
|
+
|
|
51
|
+
console.log(inline)
|
|
52
|
+
} else if (options.format === 'shell') {
|
|
53
|
+
let inline = ''
|
|
54
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
55
|
+
inline += `${key}=${value} `
|
|
56
|
+
}
|
|
57
|
+
inline = inline.trim()
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
console.log(inline)
|
|
60
|
+
} else {
|
|
61
|
+
let space = 0
|
|
62
|
+
if (options.prettyPrint) {
|
|
63
|
+
space = 2
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(JSON.stringify(parsed, null, space))
|
|
67
|
+
}
|
|
50
68
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} else {
|
|
56
|
-
console.log(results)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(error.message)
|
|
71
|
+
if (error.help) {
|
|
72
|
+
console.error(error.help)
|
|
57
73
|
}
|
|
74
|
+
process.exit(1)
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
package/src/cli/actions/run.js
CHANGED
|
@@ -5,6 +5,7 @@ const executeCommand = require('./../../lib/helpers/executeCommand')
|
|
|
5
5
|
const Run = require('./../../lib/services/run')
|
|
6
6
|
|
|
7
7
|
const conventions = require('./../../lib/helpers/conventions')
|
|
8
|
+
const DeprecationNotice = require('./../../lib/helpers/deprecationNotice')
|
|
8
9
|
|
|
9
10
|
async function run () {
|
|
10
11
|
const commandArgs = this.args
|
|
@@ -35,11 +36,7 @@ async function run () {
|
|
|
35
36
|
envs = this.envs
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
logger.warn('DEPRECATION NOTICE: Setting DOTENV_KEY with .env.vault is deprecated.')
|
|
40
|
-
logger.warn('DEPRECATION NOTICE: Run [dotenvx ext vault migrate] for instructions on converting your .env.vault file to encrypted .env files (using public key encryption algorithm secp256k1)')
|
|
41
|
-
logger.warn('DEPRECATION NOTICE: Read more at [https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md#0380]')
|
|
42
|
-
}
|
|
39
|
+
new DeprecationNotice().dotenvKey() // DEPRECATION NOTICE
|
|
43
40
|
|
|
44
41
|
const {
|
|
45
42
|
processedEnvs,
|
|
@@ -62,43 +59,37 @@ async function run () {
|
|
|
62
59
|
logger.verbose(`loading env from string (${processedEnv.string})`)
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
for (const error of processedEnv.errors || []) {
|
|
63
|
+
if (options.strict) throw error // throw immediately if strict
|
|
64
|
+
|
|
65
|
+
if (error.code === 'MISSING_ENV_FILE') {
|
|
66
|
+
if (!options.convention) { // do not output error for conventions (too noisy)
|
|
67
|
+
console.error(error.message)
|
|
68
|
+
if (error.help) {
|
|
69
|
+
console.error(`${error.help} and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
|
|
70
|
+
}
|
|
71
71
|
}
|
|
72
72
|
} else {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (processedEnv.warnings) {
|
|
77
|
-
for (const warning of processedEnv.warnings) {
|
|
78
|
-
logger.warn(warning.message)
|
|
79
|
-
if (warning.help) {
|
|
80
|
-
logger.help(warning.help)
|
|
81
|
-
}
|
|
73
|
+
console.error(error.message)
|
|
74
|
+
if (error.help) {
|
|
75
|
+
console.error(error.help)
|
|
82
76
|
}
|
|
83
77
|
}
|
|
78
|
+
}
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
logger.debug(parsed)
|
|
80
|
+
// debug parsed
|
|
81
|
+
logger.debug(processedEnv.parsed)
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
83
|
+
// verbose/debug injected key/value
|
|
84
|
+
for (const [key, value] of Object.entries(processedEnv.injected || {})) {
|
|
85
|
+
logger.verbose(`${key} set`)
|
|
86
|
+
logger.debug(`${key} set to ${value}`)
|
|
87
|
+
}
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
|
|
101
|
-
}
|
|
89
|
+
// verbose/debug preExisted key/value
|
|
90
|
+
for (const [key, value] of Object.entries(processedEnv.preExisted || {})) {
|
|
91
|
+
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
92
|
+
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
|
|
@@ -113,10 +104,11 @@ async function run () {
|
|
|
113
104
|
|
|
114
105
|
logger.successv(msg)
|
|
115
106
|
} catch (error) {
|
|
116
|
-
|
|
107
|
+
console.error(error.message)
|
|
117
108
|
if (error.help) {
|
|
118
|
-
|
|
109
|
+
console.error(error.help)
|
|
119
110
|
}
|
|
111
|
+
process.exit(1)
|
|
120
112
|
}
|
|
121
113
|
|
|
122
114
|
await executeCommand(commandArgs, process.env)
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -58,6 +58,7 @@ program.command('run')
|
|
|
58
58
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
59
59
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
60
60
|
.option('-o, --overload', 'override existing env variables')
|
|
61
|
+
.option('--strict', 'process.exit(1) on any errors (default: false)', false)
|
|
61
62
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
|
|
62
63
|
.action(function (...args) {
|
|
63
64
|
this.envs = envs
|
|
@@ -74,6 +75,7 @@ program.command('get')
|
|
|
74
75
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
75
76
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
76
77
|
.option('-o, --overload', 'override existing env variables')
|
|
78
|
+
.option('--strict', 'process.exit(1) on any errors (default: false)', false)
|
|
77
79
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
|
|
78
80
|
.option('-a, --all', 'include all machine envs as well')
|
|
79
81
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { decrypt } = require('eciesjs')
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Errors = require('./errors')
|
|
4
4
|
|
|
5
5
|
const PREFIX = 'encrypted:'
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
function decryptKeyValue (key, value, privateKeyName, privateKey) {
|
|
8
8
|
let decryptedValue
|
|
9
9
|
let decryptionError
|
|
10
10
|
|
|
@@ -14,12 +14,11 @@ function decryptValue (value, privateKey) {
|
|
|
14
14
|
|
|
15
15
|
privateKey = privateKey || ''
|
|
16
16
|
if (privateKey.length <= 0) {
|
|
17
|
-
decryptionError = new
|
|
18
|
-
decryptionError.code = 'DECRYPTION_FAILED'
|
|
17
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey }).missingPrivateKey()
|
|
19
18
|
} else {
|
|
20
19
|
const privateKeys = privateKey.split(',')
|
|
21
|
-
for (const
|
|
22
|
-
const secret = Buffer.from(
|
|
20
|
+
for (const privKey of privateKeys) {
|
|
21
|
+
const secret = Buffer.from(privKey, 'hex')
|
|
23
22
|
const encoded = value.substring(PREFIX.length)
|
|
24
23
|
const ciphertext = Buffer.from(encoded, 'base64')
|
|
25
24
|
|
|
@@ -29,16 +28,14 @@ function decryptValue (value, privateKey) {
|
|
|
29
28
|
break
|
|
30
29
|
} catch (e) {
|
|
31
30
|
if (e.message === 'Invalid private key') {
|
|
32
|
-
decryptionError = new
|
|
31
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
|
|
33
32
|
} else if (e.message === 'Unsupported state or unable to authenticate data') {
|
|
34
|
-
decryptionError = new
|
|
33
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey }).looksWrongPrivateKey()
|
|
35
34
|
} else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
|
|
36
|
-
decryptionError = new
|
|
35
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
|
|
37
36
|
} else {
|
|
38
|
-
decryptionError = new
|
|
37
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey, message: e.message }).decryptionFailed()
|
|
39
38
|
}
|
|
40
|
-
|
|
41
|
-
decryptionError.code = 'DECRYPTION_FAILED'
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -50,4 +47,4 @@ function decryptValue (value, privateKey) {
|
|
|
50
47
|
return decryptedValue
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
module.exports =
|
|
50
|
+
module.exports = decryptKeyValue
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { logger } = require('./../../shared/logger')
|
|
2
|
+
|
|
3
|
+
class DeprecationNotice {
|
|
4
|
+
constructor (options = {}) {
|
|
5
|
+
this.DOTENV_KEY = options.DOTENV_KEY || process.env.DOTENV_KEY
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
dotenvKey () {
|
|
9
|
+
if (this.DOTENV_KEY) {
|
|
10
|
+
logger.warn('[DEPRECATION NOTICE] Setting DOTENV_KEY with .env.vault is deprecated.')
|
|
11
|
+
logger.warn('[DEPRECATION NOTICE] Run [dotenvx ext vault migrate] for instructions on converting your .env.vault file to encrypted .env files (using public key encryption algorithm secp256k1)')
|
|
12
|
+
logger.warn('[DEPRECATION NOTICE] Read more at [https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md#0380]')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = DeprecationNotice
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const truncate = require('./truncate')
|
|
2
|
+
|
|
3
|
+
class Errors {
|
|
4
|
+
constructor (options = {}) {
|
|
5
|
+
this.filepath = options.filepath
|
|
6
|
+
this.envFilepath = options.envFilepath
|
|
7
|
+
|
|
8
|
+
this.key = options.key
|
|
9
|
+
this.privateKey = options.privateKey
|
|
10
|
+
this.privateKeyName = options.privateKeyName
|
|
11
|
+
|
|
12
|
+
this.message = options.message
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
missingEnvFile () {
|
|
16
|
+
const code = 'MISSING_ENV_FILE'
|
|
17
|
+
const message = `[${code}] missing ${this.envFilepath} file (${this.filepath})`
|
|
18
|
+
const help = `[${code}] ? add one with [echo "HELLO=World" > ${this.envFilepath}]`
|
|
19
|
+
|
|
20
|
+
const e = new Error(message)
|
|
21
|
+
e.code = code
|
|
22
|
+
e.help = help
|
|
23
|
+
return e
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
missingKey () {
|
|
27
|
+
const code = 'MISSING_KEY'
|
|
28
|
+
const message = `[${code}] missing ${this.key} key`
|
|
29
|
+
|
|
30
|
+
const e = new Error(message)
|
|
31
|
+
e.code = code
|
|
32
|
+
return e
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
missingPrivateKey () {
|
|
36
|
+
const code = 'MISSING_PRIVATE_KEY'
|
|
37
|
+
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
38
|
+
const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/464`
|
|
39
|
+
|
|
40
|
+
const e = new Error(message)
|
|
41
|
+
e.code = code
|
|
42
|
+
e.help = help
|
|
43
|
+
return e
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
invalidPrivateKey () {
|
|
47
|
+
const code = 'INVALID_PRIVATE_KEY'
|
|
48
|
+
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
49
|
+
const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/465`
|
|
50
|
+
|
|
51
|
+
const e = new Error(message)
|
|
52
|
+
e.code = code
|
|
53
|
+
e.help = help
|
|
54
|
+
return e
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
looksWrongPrivateKey () {
|
|
58
|
+
const code = 'WRONG_PRIVATE_KEY'
|
|
59
|
+
const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
|
|
60
|
+
const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/466`
|
|
61
|
+
|
|
62
|
+
const e = new Error(message)
|
|
63
|
+
e.code = code
|
|
64
|
+
e.help = help
|
|
65
|
+
return e
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
malformedEncryptedData () {
|
|
69
|
+
const code = 'MALFORMED_ENCRYPTED_DATA'
|
|
70
|
+
const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
|
|
71
|
+
const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/467`
|
|
72
|
+
|
|
73
|
+
const e = new Error(message)
|
|
74
|
+
e.code = code
|
|
75
|
+
e.help = help
|
|
76
|
+
return e
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
decryptionFailed () {
|
|
80
|
+
const code = 'DECRYPTION_FAILED'
|
|
81
|
+
const message = this.message
|
|
82
|
+
|
|
83
|
+
const e = new Error(message)
|
|
84
|
+
e.code = code
|
|
85
|
+
return e
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = Errors
|
package/src/lib/helpers/parse.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
const chomp = require('./chomp')
|
|
2
|
-
const
|
|
3
|
-
const decryptValue = require('./decryptValue')
|
|
2
|
+
const decryptKeyValue = require('./decryptKeyValue')
|
|
4
3
|
const resolveEscapeSequences = require('./resolveEscapeSequences')
|
|
5
4
|
const { execSync } = require('child_process')
|
|
6
5
|
|
|
7
6
|
class Parse {
|
|
8
7
|
static LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
|
|
9
8
|
|
|
10
|
-
constructor (src, privateKey = null, processEnv = process.env, overload = false) {
|
|
9
|
+
constructor (src, privateKey = null, processEnv = process.env, overload = false, privateKeyName = null) {
|
|
11
10
|
this.src = src
|
|
12
11
|
this.privateKey = privateKey
|
|
12
|
+
this.privateKeyName = privateKeyName
|
|
13
13
|
this.processEnv = processEnv
|
|
14
14
|
this.overload = overload
|
|
15
15
|
|
|
16
16
|
this.parsed = {}
|
|
17
17
|
this.preExisted = {}
|
|
18
18
|
this.injected = {}
|
|
19
|
-
this.
|
|
19
|
+
this.errors = []
|
|
20
20
|
|
|
21
21
|
// for use with progressive expansion
|
|
22
22
|
this.runningParsed = {}
|
|
@@ -40,9 +40,9 @@ class Parse {
|
|
|
40
40
|
|
|
41
41
|
// decrypt
|
|
42
42
|
try {
|
|
43
|
-
this.parsed[key] = this.decrypt(this.parsed[key])
|
|
43
|
+
this.parsed[key] = this.decrypt(key, this.parsed[key])
|
|
44
44
|
} catch (e) {
|
|
45
|
-
this.
|
|
45
|
+
this.errors.push(e)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// eval empty, double, or backticks
|
|
@@ -78,8 +78,8 @@ class Parse {
|
|
|
78
78
|
parsed: this.parsed,
|
|
79
79
|
processEnv: this.processEnv,
|
|
80
80
|
injected: this.injected,
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
preExisted: this.preExisted,
|
|
82
|
+
errors: this.errors
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -128,8 +128,8 @@ class Parse {
|
|
|
128
128
|
return v
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
decrypt (value) {
|
|
132
|
-
return
|
|
131
|
+
decrypt (key, value) {
|
|
132
|
+
return decryptKeyValue(key, value, this.privateKeyName, this.privateKey)
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
eval (value) {
|
|
@@ -207,14 +207,6 @@ class Parse {
|
|
|
207
207
|
getLines () {
|
|
208
208
|
return (this.src || '').toString().replace(/\r\n?/mg, '\n') // Convert buffer to string and Convert line breaks to same format
|
|
209
209
|
}
|
|
210
|
-
|
|
211
|
-
warning (e, key) {
|
|
212
|
-
const warning = new Error(`[${e.code}] could not decrypt ${key} using private key '${truncate(this.privateKey)}'`)
|
|
213
|
-
warning.code = e.code
|
|
214
|
-
warning.help = `[${e.code}] ? ${e.message}`
|
|
215
|
-
|
|
216
|
-
return warning
|
|
217
|
-
}
|
|
218
210
|
}
|
|
219
211
|
|
|
220
212
|
module.exports = Parse
|
package/src/lib/main.js
CHANGED
|
@@ -7,7 +7,6 @@ const { getColor, bold } = require('./../shared/colors')
|
|
|
7
7
|
|
|
8
8
|
// services
|
|
9
9
|
const Ls = require('./services/ls')
|
|
10
|
-
const Get = require('./services/get')
|
|
11
10
|
const Run = require('./services/run')
|
|
12
11
|
const Keypair = require('./services/keypair')
|
|
13
12
|
const Genexample = require('./services/genexample')
|
|
@@ -16,6 +15,7 @@ const Genexample = require('./services/genexample')
|
|
|
16
15
|
const conventions = require('./helpers/conventions')
|
|
17
16
|
const dotenvOptionPaths = require('./helpers/dotenvOptionPaths')
|
|
18
17
|
const Parse = require('./helpers/parse')
|
|
18
|
+
const DeprecationNotice = require('./helpers/deprecationNotice')
|
|
19
19
|
|
|
20
20
|
/** @type {import('./main').config} */
|
|
21
21
|
const config = function (options = {}) {
|
|
@@ -28,7 +28,10 @@ const config = function (options = {}) {
|
|
|
28
28
|
// overload
|
|
29
29
|
const overload = options.overload || options.override
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// strict
|
|
32
|
+
const strict = options.strict
|
|
33
|
+
|
|
34
|
+
// DOTENV_KEY (DEPRECATED)
|
|
32
35
|
let DOTENV_KEY = process.env.DOTENV_KEY
|
|
33
36
|
if (options && options.DOTENV_KEY) {
|
|
34
37
|
DOTENV_KEY = options.DOTENV_KEY
|
|
@@ -46,11 +49,7 @@ const config = function (options = {}) {
|
|
|
46
49
|
envs = conventions(options.convention).concat(envs)
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
logger.warn('DEPRECATION NOTICE: Setting DOTENV_KEY with .env.vault is deprecated.')
|
|
51
|
-
logger.warn('DEPRECATION NOTICE: Run [dotenvx ext vault migrate] for instructions on converting your .env.vault file to encrypted .env files (using public key encryption algorithm secp256k1)')
|
|
52
|
-
logger.warn('DEPRECATION NOTICE: Read more at [https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md#0380]')
|
|
53
|
-
}
|
|
52
|
+
new DeprecationNotice({ DOTENV_KEY }).dotenvKey() // DEPRECATION NOTICE
|
|
54
53
|
|
|
55
54
|
for (const optionPath of optionPaths) {
|
|
56
55
|
// if DOTENV_KEY is set then assume we are checking envVaultFile
|
|
@@ -64,12 +63,11 @@ const config = function (options = {}) {
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
const {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
).run()
|
|
66
|
+
const {
|
|
67
|
+
processedEnvs,
|
|
68
|
+
readableFilepaths,
|
|
69
|
+
uniqueInjectedKeys
|
|
70
|
+
} = new Run(envs, overload, DOTENV_KEY, processEnv).run()
|
|
73
71
|
|
|
74
72
|
let lastError
|
|
75
73
|
/** @type {Record<string, string>} */
|
|
@@ -77,65 +75,50 @@ const config = function (options = {}) {
|
|
|
77
75
|
|
|
78
76
|
for (const processedEnv of processedEnvs) {
|
|
79
77
|
if (processedEnv.type === 'envVaultFile') {
|
|
80
|
-
logger.verbose(
|
|
81
|
-
|
|
82
|
-
processedEnv.filepath
|
|
83
|
-
)})`
|
|
84
|
-
)
|
|
85
|
-
logger.debug(
|
|
86
|
-
`decrypting encrypted env from ${
|
|
87
|
-
processedEnv.filepath
|
|
88
|
-
} (${path.resolve(processedEnv.filepath)})`
|
|
89
|
-
)
|
|
78
|
+
logger.verbose(`loading env from encrypted ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
79
|
+
logger.debug(`decrypting encrypted env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
90
80
|
}
|
|
91
81
|
|
|
92
82
|
if (processedEnv.type === 'envFile') {
|
|
93
|
-
logger.verbose(
|
|
94
|
-
`loading env from ${processedEnv.filepath} (${path.resolve(
|
|
95
|
-
processedEnv.filepath
|
|
96
|
-
)})`
|
|
97
|
-
)
|
|
83
|
+
logger.verbose(`loading env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
98
84
|
}
|
|
99
85
|
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
for (const error of processedEnv.errors || []) {
|
|
87
|
+
if (strict) throw error // throw immediately if strict
|
|
102
88
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
89
|
+
lastError = error // surface later in { error }
|
|
90
|
+
|
|
91
|
+
if (error.code === 'MISSING_ENV_FILE') {
|
|
92
|
+
if (!options.convention) { // do not output error for conventions (too noisy)
|
|
93
|
+
console.error(error.message)
|
|
94
|
+
if (error.help) {
|
|
95
|
+
logger.help(error.help)
|
|
96
|
+
}
|
|
110
97
|
}
|
|
111
98
|
} else {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Object.assign(parsedAll, processedEnv.preExisted) // preExisted 'wins'
|
|
117
|
-
|
|
118
|
-
// debug parsed
|
|
119
|
-
const parsed = processedEnv.parsed
|
|
120
|
-
logger.debug(parsed)
|
|
121
|
-
|
|
122
|
-
// verbose/debug injected key/value
|
|
123
|
-
const injected = processedEnv.injected
|
|
124
|
-
for (const [key, value] of Object.entries(injected)) {
|
|
125
|
-
logger.verbose(`${key} set`)
|
|
126
|
-
logger.debug(`${key} set to ${value}`)
|
|
99
|
+
console.error(error.message)
|
|
100
|
+
if (error.help) {
|
|
101
|
+
logger.help(error.help)
|
|
102
|
+
}
|
|
127
103
|
}
|
|
104
|
+
}
|
|
128
105
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
106
|
+
Object.assign(parsedAll, processedEnv.injected || {})
|
|
107
|
+
Object.assign(parsedAll, processedEnv.preExisted || {}) // preExisted 'wins'
|
|
108
|
+
|
|
109
|
+
// debug parsed
|
|
110
|
+
logger.debug(processedEnv.parsed)
|
|
111
|
+
|
|
112
|
+
// verbose/debug injected key/value
|
|
113
|
+
for (const [key, value] of Object.entries(processedEnv.injected || {})) {
|
|
114
|
+
logger.verbose(`${key} set`)
|
|
115
|
+
logger.debug(`${key} set to ${value}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// verbose/debug preExisted key/value
|
|
119
|
+
for (const [key, value] of Object.entries(processedEnv.preExisted || {})) {
|
|
120
|
+
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
121
|
+
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
|
|
139
122
|
}
|
|
140
123
|
}
|
|
141
124
|
|
|
@@ -151,6 +134,8 @@ const config = function (options = {}) {
|
|
|
151
134
|
return { parsed: parsedAll }
|
|
152
135
|
}
|
|
153
136
|
} catch (error) {
|
|
137
|
+
if (strict) throw error // throw immediately if strict
|
|
138
|
+
|
|
154
139
|
logger.error(error.message)
|
|
155
140
|
if (error.help) {
|
|
156
141
|
logger.help(error.help)
|
|
@@ -189,17 +174,6 @@ const genexample = function (directory, envFile) {
|
|
|
189
174
|
return new Genexample(directory, envFile).run()
|
|
190
175
|
}
|
|
191
176
|
|
|
192
|
-
/** @type {import('./main').get} */
|
|
193
|
-
const get = function (
|
|
194
|
-
key,
|
|
195
|
-
envs = [],
|
|
196
|
-
overload = false,
|
|
197
|
-
DOTENV_KEY = '',
|
|
198
|
-
all = false
|
|
199
|
-
) {
|
|
200
|
-
return new Get(key, envs, overload, DOTENV_KEY, all).run()
|
|
201
|
-
}
|
|
202
|
-
|
|
203
177
|
/** @type {import('./main').keypair} */
|
|
204
178
|
const keypair = function (envFile, key) {
|
|
205
179
|
return new Keypair(envFile, key).run()
|
|
@@ -211,7 +185,6 @@ module.exports = {
|
|
|
211
185
|
parse,
|
|
212
186
|
// actions related
|
|
213
187
|
ls,
|
|
214
|
-
get,
|
|
215
188
|
keypair,
|
|
216
189
|
genexample,
|
|
217
190
|
// expose for libs depending on @dotenvx/dotenvx - like dotenvx-pro
|
|
@@ -5,9 +5,10 @@ const picomatch = require('picomatch')
|
|
|
5
5
|
|
|
6
6
|
const TYPE_ENV_FILE = 'envFile'
|
|
7
7
|
|
|
8
|
+
const Errors = require('./../helpers/errors')
|
|
8
9
|
const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
9
10
|
const findPrivateKey = require('./../helpers/findPrivateKey')
|
|
10
|
-
const
|
|
11
|
+
const decryptKeyValue = require('./../helpers/decryptKeyValue')
|
|
11
12
|
const isEncrypted = require('./../helpers/isEncrypted')
|
|
12
13
|
const replace = require('./../helpers/replace')
|
|
13
14
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
@@ -85,7 +86,7 @@ class Decrypt {
|
|
|
85
86
|
if (encrypted) {
|
|
86
87
|
row.keys.push(key) // track key(s)
|
|
87
88
|
|
|
88
|
-
const decryptedValue =
|
|
89
|
+
const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKey)
|
|
89
90
|
// once newSrc is built write it out
|
|
90
91
|
envSrc = replace(envSrc, key, decryptedValue)
|
|
91
92
|
|
|
@@ -101,10 +102,7 @@ class Decrypt {
|
|
|
101
102
|
}
|
|
102
103
|
} catch (e) {
|
|
103
104
|
if (e.code === 'ENOENT') {
|
|
104
|
-
|
|
105
|
-
error.code = 'MISSING_ENV_FILE'
|
|
106
|
-
|
|
107
|
-
row.error = error
|
|
105
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
108
106
|
} else {
|
|
109
107
|
row.error = e
|
|
110
108
|
}
|
|
@@ -5,6 +5,7 @@ const picomatch = require('picomatch')
|
|
|
5
5
|
|
|
6
6
|
const TYPE_ENV_FILE = 'envFile'
|
|
7
7
|
|
|
8
|
+
const Errors = require('./../helpers/errors')
|
|
8
9
|
const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
9
10
|
const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
|
|
10
11
|
const encryptValue = require('./../helpers/encryptValue')
|
|
@@ -181,10 +182,7 @@ class Encrypt {
|
|
|
181
182
|
}
|
|
182
183
|
} catch (e) {
|
|
183
184
|
if (e.code === 'ENOENT') {
|
|
184
|
-
|
|
185
|
-
error.code = 'MISSING_ENV_FILE'
|
|
186
|
-
|
|
187
|
-
row.error = error
|
|
185
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
188
186
|
} else {
|
|
189
187
|
row.error = e
|
|
190
188
|
}
|
|
@@ -2,6 +2,7 @@ const fsx = require('./../helpers/fsx')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const dotenv = require('dotenv')
|
|
4
4
|
|
|
5
|
+
const Errors = require('../helpers/errors')
|
|
5
6
|
const findEnvFiles = require('../helpers/findEnvFiles')
|
|
6
7
|
const replace = require('../helpers/replace')
|
|
7
8
|
|
|
@@ -39,13 +40,8 @@ class Genexample {
|
|
|
39
40
|
for (const envFilepath of envFilepaths) {
|
|
40
41
|
const filepath = path.resolve(this.directory, envFilepath)
|
|
41
42
|
if (!fsx.existsSync(filepath)) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
|
|
45
|
-
|
|
46
|
-
const error = new Error(message)
|
|
47
|
-
error.code = code
|
|
48
|
-
error.help = help
|
|
43
|
+
const error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
44
|
+
error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
|
|
49
45
|
throw error
|
|
50
46
|
}
|
|
51
47
|
|
package/src/lib/services/get.js
CHANGED
|
@@ -1,41 +1,58 @@
|
|
|
1
1
|
const Run = require('./run')
|
|
2
|
+
const Errors = require('./../helpers/errors')
|
|
2
3
|
|
|
3
4
|
class Get {
|
|
4
|
-
constructor (key, envs = [], overload = false, DOTENV_KEY = '', all = false) {
|
|
5
|
+
constructor (key, envs = [], overload = false, DOTENV_KEY = '', all = false, strict = false) {
|
|
5
6
|
this.key = key
|
|
6
7
|
this.envs = envs
|
|
7
8
|
this.overload = overload
|
|
8
9
|
this.DOTENV_KEY = DOTENV_KEY
|
|
9
10
|
this.all = all
|
|
11
|
+
this.strict = strict
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
run () {
|
|
13
15
|
const processEnv = { ...process.env }
|
|
14
16
|
const { processedEnvs } = new Run(this.envs, this.overload, this.DOTENV_KEY, processEnv).run()
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
const errors = []
|
|
19
|
+
for (const processedEnv of processedEnvs) {
|
|
20
|
+
for (const error of processedEnv.errors) {
|
|
21
|
+
errors.push(error)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.key) {
|
|
26
|
+
const parsed = {}
|
|
27
|
+
const value = processEnv[this.key]
|
|
28
|
+
parsed[this.key] = value
|
|
29
|
+
|
|
30
|
+
if (value === undefined) {
|
|
31
|
+
errors.push(new Errors({ key: this.key }).missingKey())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { parsed, errors }
|
|
35
|
+
} else {
|
|
17
36
|
// if user wants to return ALL envs (even prior set on machine)
|
|
18
37
|
if (this.all) {
|
|
19
|
-
return processEnv
|
|
38
|
+
return { parsed: processEnv, errors }
|
|
20
39
|
}
|
|
21
40
|
|
|
22
41
|
// typical scenario - return only envs that were identified in the .env file
|
|
23
42
|
// iterate over all processedEnvs.parsed and grab from processEnv
|
|
24
43
|
/** @type {Record<string, string>} */
|
|
25
|
-
const
|
|
44
|
+
const parsed = {}
|
|
26
45
|
for (const processedEnv of processedEnvs) {
|
|
27
46
|
// parsed means we saw the key in a file or --env flag. this effectively filters out any preset machine envs - while still respecting complex evaluating, expansion, and overload. in other words, the value might be the machine value because the key was displayed in a .env file
|
|
28
47
|
if (processedEnv.parsed) {
|
|
29
48
|
for (const key of Object.keys(processedEnv.parsed)) {
|
|
30
|
-
|
|
49
|
+
parsed[key] = processEnv[key]
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
}
|
|
34
53
|
|
|
35
|
-
return
|
|
54
|
+
return { parsed, errors }
|
|
36
55
|
}
|
|
37
|
-
|
|
38
|
-
return processEnv[this.key]
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
58
|
|
package/src/lib/services/run.js
CHANGED
|
@@ -8,9 +8,11 @@ const TYPE_ENV_VAULT_FILE = 'envVaultFile'
|
|
|
8
8
|
|
|
9
9
|
const decrypt = require('./../helpers/decrypt')
|
|
10
10
|
const Parse = require('./../helpers/parse')
|
|
11
|
+
const Errors = require('./../helpers/errors')
|
|
11
12
|
const parseEnvironmentFromDotenvKey = require('./../helpers/parseEnvironmentFromDotenvKey')
|
|
12
13
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
13
14
|
const findPrivateKey = require('./../helpers/findPrivateKey')
|
|
15
|
+
const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
14
16
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
15
17
|
|
|
16
18
|
class Run {
|
|
@@ -59,9 +61,9 @@ class Run {
|
|
|
59
61
|
row.string = env
|
|
60
62
|
|
|
61
63
|
try {
|
|
62
|
-
const { parsed,
|
|
64
|
+
const { parsed, errors, injected, preExisted } = new Parse(env, null, this.processEnv, this.overload).run()
|
|
63
65
|
row.parsed = parsed
|
|
64
|
-
row.
|
|
66
|
+
row.errors = errors
|
|
65
67
|
row.injected = injected
|
|
66
68
|
row.preExisted = preExisted
|
|
67
69
|
|
|
@@ -73,7 +75,7 @@ class Run {
|
|
|
73
75
|
this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
74
76
|
}
|
|
75
77
|
} catch (e) {
|
|
76
|
-
row.
|
|
78
|
+
row.errors = [e]
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
this.processedEnvs.push(row)
|
|
@@ -91,10 +93,11 @@ class Run {
|
|
|
91
93
|
this.readableFilepaths.add(envFilepath)
|
|
92
94
|
|
|
93
95
|
const privateKey = findPrivateKey(envFilepath)
|
|
94
|
-
const
|
|
96
|
+
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
97
|
+
const { parsed, errors, injected, preExisted } = new Parse(src, privateKey, this.processEnv, this.overload, privateKeyName).run()
|
|
95
98
|
|
|
96
99
|
row.parsed = parsed
|
|
97
|
-
row.
|
|
100
|
+
row.errors = errors
|
|
98
101
|
row.injected = injected
|
|
99
102
|
row.preExisted = preExisted
|
|
100
103
|
|
|
@@ -104,13 +107,10 @@ class Run {
|
|
|
104
107
|
this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
105
108
|
}
|
|
106
109
|
} catch (e) {
|
|
107
|
-
if (e.code === 'ENOENT') {
|
|
108
|
-
|
|
109
|
-
error.code = 'MISSING_ENV_FILE'
|
|
110
|
-
|
|
111
|
-
row.error = error
|
|
110
|
+
if (e.code === 'ENOENT' || e.code === 'EISDIR') {
|
|
111
|
+
row.errors = [new Errors({ envFilepath, filepath }).missingEnvFile()]
|
|
112
112
|
} else {
|
|
113
|
-
row.
|
|
113
|
+
row.errors = [e]
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -162,9 +162,9 @@ class Run {
|
|
|
162
162
|
|
|
163
163
|
try {
|
|
164
164
|
// parse this. it's the equivalent of the .env file
|
|
165
|
-
const { parsed,
|
|
165
|
+
const { parsed, errors, injected, preExisted } = new Parse(decrypted, null, this.processEnv, this.overload).run()
|
|
166
166
|
row.parsed = parsed
|
|
167
|
-
row.
|
|
167
|
+
row.errors = errors
|
|
168
168
|
row.injected = injected
|
|
169
169
|
row.preExisted = preExisted
|
|
170
170
|
|
|
@@ -174,7 +174,7 @@ class Run {
|
|
|
174
174
|
this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
175
175
|
}
|
|
176
176
|
} catch (e) {
|
|
177
|
-
row.
|
|
177
|
+
row.errors = [e]
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
this.processedEnvs.push(row)
|
package/src/lib/services/sets.js
CHANGED
|
@@ -4,10 +4,11 @@ const dotenv = require('dotenv')
|
|
|
4
4
|
|
|
5
5
|
const TYPE_ENV_FILE = 'envFile'
|
|
6
6
|
|
|
7
|
+
const Errors = require('./../helpers/errors')
|
|
7
8
|
const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
8
9
|
const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
|
|
9
10
|
const encryptValue = require('./../helpers/encryptValue')
|
|
10
|
-
const
|
|
11
|
+
const decryptKeyValue = require('./../helpers/decryptKeyValue')
|
|
11
12
|
const replace = require('./../helpers/replace')
|
|
12
13
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
13
14
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
@@ -84,7 +85,7 @@ class Sets {
|
|
|
84
85
|
privateKey = kp.privateKey
|
|
85
86
|
|
|
86
87
|
if (row.originalValue) {
|
|
87
|
-
row.originalValue =
|
|
88
|
+
row.originalValue = decryptKeyValue(row.key, row.originalValue, privateKeyName, privateKey)
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
// if derivation doesn't match what's in the file (or preset in env)
|
|
@@ -169,10 +170,7 @@ class Sets {
|
|
|
169
170
|
}
|
|
170
171
|
} catch (e) {
|
|
171
172
|
if (e.code === 'ENOENT') {
|
|
172
|
-
|
|
173
|
-
error.code = 'MISSING_ENV_FILE'
|
|
174
|
-
|
|
175
|
-
row.error = error
|
|
173
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
176
174
|
} else {
|
|
177
175
|
row.error = e
|
|
178
176
|
}
|