@dotenvx/dotenvx 1.13.2 โ†’ 1.14.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 CHANGED
@@ -2,7 +2,19 @@
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.13.2...main)
5
+ ## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.14.0...main)
6
+
7
+ ## 1.14.0
8
+
9
+ ### Added
10
+
11
+ * add `dotenvx keypair` command for printing your public/private keypairs ([#375](https://github.com/dotenvx/dotenvx/pull/375))
12
+
13
+ ## 1.13.3
14
+
15
+ ### Changed
16
+
17
+ * exit code 1 when `decrypt` fails in any way ([#374](https://github.com/dotenvx/dotenvx/pull/374))
6
18
 
7
19
  ## 1.13.2
8
20
 
package/README.md CHANGED
@@ -1274,84 +1274,46 @@ More examples
1274
1274
  ```
1275
1275
 
1276
1276
  </details>
1277
+ * <details><summary>`keypair`</summary><br>
1277
1278
 
1278
- * <details><summary>`help`</summary><br>
1279
-
1280
- Output help for `dotenvx`.
1279
+ Print public/private keys for `.env` file.
1281
1280
 
1282
1281
  ```sh
1283
- $ dotenvx help
1284
- Usage: @dotenvx/dotenvx [options] [command]
1285
-
1286
- a better dotenvโ€“from the creator of `dotenv`
1287
-
1288
- Options:
1289
- -l, --log-level <level> set log level (default: "info")
1290
- -q, --quiet sets log level to error
1291
- -v, --verbose sets log level to verbose
1292
- -d, --debug sets log level to debug
1293
- -V, --version output the version number
1294
- -h, --help display help for command
1282
+ $ echo "HELLO=World" > .env
1283
+ $ dotenvx encrypt
1295
1284
 
1296
- Commands:
1297
- run [options] inject env at runtime [dotenvx run -- yourcommand]
1298
- get [options] [key] return a single environment variable
1299
- set [options] <KEY> <value> set a single environment variable
1300
- encrypt [options] convert .env file(s) to encrypted .env file(s)
1301
- decrypt [options] convert encrypted .env file(s) to plain .env file(s)
1302
- pro ๐Ÿ† pro
1303
- ext [command] [args...] ๐Ÿ”Œ extensions
1304
- help [command] display help for command
1285
+ $ dotenvx keypair
1286
+ {"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}
1305
1287
  ```
1306
1288
 
1307
- You can get more detailed help per command with `dotenvx help COMMAND`.
1308
-
1309
- ```sh
1310
- $ dotenvx help run
1311
- Usage: @dotenvx/dotenvx run [options]
1312
-
1313
- inject env at runtime [dotenvx run -- yourcommand]
1314
-
1315
- Options:
1316
- -e, --env <strings...> environment variable(s) set as string (example: "HELLO=World") (default: [])
1317
- -f, --env-file <paths...> path(s) to your env file(s) (default: [])
1318
- -fv, --env-vault-file <paths...> path(s) to your .env.vault file(s) (default: [])
1319
- -o, --overload override existing env variables
1320
- --convention <name> load a .env convention (available conventions: ['nextjs'])
1321
- -h, --help display help for command
1322
-
1323
- Examples:
1324
-
1325
- $ dotenvx run -- npm run dev
1326
- $ dotenvx run -- flask --app index run
1327
- $ dotenvx run -- php artisan serve
1328
- $ dotenvx run -- bin/rails s
1289
+ </details>
1290
+ * <details><summary>`keypair -f .env.production`</summary><br>
1329
1291
 
1330
- Try it:
1292
+ Print public/private keys for `.env.production` file.
1331
1293
 
1332
- $ echo "HELLO=World" > .env
1333
- $ echo "console.log('Hello ' + process.env.HELLO)" > index.js
1294
+ ```sh
1295
+ $ echo "HELLO=Production" > .env.production
1296
+ $ dotenvx encrypt -f .env.production
1334
1297
 
1335
- $ dotenvx run -- node index.js
1336
- [dotenvx] injecting env (1) from .env
1337
- Hello World
1298
+ $ dotenvx keypair -f .env.production
1299
+ {"DOTENV_PUBLIC_KEY_PRODUCTION":"<publicKey>","DOTENV_PRIVATE_KEY_PRODUCTION":"<privateKey>"}
1338
1300
  ```
1339
1301
 
1340
1302
  </details>
1341
- * <details><summary>`--version`</summary><br>
1303
+ * <details><summary>`keypair DOTENV_PRIVATE_KEY`</summary><br>
1342
1304
 
1343
- Check current version of `dotenvx`.
1305
+ Print specific key for `.env` file.
1344
1306
 
1345
1307
  ```sh
1346
- $ dotenvx --version
1347
- X.X.X
1308
+ $ echo "HELLO=World" > .env
1309
+ $ dotenvx encrypt
1310
+
1311
+ $ dotenvx keypair DOTENV_PRIVATE_KEY
1312
+ <privateKey>
1348
1313
  ```
1349
1314
 
1350
1315
  </details>
1351
-
1352
- ### Extensions ๐Ÿ”Œ
1353
-
1354
- * <details><summary>`ext ls`</summary><br>
1316
+ * <details><summary>`ls`</summary><br>
1355
1317
 
1356
1318
  Print all `.env` files in a tree structure.
1357
1319
 
@@ -1361,7 +1323,7 @@ More examples
1361
1323
  $ mkdir -p apps/backend
1362
1324
  $ touch apps/backend/.env
1363
1325
 
1364
- $ dotenvx ext ls
1326
+ $ dotenvx ls
1365
1327
  โ”œโ”€ .env.production
1366
1328
  โ”œโ”€ .env
1367
1329
  โ””โ”€ apps
@@ -1370,7 +1332,7 @@ More examples
1370
1332
  ```
1371
1333
 
1372
1334
  </details>
1373
- * <details><summary>`ext ls directory`</summary><br>
1335
+ * <details><summary>`ls directory`</summary><br>
1374
1336
 
1375
1337
  Print all `.env` files inside a specified path to a directory.
1376
1338
 
@@ -1380,12 +1342,12 @@ More examples
1380
1342
  $ mkdir -p apps/backend
1381
1343
  $ touch apps/backend/.env
1382
1344
 
1383
- $ dotenvx ext ls apps/backend
1345
+ $ dotenvx ls apps/backend
1384
1346
  โ””โ”€ .env
1385
1347
  ```
1386
1348
 
1387
1349
  </details>
1388
- * <details><summary>`ext ls -f`</summary><br>
1350
+ * <details><summary>`ls -f`</summary><br>
1389
1351
 
1390
1352
  Glob `.env` filenames matching a wildcard.
1391
1353
 
@@ -1396,7 +1358,7 @@ More examples
1396
1358
  $ touch apps/backend/.env
1397
1359
  $ touch apps/backend/.env.prod
1398
1360
 
1399
- $ dotenvx ext ls -f **/.env.prod*
1361
+ $ dotenvx ls -f **/.env.prod*
1400
1362
  โ”œโ”€ .env.production
1401
1363
  โ””โ”€ apps
1402
1364
  โ””โ”€ backend
@@ -1404,6 +1366,104 @@ More examples
1404
1366
  ```
1405
1367
 
1406
1368
  </details>
1369
+ * <details><summary>`ls -ef`</summary><br>
1370
+
1371
+ Glob `.env` filenames excluding a wildcard.
1372
+
1373
+ ```sh
1374
+ $ touch .env
1375
+ $ touch .env.production
1376
+ $ mkdir -p apps/backend
1377
+ $ touch apps/backend/.env
1378
+ $ touch apps/backend/.env.prod
1379
+
1380
+ $ dotenvx ls -ef '**/.env.prod*'
1381
+ โ”œโ”€ .env
1382
+ โ””โ”€ apps
1383
+ โ””โ”€ backend
1384
+ โ””โ”€ .env
1385
+ ```
1386
+
1387
+ </details>
1388
+
1389
+ * <details><summary>`help`</summary><br>
1390
+
1391
+ Output help for `dotenvx`.
1392
+
1393
+ ```sh
1394
+ $ dotenvx help
1395
+ Usage: dotenvx [options] [command] [command] [args...]
1396
+
1397
+ a better dotenvโ€“from the creator of `dotenv`
1398
+
1399
+ Options:
1400
+ -l, --log-level <level> set log level (default: "info")
1401
+ -q, --quiet sets log level to error
1402
+ -v, --verbose sets log level to verbose
1403
+ -d, --debug sets log level to debug
1404
+ -V, --version output the version number
1405
+ -h, --help display help for command
1406
+
1407
+ Commands:
1408
+ run [options] inject env at runtime [dotenvx run -- yourcommand]
1409
+ get [options] [key] return a single environment variable
1410
+ set [options] <KEY> <value> set a single environment variable
1411
+ encrypt [options] convert .env file(s) to encrypted .env file(s)
1412
+ decrypt [options] convert encrypted .env file(s) to plain .env file(s)
1413
+ ls [options] [directory] print all .env files in a tree structure
1414
+
1415
+ Advanced:
1416
+ pro ๐Ÿ† pro
1417
+ ext ๐Ÿ”Œ extensions
1418
+ ```
1419
+
1420
+ You can get more detailed help per command with `dotenvx help COMMAND`.
1421
+
1422
+ ```sh
1423
+ $ dotenvx help run
1424
+ Usage: @dotenvx/dotenvx run [options]
1425
+
1426
+ inject env at runtime [dotenvx run -- yourcommand]
1427
+
1428
+ Options:
1429
+ -e, --env <strings...> environment variable(s) set as string (example: "HELLO=World") (default: [])
1430
+ -f, --env-file <paths...> path(s) to your env file(s) (default: [])
1431
+ -fv, --env-vault-file <paths...> path(s) to your .env.vault file(s) (default: [])
1432
+ -o, --overload override existing env variables
1433
+ --convention <name> load a .env convention (available conventions: ['nextjs'])
1434
+ -h, --help display help for command
1435
+
1436
+ Examples:
1437
+
1438
+ $ dotenvx run -- npm run dev
1439
+ $ dotenvx run -- flask --app index run
1440
+ $ dotenvx run -- php artisan serve
1441
+ $ dotenvx run -- bin/rails s
1442
+
1443
+ Try it:
1444
+
1445
+ $ echo "HELLO=World" > .env
1446
+ $ echo "console.log('Hello ' + process.env.HELLO)" > index.js
1447
+
1448
+ $ dotenvx run -- node index.js
1449
+ [dotenvx] injecting env (1) from .env
1450
+ Hello World
1451
+ ```
1452
+
1453
+ </details>
1454
+ * <details><summary>`--version`</summary><br>
1455
+
1456
+ Check current version of `dotenvx`.
1457
+
1458
+ ```sh
1459
+ $ dotenvx --version
1460
+ X.X.X
1461
+ ```
1462
+
1463
+ </details>
1464
+
1465
+ ### Extensions ๐Ÿ”Œ
1466
+
1407
1467
  * <details><summary>`ext genexample`</summary><br>
1408
1468
 
1409
1469
  In one command, generate a `.env.example` file from your current `.env` file contents.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.13.2",
2
+ "version": "1.14.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenvโ€“from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -9,6 +9,8 @@ function decrypt () {
9
9
  const options = this.opts()
10
10
  logger.debug(`options: ${JSON.stringify(options)}`)
11
11
 
12
+ let errorCount = 0
13
+
12
14
  // stdout - should not have a try so that exit codes can surface to stdout
13
15
  if (options.stdout) {
14
16
  const {
@@ -16,9 +18,19 @@ function decrypt () {
16
18
  } = main.decrypt(options.envFile, options.key, options.excludeKey)
17
19
 
18
20
  for (const processedEnvFile of processedEnvFiles) {
19
- process.stdout.write(processedEnvFile.envSrc)
21
+ if (processedEnvFile.error) {
22
+ errorCount += 1
23
+ console.error(processedEnvFile.error.message)
24
+ } else {
25
+ process.stdout.write(processedEnvFile.envSrc)
26
+ }
27
+ }
28
+
29
+ if (errorCount > 0) {
30
+ process.exit(1)
31
+ } else {
32
+ process.exit(0) // exit early
20
33
  }
21
- process.exit(0) // exit early
22
34
  } else {
23
35
  try {
24
36
  const {
@@ -29,12 +41,15 @@ function decrypt () {
29
41
 
30
42
  for (const processedEnvFile of processedEnvFiles) {
31
43
  logger.verbose(`decrypting ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
44
+
32
45
  if (processedEnvFile.error) {
46
+ errorCount += 1
47
+
33
48
  if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
34
- logger.warn(processedEnvFile.error.message)
49
+ logger.error(processedEnvFile.error.message)
35
50
  logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx decrypt]`)
36
51
  } else {
37
- logger.warn(processedEnvFile.error.message)
52
+ logger.error(processedEnvFile.error.message)
38
53
  }
39
54
  } else if (processedEnvFile.changed) {
40
55
  fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
@@ -52,6 +67,10 @@ function decrypt () {
52
67
  } else {
53
68
  // do nothing - scenario when no .env files found
54
69
  }
70
+
71
+ if (errorCount > 0) {
72
+ process.exit(1)
73
+ }
55
74
  } catch (error) {
56
75
  logger.error(error.message)
57
76
  if (error.help) {
@@ -0,0 +1,32 @@
1
+ const { logger } = require('./../../shared/logger')
2
+
3
+ const main = require('./../../lib/main')
4
+
5
+ function keypair (key) {
6
+ if (key) {
7
+ logger.debug(`key: ${key}`)
8
+ }
9
+
10
+ const options = this.opts()
11
+ logger.debug(`options: ${JSON.stringify(options)}`)
12
+
13
+ const results = main.keypair(options.envFile, key)
14
+
15
+ if (typeof results === 'object' && results !== null) {
16
+ let space = 0
17
+ if (options.prettyPrint) {
18
+ space = 2
19
+ }
20
+
21
+ process.stdout.write(JSON.stringify(results, null, space))
22
+ } else {
23
+ if (results === undefined) {
24
+ process.stdout.write('')
25
+ process.exit(1)
26
+ } else {
27
+ process.stdout.write(results)
28
+ }
29
+ }
30
+ }
31
+
32
+ module.exports = keypair
@@ -115,6 +115,15 @@ program.command('decrypt')
115
115
  .option('--stdout', 'send to stdout')
116
116
  .action(decryptAction)
117
117
 
118
+ // dotenvx keypair
119
+ const keypairAction = require('./actions/keypair')
120
+ program.command('keypair')
121
+ .description('print public/private keys for .env file(s)')
122
+ .argument('[key]', 'environment variable key name')
123
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
124
+ .option('-pp, --pretty-print', 'pretty print output')
125
+ .action(keypairAction)
126
+
118
127
  // dotenvx ls
119
128
  const lsAction = require('./actions/ls')
120
129
  program.command('ls')
@@ -0,0 +1,44 @@
1
+ const fs = require('fs')
2
+ const dotenv = require('dotenv')
3
+
4
+ const ENCODING = 'utf8'
5
+
6
+ const guessPublicKeyName = require('./guessPublicKeyName')
7
+
8
+ function searchProcessEnv (publicKeyName) {
9
+ if (process.env[publicKeyName] && process.env[publicKeyName].length > 0) {
10
+ return process.env[publicKeyName]
11
+ }
12
+ }
13
+
14
+ function searchEnvFile (publicKeyName, envFilepath) {
15
+ if (fs.existsSync(envFilepath)) {
16
+ const keysSrc = fs.readFileSync(envFilepath, { encoding: ENCODING })
17
+ const keysParsed = dotenv.parse(keysSrc)
18
+
19
+ if (keysParsed[publicKeyName] && keysParsed[publicKeyName].length > 0) {
20
+ return keysParsed[publicKeyName]
21
+ }
22
+ }
23
+ }
24
+
25
+ function smartDotenvPublicKey (envFilepath) {
26
+ let publicKey = null
27
+ const publicKeyName = guessPublicKeyName(envFilepath) // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
28
+
29
+ // 1. attempt process.env first
30
+ publicKey = searchProcessEnv(publicKeyName)
31
+ if (publicKey) {
32
+ return publicKey
33
+ }
34
+
35
+ // 2. attempt .env.keys second (path/to/.env.keys)
36
+ publicKey = searchEnvFile(publicKeyName, envFilepath)
37
+ if (publicKey) {
38
+ return publicKey
39
+ }
40
+
41
+ return null
42
+ }
43
+
44
+ module.exports = smartDotenvPublicKey
package/src/lib/main.js CHANGED
@@ -11,6 +11,7 @@ const Ls = require('./services/ls')
11
11
  const Get = require('./services/get')
12
12
  const Run = require('./services/run')
13
13
  const Sets = require('./services/sets')
14
+ const Keypair = require('./services/keypair')
14
15
  const Encrypt = require('./services/encrypt')
15
16
  const Decrypt = require('./services/decrypt')
16
17
  const Genexample = require('./services/genexample')
@@ -210,6 +211,11 @@ const decrypt = function (envFile, key, excludeKey) {
210
211
  return new Decrypt(envFile, key, excludeKey).run()
211
212
  }
212
213
 
214
+ /** @type {import('./main').keypair} */
215
+ const keypair = function (envFile, key) {
216
+ return new Keypair(envFile, key).run()
217
+ }
218
+
213
219
  module.exports = {
214
220
  // dotenv proxies
215
221
  config,
@@ -221,6 +227,7 @@ module.exports = {
221
227
  ls,
222
228
  get,
223
229
  set,
230
+ keypair,
224
231
  genexample,
225
232
  // expose for libs depending on @dotenvx/dotenvx - like dotenvx-pro
226
233
  setLogLevel,
@@ -0,0 +1,45 @@
1
+ const guessPublicKeyName = require('./../helpers/guessPublicKeyName')
2
+ const smartDotenvPublicKey = require('./../helpers/smartDotenvPublicKey')
3
+ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
4
+ const smartDotenvPrivateKey = require('./../helpers/smartDotenvPrivateKey')
5
+
6
+ class Keypair {
7
+ constructor (envFile = '.env', key = undefined) {
8
+ this.envFile = envFile
9
+ this.key = key
10
+ }
11
+
12
+ run () {
13
+ const out = {}
14
+
15
+ const envFilepaths = this._envFilepaths()
16
+ for (const envFilepath of envFilepaths) {
17
+ // public key
18
+ const publicKeyName = guessPublicKeyName(envFilepath)
19
+ const publicKeyValue = smartDotenvPublicKey(envFilepath)
20
+ out[publicKeyName] = publicKeyValue
21
+
22
+ // private key
23
+ const privateKeyName = guessPrivateKeyName(envFilepath)
24
+ const privateKeyValue = smartDotenvPrivateKey(envFilepath)
25
+
26
+ out[privateKeyName] = privateKeyValue
27
+ }
28
+
29
+ if (this.key) {
30
+ return out[this.key]
31
+ } else {
32
+ return out
33
+ }
34
+ }
35
+
36
+ _envFilepaths () {
37
+ if (!Array.isArray(this.envFile)) {
38
+ return [this.envFile]
39
+ }
40
+
41
+ return this.envFile
42
+ }
43
+ }
44
+
45
+ module.exports = Keypair