@dotenvx/dotenvx 1.29.0 → 1.30.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 +30 -1
- package/README.md +126 -3
- package/package.json +1 -1
- package/src/cli/actions/decrypt.js +8 -2
- package/src/cli/actions/encrypt.js +2 -2
- package/src/cli/actions/get.js +1 -1
- package/src/cli/actions/keypair.js +1 -1
- package/src/cli/actions/run.js +1 -1
- package/src/cli/actions/set.js +3 -2
- package/src/cli/dotenvx.js +6 -0
- package/src/lib/helpers/findPrivateKey.js +6 -21
- package/src/lib/helpers/findPublicKey.js +4 -20
- package/src/lib/helpers/{keyPair.js → keypair.js} +2 -2
- package/src/lib/helpers/parse.js +2 -3
- package/src/lib/helpers/proKeypair.js +42 -0
- package/src/lib/helpers/smartDotenvPrivateKey.js +10 -8
- package/src/lib/main.js +11 -3
- package/src/lib/services/decrypt.js +3 -2
- package/src/lib/services/encrypt.js +59 -23
- package/src/lib/services/get.js +3 -3
- package/src/lib/services/keypair.js +4 -8
- package/src/lib/services/run.js +3 -2
- package/src/lib/services/sets.js +58 -23
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,36 @@
|
|
|
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.30.1...main)
|
|
6
|
+
|
|
7
|
+
## [1.30.1](https://github.com/dotenvx/dotenvx/compare/v1.30.0...v1.30.1)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* support complex command substitution combining variable expansion ([#490](https://github.com/dotenvx/dotenvx/pull/490))
|
|
12
|
+
|
|
13
|
+
## [1.30.0](https://github.com/dotenvx/dotenvx/compare/v1.29.0...v1.30.0)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
* add `-fk` (`--env-keys-file`) flag to customize the path to your `.env.keys` file with `run, get, set, encrypt, decrypt, and keypair` 🎉 ([#486](https://github.com/dotenvx/dotenvx/pull/486))
|
|
18
|
+
|
|
19
|
+
This is great for monorepos. Maintain one `.env.keys` file across all your apps.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
$ dotenvx encrypt -fk .env.keys -f apps/backend/.env
|
|
23
|
+
$ dotenvx encrypt -fk .env.keys -f apps/frontend/.env
|
|
24
|
+
|
|
25
|
+
$ tree -a .
|
|
26
|
+
├── .env.keys
|
|
27
|
+
└── apps
|
|
28
|
+
├── backend
|
|
29
|
+
│ └── .env
|
|
30
|
+
└── frontend
|
|
31
|
+
└── .env
|
|
32
|
+
|
|
33
|
+
$ dotenvx get -fk .env.keys -f apps/backend/.env
|
|
34
|
+
```
|
|
6
35
|
|
|
7
36
|
## [1.29.0](https://github.com/dotenvx/dotenvx/compare/v1.28.0...v1.29.0)
|
|
8
37
|
|
package/README.md
CHANGED
|
@@ -583,7 +583,7 @@ More examples
|
|
|
583
583
|
</details>
|
|
584
584
|
* <details><summary>`--log-level` flag</summary><br>
|
|
585
585
|
|
|
586
|
-
Set `--log-level` to whatever you wish. For example, to
|
|
586
|
+
Set `--log-level` to whatever you wish. For example, to suppress warnings (risky), set log level to `error`:
|
|
587
587
|
|
|
588
588
|
```sh
|
|
589
589
|
$ echo "HELLO=production" > .env.production
|
|
@@ -956,7 +956,7 @@ More examples
|
|
|
956
956
|
</details>
|
|
957
957
|
* <details><summary>`run --log-level`</summary><br>
|
|
958
958
|
|
|
959
|
-
Set `--log-level` to whatever you wish. For example, to
|
|
959
|
+
Set `--log-level` to whatever you wish. For example, to suppress warnings (risky), set log level to `error`:
|
|
960
960
|
|
|
961
961
|
```sh
|
|
962
962
|
$ echo "HELLO=production" > .env.production
|
|
@@ -1014,6 +1014,19 @@ More examples
|
|
|
1014
1014
|
|
|
1015
1015
|
(more conventions available upon request)
|
|
1016
1016
|
|
|
1017
|
+
</details>
|
|
1018
|
+
* <details><summary>`run -fk`</summary><br>
|
|
1019
|
+
|
|
1020
|
+
Specify path to `.env.keys`. This is useful with monorepos.
|
|
1021
|
+
|
|
1022
|
+
```sh
|
|
1023
|
+
$ mkdir -p apps/app1
|
|
1024
|
+
$ touch apps/app1/.env
|
|
1025
|
+
$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env
|
|
1026
|
+
|
|
1027
|
+
$ dotenvx run -fk .env.keys -f apps/app1/.env -- yourcommand
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1017
1030
|
</details>
|
|
1018
1031
|
* <details><summary>`get KEY`</summary><br>
|
|
1019
1032
|
|
|
@@ -1039,6 +1052,20 @@ More examples
|
|
|
1039
1052
|
production
|
|
1040
1053
|
```
|
|
1041
1054
|
|
|
1055
|
+
</details>
|
|
1056
|
+
* <details><summary>`get KEY -fk`</summary><br>
|
|
1057
|
+
|
|
1058
|
+
Specify path to `.env.keys`. This is useful with monorepos.
|
|
1059
|
+
|
|
1060
|
+
```sh
|
|
1061
|
+
$ mkdir -p apps/app1
|
|
1062
|
+
$ touch apps/app1/.env
|
|
1063
|
+
$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env
|
|
1064
|
+
|
|
1065
|
+
$ dotenvx get HELLO -fk .env.keys -f apps/app1/.env
|
|
1066
|
+
world
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1042
1069
|
</details>
|
|
1043
1070
|
* <details><summary>`get KEY --env`</summary><br>
|
|
1044
1071
|
|
|
@@ -1212,6 +1239,32 @@ More examples
|
|
|
1212
1239
|
set HELLO with encryption (.env.production)
|
|
1213
1240
|
```
|
|
1214
1241
|
|
|
1242
|
+
</details>
|
|
1243
|
+
* <details><summary>`set KEY value -fk`</summary><br>
|
|
1244
|
+
|
|
1245
|
+
Specify path to `.env.keys`. This is useful with monorepos.
|
|
1246
|
+
|
|
1247
|
+
```sh
|
|
1248
|
+
$ mkdir -p apps/app1
|
|
1249
|
+
$ touch apps/app1/.env
|
|
1250
|
+
|
|
1251
|
+
$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env
|
|
1252
|
+
set HELLO with encryption (.env)
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
Put it to use.
|
|
1256
|
+
|
|
1257
|
+
```sh
|
|
1258
|
+
$ dotenvx get -fk .env.keys -f apps/app1/.env
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
Use it with a relative path.
|
|
1262
|
+
|
|
1263
|
+
```sh
|
|
1264
|
+
$ cd apps/app1
|
|
1265
|
+
$ dotenvx get -fk ../../.env.keys -f .env
|
|
1266
|
+
```
|
|
1267
|
+
|
|
1215
1268
|
</details>
|
|
1216
1269
|
* <details><summary>`set KEY "value with spaces"`</summary><br>
|
|
1217
1270
|
|
|
@@ -1279,6 +1332,32 @@ More examples
|
|
|
1279
1332
|
⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
|
|
1280
1333
|
```
|
|
1281
1334
|
|
|
1335
|
+
</details>
|
|
1336
|
+
* <details><summary>`encrypt -fk`</summary><br>
|
|
1337
|
+
|
|
1338
|
+
Specify path to `.env.keys`. This is useful with monorepos.
|
|
1339
|
+
|
|
1340
|
+
```sh
|
|
1341
|
+
$ mkdir -p apps/app1
|
|
1342
|
+
$ echo "HELLO=World" > apps/app1/.env
|
|
1343
|
+
|
|
1344
|
+
$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
|
|
1345
|
+
✔ encrypted (apps/app1/.env)
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
Put it to use.
|
|
1349
|
+
|
|
1350
|
+
```sh
|
|
1351
|
+
$ dotenvx run -fk .env.keys -f apps/app1/.env
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
Use with a relative path.
|
|
1355
|
+
|
|
1356
|
+
```sh
|
|
1357
|
+
$ cd apps/app1
|
|
1358
|
+
$ dotenvx run -fk ../../.env.keys -f .env
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1282
1361
|
</details>
|
|
1283
1362
|
* <details><summary>`encrypt -k`</summary><br>
|
|
1284
1363
|
|
|
@@ -1373,6 +1452,21 @@ More examples
|
|
|
1373
1452
|
✔ decrypted (.env.production)
|
|
1374
1453
|
```
|
|
1375
1454
|
|
|
1455
|
+
</details>
|
|
1456
|
+
* <details><summary>`decrypt -fk`</summary><br>
|
|
1457
|
+
|
|
1458
|
+
Specify path to `.env.keys`. This is useful with monorepos.
|
|
1459
|
+
|
|
1460
|
+
```sh
|
|
1461
|
+
$ mkdir -p apps/app1
|
|
1462
|
+
$ echo "HELLO=World" > apps/app1/.env
|
|
1463
|
+
|
|
1464
|
+
$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
|
|
1465
|
+
✔ encrypted (apps/app1/.env)
|
|
1466
|
+
$ dotenvx decrypt -fk .env.keys -f apps/app1/.env
|
|
1467
|
+
✔ decrypted (apps/app1/.env)
|
|
1468
|
+
```
|
|
1469
|
+
|
|
1376
1470
|
</details>
|
|
1377
1471
|
* <details><summary>`decrypt -k`</summary><br>
|
|
1378
1472
|
|
|
@@ -1455,7 +1549,7 @@ More examples
|
|
|
1455
1549
|
```
|
|
1456
1550
|
|
|
1457
1551
|
</details>
|
|
1458
|
-
* <details><summary>`keypair -f
|
|
1552
|
+
* <details><summary>`keypair -f`</summary><br>
|
|
1459
1553
|
|
|
1460
1554
|
Print public/private keys for `.env.production` file.
|
|
1461
1555
|
|
|
@@ -1467,6 +1561,20 @@ More examples
|
|
|
1467
1561
|
{"DOTENV_PUBLIC_KEY_PRODUCTION":"<publicKey>","DOTENV_PRIVATE_KEY_PRODUCTION":"<privateKey>"}
|
|
1468
1562
|
```
|
|
1469
1563
|
|
|
1564
|
+
</details>
|
|
1565
|
+
* <details><summary>`keypair -fk`</summary><br>
|
|
1566
|
+
|
|
1567
|
+
Specify path to `.env.keys`. This is useful for printing public/private keys for monorepos.
|
|
1568
|
+
|
|
1569
|
+
```sh
|
|
1570
|
+
$ mkdir -p apps/app1
|
|
1571
|
+
$ echo "HELLO=World" > apps/app1/.env
|
|
1572
|
+
$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
|
|
1573
|
+
|
|
1574
|
+
$ dotenvx keypair -fk .env.keys -f apps/app1/.env
|
|
1575
|
+
{"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}
|
|
1576
|
+
```
|
|
1577
|
+
|
|
1470
1578
|
</details>
|
|
1471
1579
|
* <details><summary>`keypair DOTENV_PRIVATE_KEY`</summary><br>
|
|
1472
1580
|
|
|
@@ -1904,6 +2012,21 @@ More examples
|
|
|
1904
2012
|
Hello World
|
|
1905
2013
|
```
|
|
1906
2014
|
|
|
2015
|
+
</details>
|
|
2016
|
+
* <details><summary>`config(envKeysFile:)` - envKeysFile</summary><br>
|
|
2017
|
+
|
|
2018
|
+
Use `envKeysFile` to customize the path to your `.env.keys` file. This is useful with monorepos.
|
|
2019
|
+
|
|
2020
|
+
```ini
|
|
2021
|
+
# .env
|
|
2022
|
+
HELLO="World"
|
|
2023
|
+
```
|
|
2024
|
+
|
|
2025
|
+
```js
|
|
2026
|
+
// index.js
|
|
2027
|
+
require('@dotenvx/dotenvx').config({envKeysFile: '../../.env.keys'})
|
|
2028
|
+
```
|
|
2029
|
+
|
|
1907
2030
|
</details>
|
|
1908
2031
|
* <details><summary>`parse(src)`</summary><br>
|
|
1909
2032
|
|
package/package.json
CHANGED
|
@@ -17,12 +17,15 @@ function decrypt () {
|
|
|
17
17
|
if (options.stdout) {
|
|
18
18
|
const {
|
|
19
19
|
processedEnvs
|
|
20
|
-
} = new Decrypt(envs, options.key, options.excludeKey).run()
|
|
20
|
+
} = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
21
21
|
|
|
22
22
|
for (const processedEnv of processedEnvs) {
|
|
23
23
|
if (processedEnv.error) {
|
|
24
24
|
errorCount += 1
|
|
25
25
|
console.error(processedEnv.error.message)
|
|
26
|
+
if (processedEnv.error.help) {
|
|
27
|
+
console.error(processedEnv.error.help)
|
|
28
|
+
}
|
|
26
29
|
} else {
|
|
27
30
|
console.log(processedEnv.envSrc)
|
|
28
31
|
}
|
|
@@ -39,7 +42,7 @@ function decrypt () {
|
|
|
39
42
|
processedEnvs,
|
|
40
43
|
changedFilepaths,
|
|
41
44
|
unchangedFilepaths
|
|
42
|
-
} = new Decrypt(envs, options.key, options.excludeKey).run()
|
|
45
|
+
} = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
43
46
|
|
|
44
47
|
for (const processedEnv of processedEnvs) {
|
|
45
48
|
logger.verbose(`decrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
@@ -52,6 +55,9 @@ function decrypt () {
|
|
|
52
55
|
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx decrypt]`)
|
|
53
56
|
} else {
|
|
54
57
|
console.error(processedEnv.error.message)
|
|
58
|
+
if (processedEnv.error.help) {
|
|
59
|
+
console.error(processedEnv.error.help)
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
} else if (processedEnv.changed) {
|
|
57
63
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
@@ -16,7 +16,7 @@ function encrypt () {
|
|
|
16
16
|
if (options.stdout) {
|
|
17
17
|
const {
|
|
18
18
|
processedEnvs
|
|
19
|
-
} = new Encrypt(envs, options.key, options.excludeKey).run()
|
|
19
|
+
} = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
20
20
|
|
|
21
21
|
for (const processedEnv of processedEnvs) {
|
|
22
22
|
console.log(processedEnv.envSrc)
|
|
@@ -28,7 +28,7 @@ function encrypt () {
|
|
|
28
28
|
processedEnvs,
|
|
29
29
|
changedFilepaths,
|
|
30
30
|
unchangedFilepaths
|
|
31
|
-
} = new Encrypt(envs, options.key, options.excludeKey).run()
|
|
31
|
+
} = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
32
32
|
|
|
33
33
|
for (const processedEnv of processedEnvs) {
|
|
34
34
|
logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
package/src/cli/actions/get.js
CHANGED
|
@@ -24,7 +24,7 @@ function get (key) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
const { parsed, errors } = new Get(key, envs, options.overload, process.env.DOTENV_KEY, options.all).run()
|
|
27
|
+
const { parsed, errors } = new Get(key, envs, options.overload, process.env.DOTENV_KEY, options.all, options.envKeysFile).run()
|
|
28
28
|
|
|
29
29
|
for (const error of errors || []) {
|
|
30
30
|
if (options.strict) throw error // throw immediately if strict
|
|
@@ -10,7 +10,7 @@ function keypair (key) {
|
|
|
10
10
|
const options = this.opts()
|
|
11
11
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
12
12
|
|
|
13
|
-
const results = main.keypair(options.envFile, key)
|
|
13
|
+
const results = main.keypair(options.envFile, key, options.envKeysFile)
|
|
14
14
|
|
|
15
15
|
if (typeof results === 'object' && results !== null) {
|
|
16
16
|
// inline shell format - env $(dotenvx keypair --format=shell) your-command
|
package/src/cli/actions/run.js
CHANGED
|
@@ -45,7 +45,7 @@ async function run () {
|
|
|
45
45
|
readableStrings,
|
|
46
46
|
readableFilepaths,
|
|
47
47
|
uniqueInjectedKeys
|
|
48
|
-
} = new Run(envs, options.overload, process.env.DOTENV_KEY).run()
|
|
48
|
+
} = new Run(envs, options.overload, process.env.DOTENV_KEY, process.env, options.envKeysFile).run()
|
|
49
49
|
|
|
50
50
|
for (const processedEnv of processedEnvs) {
|
|
51
51
|
if (processedEnv.type === 'envVaultFile') {
|
package/src/cli/actions/set.js
CHANGED
|
@@ -21,12 +21,13 @@ function set (key, value) {
|
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
23
|
const envs = this.envs
|
|
24
|
+
const envKeysFilepath = options.envKeysFile
|
|
24
25
|
|
|
25
26
|
const {
|
|
26
27
|
processedEnvs,
|
|
27
28
|
changedFilepaths,
|
|
28
29
|
unchangedFilepaths
|
|
29
|
-
} = new Sets(key, value, envs, encrypt).run()
|
|
30
|
+
} = new Sets(key, value, envs, encrypt, envKeysFilepath).run()
|
|
30
31
|
|
|
31
32
|
let withEncryption = ''
|
|
32
33
|
|
|
@@ -65,7 +66,7 @@ function set (key, value) {
|
|
|
65
66
|
|
|
66
67
|
for (const processedEnv of processedEnvs) {
|
|
67
68
|
if (processedEnv.privateKeyAdded) {
|
|
68
|
-
logger.success(`✔ key added to .
|
|
69
|
+
logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
|
|
69
70
|
|
|
70
71
|
if (!isIgnoringDotenvKeys()) {
|
|
71
72
|
logger.help('⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -56,6 +56,7 @@ program.command('run')
|
|
|
56
56
|
.addHelpText('after', examples.run)
|
|
57
57
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
58
58
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
59
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
59
60
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
60
61
|
.option('-o, --overload', 'override existing env variables')
|
|
61
62
|
.option('--strict', 'process.exit(1) on any errors (default: false)', false)
|
|
@@ -74,6 +75,7 @@ program.command('get')
|
|
|
74
75
|
.argument('[KEY]', 'environment variable name')
|
|
75
76
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
76
77
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
78
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
77
79
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
78
80
|
.option('-o, --overload', 'override existing env variables')
|
|
79
81
|
.option('--strict', 'process.exit(1) on any errors (default: false)', false)
|
|
@@ -97,6 +99,7 @@ program.command('set')
|
|
|
97
99
|
.argument('KEY', 'KEY')
|
|
98
100
|
.argument('value', 'value')
|
|
99
101
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
102
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
100
103
|
.option('-c, --encrypt', 'encrypt value (default: true)', true)
|
|
101
104
|
.option('-p, --plain', 'store value as plain text', false)
|
|
102
105
|
.action(function (...args) {
|
|
@@ -109,6 +112,7 @@ const encryptAction = require('./actions/encrypt')
|
|
|
109
112
|
program.command('encrypt')
|
|
110
113
|
.description('convert .env file(s) to encrypted .env file(s)')
|
|
111
114
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
115
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
112
116
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
113
117
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
114
118
|
.option('--stdout', 'send to stdout')
|
|
@@ -122,6 +126,7 @@ const decryptAction = require('./actions/decrypt')
|
|
|
122
126
|
program.command('decrypt')
|
|
123
127
|
.description('convert encrypted .env file(s) to plain .env file(s)')
|
|
124
128
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
129
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
125
130
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
126
131
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
127
132
|
.option('--stdout', 'send to stdout')
|
|
@@ -137,6 +142,7 @@ program.command('keypair')
|
|
|
137
142
|
.description('print public/private keys for .env file(s)')
|
|
138
143
|
.argument('[KEY]', 'environment variable key name')
|
|
139
144
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
145
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
140
146
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
141
147
|
.option('--format <type>', 'format of the output (json, shell)', 'json')
|
|
142
148
|
.action(keypairAction)
|
|
@@ -1,33 +1,18 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const childProcess = require('child_process')
|
|
3
|
-
|
|
4
1
|
// helpers
|
|
5
2
|
const guessPrivateKeyName = require('./guessPrivateKeyName')
|
|
3
|
+
const ProKeypair = require('./proKeypair')
|
|
6
4
|
|
|
7
5
|
// services
|
|
8
6
|
const Keypair = require('./../services/keypair')
|
|
9
7
|
|
|
10
|
-
function findPrivateKey (envFilepath) {
|
|
8
|
+
function findPrivateKey (envFilepath, envKeysFilepath = null) {
|
|
9
|
+
// use path/to/.env.${environment} to generate privateKeyName
|
|
11
10
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// if installed as sibling module
|
|
16
|
-
const projectRoot = path.resolve(process.cwd())
|
|
17
|
-
const dotenvxProPath = require.resolve('@dotenvx/dotenvx-pro', { paths: [projectRoot] })
|
|
18
|
-
const { keypair } = require(dotenvxProPath)
|
|
19
|
-
privateKey = keypair(envFilepath, privateKeyName)
|
|
20
|
-
} catch (_e) {
|
|
21
|
-
try {
|
|
22
|
-
// if installed as binary cli
|
|
23
|
-
privateKey = childProcess.execSync(`dotenvx-pro keypair ${privateKeyName} -f ${envFilepath}`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
24
|
-
} catch (_e) {
|
|
25
|
-
// fallback to local KeyPair - smart enough to handle process.env, .env.keys, etc
|
|
26
|
-
privateKey = new Keypair(envFilepath, privateKeyName).run()
|
|
27
|
-
}
|
|
28
|
-
}
|
|
12
|
+
const proKeypairs = new ProKeypair(envFilepath).run() // TODO: implement custom envKeysFilepath
|
|
13
|
+
const keypairs = new Keypair(envFilepath, envKeysFilepath).run()
|
|
29
14
|
|
|
30
|
-
return
|
|
15
|
+
return proKeypairs[privateKeyName] || keypairs[privateKeyName]
|
|
31
16
|
}
|
|
32
17
|
|
|
33
18
|
module.exports = findPrivateKey
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const childProcess = require('child_process')
|
|
3
|
-
|
|
4
1
|
// helpers
|
|
5
2
|
const guessPublicKeyName = require('./guessPublicKeyName')
|
|
3
|
+
const ProKeypair = require('./proKeypair')
|
|
6
4
|
|
|
7
5
|
// services
|
|
8
6
|
const Keypair = require('./../services/keypair')
|
|
@@ -10,24 +8,10 @@ const Keypair = require('./../services/keypair')
|
|
|
10
8
|
function findPublicKey (envFilepath) {
|
|
11
9
|
const publicKeyName = guessPublicKeyName(envFilepath)
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// if installed as sibling module
|
|
16
|
-
const projectRoot = path.resolve(process.cwd())
|
|
17
|
-
const dotenvxProPath = require.resolve('@dotenvx/dotenvx-pro', { paths: [projectRoot] })
|
|
18
|
-
const { keypair } = require(dotenvxProPath)
|
|
19
|
-
publicKey = keypair(envFilepath, publicKeyName)
|
|
20
|
-
} catch (_e) {
|
|
21
|
-
try {
|
|
22
|
-
// if installed as binary cli
|
|
23
|
-
publicKey = childProcess.execSync(`dotenvx-pro keypair ${publicKeyName} -f ${envFilepath}`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
24
|
-
} catch (_e) {
|
|
25
|
-
// fallback to local KeyPair - smart enough to handle process.env, .env.keys, etc
|
|
26
|
-
publicKey = new Keypair(envFilepath, publicKeyName).run()
|
|
27
|
-
}
|
|
28
|
-
}
|
|
11
|
+
const proKeypairs = new ProKeypair(envFilepath).run()
|
|
12
|
+
const keypairs = new Keypair(envFilepath).run()
|
|
29
13
|
|
|
30
|
-
return
|
|
14
|
+
return proKeypairs[publicKeyName] || keypairs[publicKeyName]
|
|
31
15
|
}
|
|
32
16
|
|
|
33
17
|
module.exports = findPublicKey
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { PrivateKey } = require('eciesjs')
|
|
2
2
|
|
|
3
|
-
function
|
|
3
|
+
function keypair (existingPrivateKey) {
|
|
4
4
|
let kp
|
|
5
5
|
|
|
6
6
|
if (existingPrivateKey) {
|
|
@@ -18,4 +18,4 @@ function keyPair (existingPrivateKey) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
module.exports =
|
|
21
|
+
module.exports = keypair
|
package/src/lib/helpers/parse.js
CHANGED
|
@@ -136,10 +136,9 @@ class Parse {
|
|
|
136
136
|
eval (value) {
|
|
137
137
|
// Match everything between the outermost $() using a regex with non-capturing groups
|
|
138
138
|
const matches = value.match(/\$\(([^)]+(?:\)[^(]*)*)\)/g) || []
|
|
139
|
-
|
|
140
|
-
return matches.reduce(function (newValue, match) {
|
|
139
|
+
return matches.reduce((newValue, match) => {
|
|
141
140
|
const command = match.slice(2, -1) // Extract command by removing $() wrapper
|
|
142
|
-
const result = chomp(execSync(command).toString()) // execute command
|
|
141
|
+
const result = chomp(execSync(command, { env: { ...this.processEnv, ...this.runningParsed } }).toString()) // execute command (including runningParsed)
|
|
143
142
|
return newValue.replace(match, result) // Replace match with result
|
|
144
143
|
}, value)
|
|
145
144
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const childProcess = require('child_process')
|
|
3
|
+
|
|
4
|
+
const guessPrivateKeyName = require('./guessPrivateKeyName')
|
|
5
|
+
const guessPublicKeyName = require('./guessPublicKeyName')
|
|
6
|
+
|
|
7
|
+
class ProKeypair {
|
|
8
|
+
constructor (envFilepath) {
|
|
9
|
+
this.envFilepath = envFilepath
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
run () {
|
|
13
|
+
let result = {}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// if installed as sibling module
|
|
17
|
+
const projectRoot = path.resolve(process.cwd())
|
|
18
|
+
const dotenvxProPath = require.resolve('@dotenvx/dotenvx-pro', { paths: [projectRoot] })
|
|
19
|
+
const { keypair } = require(dotenvxProPath)
|
|
20
|
+
|
|
21
|
+
result = keypair(this.envFilepath)
|
|
22
|
+
} catch (_e) {
|
|
23
|
+
try {
|
|
24
|
+
// if installed as binary cli
|
|
25
|
+
const output = childProcess.execSync(`dotenvx-pro keypair -f ${this.envFilepath}`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
26
|
+
|
|
27
|
+
result = JSON.parse(output)
|
|
28
|
+
} catch (_e) {
|
|
29
|
+
const privateKeyName = guessPrivateKeyName(this.envFilepath)
|
|
30
|
+
const publicKeyName = guessPublicKeyName(this.envFilepath)
|
|
31
|
+
|
|
32
|
+
// match format of dotenvx-pro
|
|
33
|
+
result[privateKeyName] = null
|
|
34
|
+
result[publicKeyName] = null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = ProKeypair
|
|
@@ -13,12 +13,14 @@ function searchProcessEnv (privateKeyName) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function searchKeysFile (privateKeyName, envFilepath) {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
function searchKeysFile (privateKeyName, envFilepath, envKeysFilepath = null) {
|
|
17
|
+
let keysFilepath = path.resolve(path.dirname(envFilepath), '.env.keys') // typical scenario
|
|
18
|
+
if (envKeysFilepath) { // user specified -fk flag
|
|
19
|
+
keysFilepath = path.resolve(envKeysFilepath)
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
if (fsx.existsSync(
|
|
21
|
-
const keysSrc = fsx.readFileX(
|
|
22
|
+
if (fsx.existsSync(keysFilepath)) {
|
|
23
|
+
const keysSrc = fsx.readFileX(keysFilepath)
|
|
22
24
|
const keysParsed = dotenv.parse(keysSrc)
|
|
23
25
|
|
|
24
26
|
if (keysParsed[privateKeyName] && keysParsed[privateKeyName].length > 0) {
|
|
@@ -49,7 +51,7 @@ function invertForPrivateKeyName (envFilepath) {
|
|
|
49
51
|
return null
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
function smartDotenvPrivateKey (envFilepath) {
|
|
54
|
+
function smartDotenvPrivateKey (envFilepath, envKeysFilepath = null) {
|
|
53
55
|
let privateKey = null
|
|
54
56
|
let privateKeyName = guessPrivateKeyName(envFilepath) // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
|
|
55
57
|
|
|
@@ -60,7 +62,7 @@ function smartDotenvPrivateKey (envFilepath) {
|
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
// 2. attempt .env.keys second (path/to/.env.keys)
|
|
63
|
-
privateKey = searchKeysFile(privateKeyName, envFilepath)
|
|
65
|
+
privateKey = searchKeysFile(privateKeyName, envFilepath, envKeysFilepath)
|
|
64
66
|
if (privateKey) {
|
|
65
67
|
return privateKey
|
|
66
68
|
}
|
|
@@ -75,7 +77,7 @@ function smartDotenvPrivateKey (envFilepath) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// 3.2. attempt .env.keys second (path/to/.env.keys)
|
|
78
|
-
privateKey = searchKeysFile(privateKeyName, envFilepath)
|
|
80
|
+
privateKey = searchKeysFile(privateKeyName, envFilepath, envKeysFilepath)
|
|
79
81
|
if (privateKey) {
|
|
80
82
|
return privateKey
|
|
81
83
|
}
|
package/src/lib/main.js
CHANGED
|
@@ -34,6 +34,9 @@ const config = function (options = {}) {
|
|
|
34
34
|
// strict
|
|
35
35
|
const strict = options.strict
|
|
36
36
|
|
|
37
|
+
// envKeysFile
|
|
38
|
+
const envKeysFile = options.envKeysFile
|
|
39
|
+
|
|
37
40
|
// DOTENV_KEY (DEPRECATED)
|
|
38
41
|
let DOTENV_KEY = process.env.DOTENV_KEY
|
|
39
42
|
if (options && options.DOTENV_KEY) {
|
|
@@ -70,7 +73,7 @@ const config = function (options = {}) {
|
|
|
70
73
|
processedEnvs,
|
|
71
74
|
readableFilepaths,
|
|
72
75
|
uniqueInjectedKeys
|
|
73
|
-
} = new Run(envs, overload, DOTENV_KEY, processEnv).run()
|
|
76
|
+
} = new Run(envs, overload, DOTENV_KEY, processEnv, envKeysFile).run()
|
|
74
77
|
|
|
75
78
|
let lastError
|
|
76
79
|
/** @type {Record<string, string>} */
|
|
@@ -190,8 +193,13 @@ const genexample = function (directory, envFile) {
|
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
/** @type {import('./main').keypair} */
|
|
193
|
-
const keypair = function (envFile, key) {
|
|
194
|
-
|
|
196
|
+
const keypair = function (envFile, key, envKeysFile = null) {
|
|
197
|
+
const keypairs = new Keypair(envFile, envKeysFile).run()
|
|
198
|
+
if (key) {
|
|
199
|
+
return keypairs[key]
|
|
200
|
+
} else {
|
|
201
|
+
return keypairs
|
|
202
|
+
}
|
|
195
203
|
}
|
|
196
204
|
|
|
197
205
|
module.exports = {
|
|
@@ -15,10 +15,11 @@ const detectEncoding = require('./../helpers/detectEncoding')
|
|
|
15
15
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
16
16
|
|
|
17
17
|
class Decrypt {
|
|
18
|
-
constructor (envs = [], key = [], excludeKey = []) {
|
|
18
|
+
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null) {
|
|
19
19
|
this.envs = determineEnvs(envs, process.env)
|
|
20
20
|
this.key = key
|
|
21
21
|
this.excludeKey = excludeKey
|
|
22
|
+
this.envKeysFilepath = envKeysFilepath
|
|
22
23
|
|
|
23
24
|
this.processedEnvs = []
|
|
24
25
|
this.changedFilepaths = new Set()
|
|
@@ -64,7 +65,7 @@ class Decrypt {
|
|
|
64
65
|
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
65
66
|
const envParsed = dotenv.parse(envSrc)
|
|
66
67
|
|
|
67
|
-
const privateKey = findPrivateKey(envFilepath)
|
|
68
|
+
const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
|
|
68
69
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
69
70
|
|
|
70
71
|
row.privateKey = privateKey
|
|
@@ -15,15 +15,16 @@ const detectEncoding = require('./../helpers/detectEncoding')
|
|
|
15
15
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
16
16
|
const findPrivateKey = require('./../helpers/findPrivateKey')
|
|
17
17
|
const findPublicKey = require('./../helpers/findPublicKey')
|
|
18
|
-
const
|
|
18
|
+
const keypair = require('./../helpers/keypair')
|
|
19
19
|
const truncate = require('./../helpers/truncate')
|
|
20
20
|
const isPublicKey = require('./../helpers/isPublicKey')
|
|
21
21
|
|
|
22
22
|
class Encrypt {
|
|
23
|
-
constructor (envs = [], key = [], excludeKey = []) {
|
|
23
|
+
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null) {
|
|
24
24
|
this.envs = determineEnvs(envs, process.env)
|
|
25
25
|
this.key = key
|
|
26
26
|
this.excludeKey = excludeKey
|
|
27
|
+
this.envKeysFilepath = envKeysFilepath
|
|
27
28
|
|
|
28
29
|
this.processedEnvs = []
|
|
29
30
|
this.changedFilepaths = new Set()
|
|
@@ -75,11 +76,17 @@ class Encrypt {
|
|
|
75
76
|
|
|
76
77
|
const publicKeyName = guessPublicKeyName(envFilepath)
|
|
77
78
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
78
|
-
const existingPrivateKey = findPrivateKey(envFilepath)
|
|
79
|
+
const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
|
|
79
80
|
const existingPublicKey = findPublicKey(envFilepath)
|
|
80
81
|
|
|
82
|
+
let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
83
|
+
if (this.envKeysFilepath) {
|
|
84
|
+
envKeysFilepath = path.resolve(this.envKeysFilepath)
|
|
85
|
+
}
|
|
86
|
+
const relativeFilepath = path.relative(path.dirname(filepath), envKeysFilepath)
|
|
87
|
+
|
|
81
88
|
if (existingPrivateKey) {
|
|
82
|
-
const kp =
|
|
89
|
+
const kp = keypair(existingPrivateKey)
|
|
83
90
|
publicKey = kp.publicKey
|
|
84
91
|
privateKey = kp.privateKey
|
|
85
92
|
|
|
@@ -90,38 +97,36 @@ class Encrypt {
|
|
|
90
97
|
error.help = `debug info: ${privateKeyName}=${truncate(existingPrivateKey)} (derived ${publicKeyName}=${truncate(publicKey)} vs existing ${publicKeyName}=${truncate(existingPublicKey)})`
|
|
91
98
|
throw error
|
|
92
99
|
}
|
|
100
|
+
|
|
101
|
+
// typical scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
|
|
102
|
+
if (!existingPublicKey) {
|
|
103
|
+
const ps = this._preserveShebang(envSrc)
|
|
104
|
+
const firstLinePreserved = ps.firstLinePreserved
|
|
105
|
+
envSrc = ps.envSrc
|
|
106
|
+
|
|
107
|
+
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
108
|
+
|
|
109
|
+
envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
|
|
110
|
+
}
|
|
93
111
|
} else if (existingPublicKey) {
|
|
94
112
|
publicKey = existingPublicKey
|
|
95
113
|
} else {
|
|
96
114
|
// .env.keys
|
|
97
115
|
let keysSrc = ''
|
|
98
|
-
|
|
116
|
+
|
|
99
117
|
if (fsx.existsSync(envKeysFilepath)) {
|
|
100
118
|
keysSrc = fsx.readFileX(envKeysFilepath)
|
|
101
119
|
}
|
|
102
120
|
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
if (firstLine.startsWith('#!')) {
|
|
107
|
-
firstLinePreserved = firstLine + '\n'
|
|
108
|
-
envSrc = remainingLines.join('\n')
|
|
109
|
-
}
|
|
121
|
+
const ps = this._preserveShebang(envSrc)
|
|
122
|
+
const firstLinePreserved = ps.firstLinePreserved
|
|
123
|
+
envSrc = ps.envSrc
|
|
110
124
|
|
|
111
|
-
const kp =
|
|
125
|
+
const kp = keypair() // generates a fresh keypair in memory
|
|
112
126
|
publicKey = kp.publicKey
|
|
113
127
|
privateKey = kp.privateKey
|
|
114
128
|
|
|
115
|
-
|
|
116
|
-
const prependPublicKey = [
|
|
117
|
-
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
118
|
-
'#/ public-key encryption for .env files /',
|
|
119
|
-
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
120
|
-
'#/----------------------------------------------------------/',
|
|
121
|
-
`${publicKeyName}="${publicKey}"`,
|
|
122
|
-
'',
|
|
123
|
-
`# ${filename}`
|
|
124
|
-
].join('\n')
|
|
129
|
+
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
125
130
|
|
|
126
131
|
// privateKey
|
|
127
132
|
const firstTimeKeysSrc = [
|
|
@@ -144,6 +149,7 @@ class Encrypt {
|
|
|
144
149
|
fsx.writeFileX(envKeysFilepath, keysSrc)
|
|
145
150
|
|
|
146
151
|
row.privateKeyAdded = true
|
|
152
|
+
row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), path.basename(envKeysFilepath))
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
row.publicKey = publicKey
|
|
@@ -210,6 +216,36 @@ class Encrypt {
|
|
|
210
216
|
_detectEncoding (filepath) {
|
|
211
217
|
return detectEncoding(filepath)
|
|
212
218
|
}
|
|
219
|
+
|
|
220
|
+
_prependPublicKey (publicKeyName, publicKey, filename, relativeFilepath = '') {
|
|
221
|
+
const comment = relativeFilepath === '.env.keys' ? '' : ` # ${relativeFilepath}`
|
|
222
|
+
|
|
223
|
+
return [
|
|
224
|
+
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
225
|
+
'#/ public-key encryption for .env files /',
|
|
226
|
+
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
227
|
+
'#/----------------------------------------------------------/',
|
|
228
|
+
`${publicKeyName}="${publicKey}"${comment}`,
|
|
229
|
+
'',
|
|
230
|
+
`# ${filename}`
|
|
231
|
+
].join('\n')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
_preserveShebang (envSrc) {
|
|
235
|
+
// preserve shebang
|
|
236
|
+
const [firstLine, ...remainingLines] = envSrc.split('\n')
|
|
237
|
+
let firstLinePreserved = ''
|
|
238
|
+
|
|
239
|
+
if (firstLine.startsWith('#!')) {
|
|
240
|
+
firstLinePreserved = firstLine + '\n'
|
|
241
|
+
envSrc = remainingLines.join('\n')
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
firstLinePreserved,
|
|
246
|
+
envSrc
|
|
247
|
+
}
|
|
248
|
+
}
|
|
213
249
|
}
|
|
214
250
|
|
|
215
251
|
module.exports = Encrypt
|
package/src/lib/services/get.js
CHANGED
|
@@ -2,18 +2,18 @@ const Run = require('./run')
|
|
|
2
2
|
const Errors = require('./../helpers/errors')
|
|
3
3
|
|
|
4
4
|
class Get {
|
|
5
|
-
constructor (key, envs = [], overload = false, DOTENV_KEY = '', all = false,
|
|
5
|
+
constructor (key, envs = [], overload = false, DOTENV_KEY = '', all = false, envKeysFilepath = null) {
|
|
6
6
|
this.key = key
|
|
7
7
|
this.envs = envs
|
|
8
8
|
this.overload = overload
|
|
9
9
|
this.DOTENV_KEY = DOTENV_KEY
|
|
10
10
|
this.all = all
|
|
11
|
-
this.
|
|
11
|
+
this.envKeysFilepath = envKeysFilepath
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
run () {
|
|
15
15
|
const processEnv = { ...process.env }
|
|
16
|
-
const { processedEnvs } = new Run(this.envs, this.overload, this.DOTENV_KEY, processEnv).run()
|
|
16
|
+
const { processedEnvs } = new Run(this.envs, this.overload, this.DOTENV_KEY, processEnv, this.envKeysFilepath).run()
|
|
17
17
|
|
|
18
18
|
const errors = []
|
|
19
19
|
for (const processedEnv of processedEnvs) {
|
|
@@ -4,9 +4,9 @@ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
|
4
4
|
const smartDotenvPrivateKey = require('./../helpers/smartDotenvPrivateKey')
|
|
5
5
|
|
|
6
6
|
class Keypair {
|
|
7
|
-
constructor (envFile = '.env',
|
|
7
|
+
constructor (envFile = '.env', envKeysFilepath = null) {
|
|
8
8
|
this.envFile = envFile
|
|
9
|
-
this.
|
|
9
|
+
this.envKeysFilepath = envKeysFilepath
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
run () {
|
|
@@ -21,16 +21,12 @@ class Keypair {
|
|
|
21
21
|
|
|
22
22
|
// private key
|
|
23
23
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
24
|
-
const privateKeyValue = smartDotenvPrivateKey(envFilepath)
|
|
24
|
+
const privateKeyValue = smartDotenvPrivateKey(envFilepath, this.envKeysFilepath)
|
|
25
25
|
|
|
26
26
|
out[privateKeyName] = privateKeyValue
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
return out[this.key]
|
|
31
|
-
} else {
|
|
32
|
-
return out
|
|
33
|
-
}
|
|
29
|
+
return out
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
_envFilepaths () {
|
package/src/lib/services/run.js
CHANGED
|
@@ -16,11 +16,12 @@ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
|
16
16
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
17
17
|
|
|
18
18
|
class Run {
|
|
19
|
-
constructor (envs = [], overload = false, DOTENV_KEY = '', processEnv = process.env) {
|
|
19
|
+
constructor (envs = [], overload = false, DOTENV_KEY = '', processEnv = process.env, envKeysFilepath = null) {
|
|
20
20
|
this.envs = determineEnvs(envs, processEnv, DOTENV_KEY)
|
|
21
21
|
this.overload = overload
|
|
22
22
|
this.DOTENV_KEY = DOTENV_KEY
|
|
23
23
|
this.processEnv = processEnv
|
|
24
|
+
this.envKeysFilepath = envKeysFilepath
|
|
24
25
|
|
|
25
26
|
this.processedEnvs = []
|
|
26
27
|
this.readableFilepaths = new Set()
|
|
@@ -92,7 +93,7 @@ class Run {
|
|
|
92
93
|
const src = fsx.readFileX(filepath, { encoding })
|
|
93
94
|
this.readableFilepaths.add(envFilepath)
|
|
94
95
|
|
|
95
|
-
const privateKey = findPrivateKey(envFilepath)
|
|
96
|
+
const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
|
|
96
97
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
97
98
|
const { parsed, errors, injected, preExisted } = new Parse(src, privateKey, this.processEnv, this.overload, privateKeyName).run()
|
|
98
99
|
|
package/src/lib/services/sets.js
CHANGED
|
@@ -14,16 +14,17 @@ const detectEncoding = require('./../helpers/detectEncoding')
|
|
|
14
14
|
const determineEnvs = require('./../helpers/determineEnvs')
|
|
15
15
|
const findPrivateKey = require('./../helpers/findPrivateKey')
|
|
16
16
|
const findPublicKey = require('./../helpers/findPublicKey')
|
|
17
|
-
const
|
|
17
|
+
const keypair = require('./../helpers/keypair')
|
|
18
18
|
const truncate = require('./../helpers/truncate')
|
|
19
19
|
const isEncrypted = require('./../helpers/isEncrypted')
|
|
20
20
|
|
|
21
21
|
class Sets {
|
|
22
|
-
constructor (key, value, envs = [], encrypt = true) {
|
|
22
|
+
constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null) {
|
|
23
23
|
this.envs = determineEnvs(envs, process.env)
|
|
24
24
|
this.key = key
|
|
25
25
|
this.value = value
|
|
26
26
|
this.encrypt = encrypt
|
|
27
|
+
this.envKeysFilepath = envKeysFilepath
|
|
27
28
|
|
|
28
29
|
this.processedEnvs = []
|
|
29
30
|
this.changedFilepaths = new Set()
|
|
@@ -76,11 +77,17 @@ class Sets {
|
|
|
76
77
|
|
|
77
78
|
const publicKeyName = guessPublicKeyName(envFilepath)
|
|
78
79
|
const privateKeyName = guessPrivateKeyName(envFilepath)
|
|
79
|
-
const existingPrivateKey = findPrivateKey(envFilepath)
|
|
80
|
+
const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
|
|
80
81
|
const existingPublicKey = findPublicKey(envFilepath)
|
|
81
82
|
|
|
83
|
+
let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
84
|
+
if (this.envKeysFilepath) {
|
|
85
|
+
envKeysFilepath = path.resolve(this.envKeysFilepath)
|
|
86
|
+
}
|
|
87
|
+
const relativeFilepath = path.relative(path.dirname(filepath), envKeysFilepath)
|
|
88
|
+
|
|
82
89
|
if (existingPrivateKey) {
|
|
83
|
-
const kp =
|
|
90
|
+
const kp = keypair(existingPrivateKey)
|
|
84
91
|
publicKey = kp.publicKey
|
|
85
92
|
privateKey = kp.privateKey
|
|
86
93
|
|
|
@@ -95,38 +102,35 @@ class Sets {
|
|
|
95
102
|
error.help = `debug info: ${privateKeyName}=${truncate(existingPrivateKey)} (derived ${publicKeyName}=${truncate(publicKey)} vs existing ${publicKeyName}=${truncate(existingPublicKey)})`
|
|
96
103
|
throw error
|
|
97
104
|
}
|
|
105
|
+
|
|
106
|
+
// typical scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
|
|
107
|
+
if (!existingPublicKey) {
|
|
108
|
+
const ps = this._preserveShebang(envSrc)
|
|
109
|
+
const firstLinePreserved = ps.firstLinePreserved
|
|
110
|
+
envSrc = ps.envSrc
|
|
111
|
+
|
|
112
|
+
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
113
|
+
|
|
114
|
+
envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
|
|
115
|
+
}
|
|
98
116
|
} else if (existingPublicKey) {
|
|
99
117
|
publicKey = existingPublicKey
|
|
100
118
|
} else {
|
|
101
119
|
// .env.keys
|
|
102
120
|
let keysSrc = ''
|
|
103
|
-
const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
104
121
|
if (fsx.existsSync(envKeysFilepath)) {
|
|
105
122
|
keysSrc = fsx.readFileX(envKeysFilepath)
|
|
106
123
|
}
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
if (firstLine.startsWith('#!')) {
|
|
112
|
-
firstLinePreserved = firstLine + '\n'
|
|
113
|
-
envSrc = remainingLines.join('\n')
|
|
114
|
-
}
|
|
125
|
+
const ps = this._preserveShebang(envSrc)
|
|
126
|
+
const firstLinePreserved = ps.firstLinePreserved
|
|
127
|
+
envSrc = ps.envSrc
|
|
115
128
|
|
|
116
|
-
const kp =
|
|
129
|
+
const kp = keypair() // generates a fresh keypair in memory
|
|
117
130
|
publicKey = kp.publicKey
|
|
118
131
|
privateKey = kp.privateKey
|
|
119
132
|
|
|
120
|
-
|
|
121
|
-
const prependPublicKey = [
|
|
122
|
-
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
123
|
-
'#/ public-key encryption for .env files /',
|
|
124
|
-
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
125
|
-
'#/----------------------------------------------------------/',
|
|
126
|
-
`${publicKeyName}="${publicKey}"`,
|
|
127
|
-
'',
|
|
128
|
-
`# ${filename}`
|
|
129
|
-
].join('\n')
|
|
133
|
+
const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
|
|
130
134
|
|
|
131
135
|
// privateKey
|
|
132
136
|
const firstTimeKeysSrc = [
|
|
@@ -149,6 +153,7 @@ class Sets {
|
|
|
149
153
|
fsx.writeFileX(envKeysFilepath, keysSrc)
|
|
150
154
|
|
|
151
155
|
row.privateKeyAdded = true
|
|
156
|
+
row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), path.basename(envKeysFilepath))
|
|
152
157
|
}
|
|
153
158
|
|
|
154
159
|
row.publicKey = publicKey
|
|
@@ -182,6 +187,36 @@ class Sets {
|
|
|
182
187
|
_detectEncoding (filepath) {
|
|
183
188
|
return detectEncoding(filepath)
|
|
184
189
|
}
|
|
190
|
+
|
|
191
|
+
_prependPublicKey (publicKeyName, publicKey, filename, relativeFilepath = '.env.keys') {
|
|
192
|
+
const comment = relativeFilepath === '.env.keys' ? '' : ` # -fk ${relativeFilepath}`
|
|
193
|
+
|
|
194
|
+
return [
|
|
195
|
+
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
196
|
+
'#/ public-key encryption for .env files /',
|
|
197
|
+
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
198
|
+
'#/----------------------------------------------------------/',
|
|
199
|
+
`${publicKeyName}="${publicKey}"${comment}`,
|
|
200
|
+
'',
|
|
201
|
+
`# ${filename}`
|
|
202
|
+
].join('\n')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_preserveShebang (envSrc) {
|
|
206
|
+
// preserve shebang
|
|
207
|
+
const [firstLine, ...remainingLines] = envSrc.split('\n')
|
|
208
|
+
let firstLinePreserved = ''
|
|
209
|
+
|
|
210
|
+
if (firstLine.startsWith('#!')) {
|
|
211
|
+
firstLinePreserved = firstLine + '\n'
|
|
212
|
+
envSrc = remainingLines.join('\n')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
firstLinePreserved,
|
|
217
|
+
envSrc
|
|
218
|
+
}
|
|
219
|
+
}
|
|
185
220
|
}
|
|
186
221
|
|
|
187
222
|
module.exports = Sets
|