@dotenvx/dotenvx 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/README.md +178 -9
- 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 +7 -1
- package/src/cli/actions/keypair.js +1 -1
- package/src/cli/actions/run.js +8 -1
- package/src/cli/actions/set.js +3 -2
- package/src/cli/dotenvx.js +8 -0
- package/src/lib/helpers/errors.js +1 -1
- 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/proKeypair.js +42 -0
- package/src/lib/helpers/smartDotenvPrivateKey.js +10 -8
- package/src/lib/main.js +20 -5
- 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.0...main)
|
|
6
|
+
|
|
7
|
+
## [1.30.0](https://github.com/dotenvx/dotenvx/compare/v1.29.0...v1.30.0)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* 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))
|
|
12
|
+
|
|
13
|
+
This is great for monorepos. Maintain one `.env.keys` file across multiple monorepos `.env*` files.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
$ dotenvx encrypt -fk .env.keys -f apps/backend/.env
|
|
17
|
+
$ dotenvx encrypt -fk .env.keys -f apps/frontend/.env
|
|
18
|
+
|
|
19
|
+
$ tree -a .
|
|
20
|
+
├── .env.keys
|
|
21
|
+
└── apps
|
|
22
|
+
├── backend
|
|
23
|
+
│ └── .env
|
|
24
|
+
└── frontend
|
|
25
|
+
└── .env
|
|
26
|
+
|
|
27
|
+
$ dotenvx get -fk .env.keys -f apps/backend/.env
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## [1.29.0](https://github.com/dotenvx/dotenvx/compare/v1.28.0...v1.29.0)
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
* add `--ignore` flag to suppress specified errors. example: `dotenvx run --ignore=MISSING_ENV_FILE` ([#485](https://github.com/dotenvx/dotenvx/pull/485))
|
|
6
35
|
|
|
7
36
|
## [1.28.0](https://github.com/dotenvx/dotenvx/compare/v1.27.0...v1.28.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
|
|
@@ -983,6 +983,18 @@ More examples
|
|
|
983
983
|
|
|
984
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
985
|
|
|
986
|
+
</details>
|
|
987
|
+
* <details><summary>`run --ignore`</summary><br>
|
|
988
|
+
|
|
989
|
+
Ignore errors like `MISSING_ENV_FILE`.
|
|
990
|
+
|
|
991
|
+
```sh
|
|
992
|
+
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
|
993
|
+
|
|
994
|
+
$ dotenvx run -f .env.missing --ignore=MISSING_ENV_FILE -- node index.js
|
|
995
|
+
...
|
|
996
|
+
```
|
|
997
|
+
|
|
986
998
|
</details>
|
|
987
999
|
* <details><summary>`run --convention=nextjs`</summary><br>
|
|
988
1000
|
|
|
@@ -1002,6 +1014,19 @@ More examples
|
|
|
1002
1014
|
|
|
1003
1015
|
(more conventions available upon request)
|
|
1004
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
|
+
|
|
1005
1030
|
</details>
|
|
1006
1031
|
* <details><summary>`get KEY`</summary><br>
|
|
1007
1032
|
|
|
@@ -1027,6 +1052,20 @@ More examples
|
|
|
1027
1052
|
production
|
|
1028
1053
|
```
|
|
1029
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
|
+
|
|
1030
1069
|
</details>
|
|
1031
1070
|
* <details><summary>`get KEY --env`</summary><br>
|
|
1032
1071
|
|
|
@@ -1200,6 +1239,32 @@ More examples
|
|
|
1200
1239
|
set HELLO with encryption (.env.production)
|
|
1201
1240
|
```
|
|
1202
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
|
+
|
|
1203
1268
|
</details>
|
|
1204
1269
|
* <details><summary>`set KEY "value with spaces"`</summary><br>
|
|
1205
1270
|
|
|
@@ -1267,6 +1332,32 @@ More examples
|
|
|
1267
1332
|
⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
|
|
1268
1333
|
```
|
|
1269
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
|
+
|
|
1270
1361
|
</details>
|
|
1271
1362
|
* <details><summary>`encrypt -k`</summary><br>
|
|
1272
1363
|
|
|
@@ -1361,6 +1452,21 @@ More examples
|
|
|
1361
1452
|
✔ decrypted (.env.production)
|
|
1362
1453
|
```
|
|
1363
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
|
+
|
|
1364
1470
|
</details>
|
|
1365
1471
|
* <details><summary>`decrypt -k`</summary><br>
|
|
1366
1472
|
|
|
@@ -1443,7 +1549,7 @@ More examples
|
|
|
1443
1549
|
```
|
|
1444
1550
|
|
|
1445
1551
|
</details>
|
|
1446
|
-
* <details><summary>`keypair -f
|
|
1552
|
+
* <details><summary>`keypair -f`</summary><br>
|
|
1447
1553
|
|
|
1448
1554
|
Print public/private keys for `.env.production` file.
|
|
1449
1555
|
|
|
@@ -1455,6 +1561,20 @@ More examples
|
|
|
1455
1561
|
{"DOTENV_PUBLIC_KEY_PRODUCTION":"<publicKey>","DOTENV_PRIVATE_KEY_PRODUCTION":"<privateKey>"}
|
|
1456
1562
|
```
|
|
1457
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
|
+
|
|
1458
1578
|
</details>
|
|
1459
1579
|
* <details><summary>`keypair DOTENV_PRIVATE_KEY`</summary><br>
|
|
1460
1580
|
|
|
@@ -1695,9 +1815,17 @@ More examples
|
|
|
1695
1815
|
|
|
1696
1816
|
```sh
|
|
1697
1817
|
$ dotenvx ext gitignore
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1818
|
+
✔ ignored .env* (.gitignore)
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
</details>
|
|
1822
|
+
* <details><summary>`ext gitignore --pattern`</summary><br>
|
|
1823
|
+
|
|
1824
|
+
Gitignore specific pattern(s) of `.env` files.
|
|
1825
|
+
|
|
1826
|
+
```sh
|
|
1827
|
+
$ dotenvx ext gitignore --pattern .env.keys
|
|
1828
|
+
✔ ignored .env.keys (.gitignore)
|
|
1701
1829
|
```
|
|
1702
1830
|
|
|
1703
1831
|
</details>
|
|
@@ -1707,7 +1835,7 @@ More examples
|
|
|
1707
1835
|
|
|
1708
1836
|
```sh
|
|
1709
1837
|
$ dotenvx ext precommit
|
|
1710
|
-
[dotenvx][precommit]
|
|
1838
|
+
[dotenvx][precommit] .env files (1) protected (encrypted or gitignored)
|
|
1711
1839
|
```
|
|
1712
1840
|
|
|
1713
1841
|
</details>
|
|
@@ -1717,7 +1845,7 @@ More examples
|
|
|
1717
1845
|
|
|
1718
1846
|
```sh
|
|
1719
1847
|
$ dotenvx ext precommit --install
|
|
1720
|
-
[dotenvx][precommit] dotenvx precommit installed [.git/hooks/pre-commit]
|
|
1848
|
+
[dotenvx][precommit] dotenvx ext precommit installed [.git/hooks/pre-commit]
|
|
1721
1849
|
```
|
|
1722
1850
|
|
|
1723
1851
|
</details>
|
|
@@ -1728,6 +1856,7 @@ More examples
|
|
|
1728
1856
|
Add it to your `Dockerfile`.
|
|
1729
1857
|
|
|
1730
1858
|
```sh
|
|
1859
|
+
# Dockerfile
|
|
1731
1860
|
RUN curl -fsS https://dotenvx.sh | sh
|
|
1732
1861
|
|
|
1733
1862
|
...
|
|
@@ -1780,6 +1909,8 @@ More examples
|
|
|
1780
1909
|
Hello World
|
|
1781
1910
|
```
|
|
1782
1911
|
|
|
1912
|
+
It defaults to looking for a `.env` file.
|
|
1913
|
+
|
|
1783
1914
|
</details>
|
|
1784
1915
|
* <details><summary>`config(path: ['.env.local', '.env'])` - multiple files</summary><br>
|
|
1785
1916
|
|
|
@@ -1811,7 +1942,7 @@ More examples
|
|
|
1811
1942
|
</details>
|
|
1812
1943
|
* <details><summary>`config(overload: true)` - overload</summary><br>
|
|
1813
1944
|
|
|
1814
|
-
|
|
1945
|
+
Use `overload` to overwrite the prior set value.
|
|
1815
1946
|
|
|
1816
1947
|
```ini
|
|
1817
1948
|
# .env.local
|
|
@@ -1858,6 +1989,44 @@ More examples
|
|
|
1858
1989
|
Error: [MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
|
|
1859
1990
|
```
|
|
1860
1991
|
|
|
1992
|
+
</details>
|
|
1993
|
+
* <details><summary>`config(ignore:)` - ignore</summary><br>
|
|
1994
|
+
|
|
1995
|
+
Use `ignore` to suppress specific errors like `MISSING_ENV_FILE`.
|
|
1996
|
+
|
|
1997
|
+
```ini
|
|
1998
|
+
# .env
|
|
1999
|
+
HELLO="World"
|
|
2000
|
+
```
|
|
2001
|
+
|
|
2002
|
+
```js
|
|
2003
|
+
// index.js
|
|
2004
|
+
require('@dotenvx/dotenvx').config({path: ['.env.missing', '.env'], ignore: ['MISSING_ENV_FILE']})
|
|
2005
|
+
|
|
2006
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
2007
|
+
```
|
|
2008
|
+
|
|
2009
|
+
```sh
|
|
2010
|
+
$ node index.js
|
|
2011
|
+
[dotenvx@1.24.5] injecting env (1) from .env.local, .env
|
|
2012
|
+
Hello World
|
|
2013
|
+
```
|
|
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
|
+
|
|
1861
2030
|
</details>
|
|
1862
2031
|
* <details><summary>`parse(src)`</summary><br>
|
|
1863
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
|
@@ -13,6 +13,8 @@ function get (key) {
|
|
|
13
13
|
const options = this.opts()
|
|
14
14
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
15
15
|
|
|
16
|
+
const ignore = options.ignore || []
|
|
17
|
+
|
|
16
18
|
let envs = []
|
|
17
19
|
// handle shorthand conventions - like --convention=nextjs
|
|
18
20
|
if (options.convention) {
|
|
@@ -22,11 +24,15 @@ function get (key) {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
try {
|
|
25
|
-
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()
|
|
26
28
|
|
|
27
29
|
for (const error of errors || []) {
|
|
28
30
|
if (options.strict) throw error // throw immediately if strict
|
|
29
31
|
|
|
32
|
+
if (ignore.includes(error.code)) {
|
|
33
|
+
continue // ignore error
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
console.error(error.message)
|
|
31
37
|
if (error.help) {
|
|
32
38
|
console.error(error.help)
|
|
@@ -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
|
@@ -14,6 +14,8 @@ async function run () {
|
|
|
14
14
|
const options = this.opts()
|
|
15
15
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
16
16
|
|
|
17
|
+
const ignore = options.ignore || []
|
|
18
|
+
|
|
17
19
|
if (commandArgs.length < 1) {
|
|
18
20
|
const hasSeparator = process.argv.indexOf('--') !== -1
|
|
19
21
|
|
|
@@ -43,7 +45,7 @@ async function run () {
|
|
|
43
45
|
readableStrings,
|
|
44
46
|
readableFilepaths,
|
|
45
47
|
uniqueInjectedKeys
|
|
46
|
-
} = 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()
|
|
47
49
|
|
|
48
50
|
for (const processedEnv of processedEnvs) {
|
|
49
51
|
if (processedEnv.type === 'envVaultFile') {
|
|
@@ -62,6 +64,11 @@ async function run () {
|
|
|
62
64
|
for (const error of processedEnv.errors || []) {
|
|
63
65
|
if (options.strict) throw error // throw immediately if strict
|
|
64
66
|
|
|
67
|
+
if (ignore.includes(error.code)) {
|
|
68
|
+
logger.verbose(`ignored: ${error.message}`)
|
|
69
|
+
continue // ignore error
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
if (error.code === 'MISSING_ENV_FILE') {
|
|
66
73
|
if (!options.convention) { // do not output error for conventions (too noisy)
|
|
67
74
|
console.error(error.message)
|
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,10 +56,12 @@ 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)
|
|
62
63
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
|
|
64
|
+
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
63
65
|
.action(function (...args) {
|
|
64
66
|
this.envs = envs
|
|
65
67
|
runAction.apply(this, args)
|
|
@@ -73,10 +75,12 @@ program.command('get')
|
|
|
73
75
|
.argument('[KEY]', 'environment variable name')
|
|
74
76
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
75
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)')
|
|
76
79
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
77
80
|
.option('-o, --overload', 'override existing env variables')
|
|
78
81
|
.option('--strict', 'process.exit(1) on any errors (default: false)', false)
|
|
79
82
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
|
|
83
|
+
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
80
84
|
.option('-a, --all', 'include all machine envs as well')
|
|
81
85
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
82
86
|
.option('--format <type>', 'format of the output (json, shell, eval)', 'json')
|
|
@@ -95,6 +99,7 @@ program.command('set')
|
|
|
95
99
|
.argument('KEY', 'KEY')
|
|
96
100
|
.argument('value', 'value')
|
|
97
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)')
|
|
98
103
|
.option('-c, --encrypt', 'encrypt value (default: true)', true)
|
|
99
104
|
.option('-p, --plain', 'store value as plain text', false)
|
|
100
105
|
.action(function (...args) {
|
|
@@ -107,6 +112,7 @@ const encryptAction = require('./actions/encrypt')
|
|
|
107
112
|
program.command('encrypt')
|
|
108
113
|
.description('convert .env file(s) to encrypted .env file(s)')
|
|
109
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)')
|
|
110
116
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
111
117
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
112
118
|
.option('--stdout', 'send to stdout')
|
|
@@ -120,6 +126,7 @@ const decryptAction = require('./actions/decrypt')
|
|
|
120
126
|
program.command('decrypt')
|
|
121
127
|
.description('convert encrypted .env file(s) to plain .env file(s)')
|
|
122
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)')
|
|
123
130
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
124
131
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
125
132
|
.option('--stdout', 'send to stdout')
|
|
@@ -135,6 +142,7 @@ program.command('keypair')
|
|
|
135
142
|
.description('print public/private keys for .env file(s)')
|
|
136
143
|
.argument('[KEY]', 'environment variable key name')
|
|
137
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)')
|
|
138
146
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
139
147
|
.option('--format <type>', 'format of the output (json, shell)', 'json')
|
|
140
148
|
.action(keypairAction)
|
|
@@ -15,7 +15,7 @@ class Errors {
|
|
|
15
15
|
missingEnvFile () {
|
|
16
16
|
const code = 'MISSING_ENV_FILE'
|
|
17
17
|
const message = `[${code}] missing ${this.envFilepath} file (${this.filepath})`
|
|
18
|
-
const help = `[${code}]
|
|
18
|
+
const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/484`
|
|
19
19
|
|
|
20
20
|
const e = new Error(message)
|
|
21
21
|
e.code = code
|
|
@@ -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
|
|
@@ -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
|
@@ -28,9 +28,15 @@ const config = function (options = {}) {
|
|
|
28
28
|
// overload
|
|
29
29
|
const overload = options.overload || options.override
|
|
30
30
|
|
|
31
|
+
// ignore
|
|
32
|
+
const ignore = options.ignore || []
|
|
33
|
+
|
|
31
34
|
// strict
|
|
32
35
|
const strict = options.strict
|
|
33
36
|
|
|
37
|
+
// envKeysFile
|
|
38
|
+
const envKeysFile = options.envKeysFile
|
|
39
|
+
|
|
34
40
|
// DOTENV_KEY (DEPRECATED)
|
|
35
41
|
let DOTENV_KEY = process.env.DOTENV_KEY
|
|
36
42
|
if (options && options.DOTENV_KEY) {
|
|
@@ -67,7 +73,7 @@ const config = function (options = {}) {
|
|
|
67
73
|
processedEnvs,
|
|
68
74
|
readableFilepaths,
|
|
69
75
|
uniqueInjectedKeys
|
|
70
|
-
} = new Run(envs, overload, DOTENV_KEY, processEnv).run()
|
|
76
|
+
} = new Run(envs, overload, DOTENV_KEY, processEnv, envKeysFile).run()
|
|
71
77
|
|
|
72
78
|
let lastError
|
|
73
79
|
/** @type {Record<string, string>} */
|
|
@@ -86,19 +92,23 @@ const config = function (options = {}) {
|
|
|
86
92
|
for (const error of processedEnv.errors || []) {
|
|
87
93
|
if (strict) throw error // throw immediately if strict
|
|
88
94
|
|
|
95
|
+
if (ignore.includes(error.code)) {
|
|
96
|
+
continue // ignore error
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
lastError = error // surface later in { error }
|
|
90
100
|
|
|
91
101
|
if (error.code === 'MISSING_ENV_FILE') {
|
|
92
102
|
if (!options.convention) { // do not output error for conventions (too noisy)
|
|
93
103
|
console.error(error.message)
|
|
94
104
|
if (error.help) {
|
|
95
|
-
|
|
105
|
+
console.error(error.help)
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
} else {
|
|
99
109
|
console.error(error.message)
|
|
100
110
|
if (error.help) {
|
|
101
|
-
|
|
111
|
+
console.error(error.help)
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
}
|
|
@@ -183,8 +193,13 @@ const genexample = function (directory, envFile) {
|
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
/** @type {import('./main').keypair} */
|
|
186
|
-
const keypair = function (envFile, key) {
|
|
187
|
-
|
|
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
|
+
}
|
|
188
203
|
}
|
|
189
204
|
|
|
190
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
|