@dotenvx/dotenvx 1.59.1 → 1.60.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +14 -4
  3. package/package.json +7 -6
  4. package/src/cli/actions/decrypt.js +15 -8
  5. package/src/cli/actions/encrypt.js +15 -8
  6. package/src/cli/actions/ext/genexample.js +2 -2
  7. package/src/cli/actions/ext/gitignore.js +3 -3
  8. package/src/cli/actions/get.js +12 -7
  9. package/src/cli/actions/keypair.js +13 -5
  10. package/src/cli/actions/rotate.js +16 -9
  11. package/src/cli/actions/run.js +13 -6
  12. package/src/cli/actions/set.js +19 -12
  13. package/src/cli/dotenvx.js +19 -21
  14. package/src/cli/examples.js +1 -1
  15. package/src/db/session.js +10 -6
  16. package/src/lib/extensions/ops.js +112 -63
  17. package/src/lib/helpers/catchAndLog.js +1 -1
  18. package/src/lib/helpers/createSpinner.js +24 -0
  19. package/src/lib/helpers/cryptography/index.js +4 -0
  20. package/src/lib/helpers/cryptography/mutateKeysSrc.js +4 -4
  21. package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +38 -0
  22. package/src/lib/helpers/cryptography/opsKeypair.js +2 -2
  23. package/src/lib/helpers/cryptography/opsKeypairSync.js +14 -0
  24. package/src/lib/helpers/cryptography/provision.js +7 -7
  25. package/src/lib/helpers/cryptography/provisionSync.js +47 -0
  26. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +1 -1
  27. package/src/lib/helpers/detectEncoding.js +2 -2
  28. package/src/lib/helpers/detectEncodingSync.js +22 -0
  29. package/src/lib/helpers/errors.js +15 -0
  30. package/src/lib/helpers/fsx.js +27 -3
  31. package/src/lib/helpers/installPrecommitHook.js +2 -2
  32. package/src/lib/helpers/isIgnoringDotenvKeys.js +1 -1
  33. package/src/lib/helpers/keyResolution/index.js +3 -1
  34. package/src/lib/helpers/keyResolution/keyValues.js +12 -12
  35. package/src/lib/helpers/keyResolution/keyValuesSync.js +85 -0
  36. package/src/lib/helpers/keyResolution/readFileKey.js +10 -8
  37. package/src/lib/helpers/keyResolution/readFileKeySync.js +15 -0
  38. package/src/lib/helpers/kits/sample.js +9 -21
  39. package/src/lib/main.d.ts +18 -3
  40. package/src/lib/main.js +18 -18
  41. package/src/lib/services/decrypt.js +8 -8
  42. package/src/lib/services/encrypt.js +17 -11
  43. package/src/lib/services/genexample.js +2 -2
  44. package/src/lib/services/get.js +30 -21
  45. package/src/lib/services/keypair.js +21 -5
  46. package/src/lib/services/prebuild.js +7 -12
  47. package/src/lib/services/precommit.js +7 -11
  48. package/src/lib/services/rotate.js +22 -18
  49. package/src/lib/services/run.js +82 -9
  50. package/src/lib/services/sets.js +139 -12
  51. package/src/shared/logger.js +3 -3
  52. package/src/lib/helpers/sleep.js +0 -5
package/CHANGELOG.md CHANGED
@@ -2,7 +2,18 @@
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.59.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.60.0...main)
6
+
7
+ ## [1.60.0](https://github.com/dotenvx/dotenvx/compare/v1.59.1...v1.60.0) (2026-04-04)
8
+
9
+ ### Added
10
+
11
+ * Add spinner with loading messages
12
+ * `injecting` (`run`)
13
+ * `encrypting` (`encrypt`, `set`)
14
+ * `decrypting` (`decrypt`, `get`)
15
+ * `rotating` (`rotate`)
16
+ * `retrieving` (`keypair`)
6
17
 
7
18
  ## [1.59.1](https://github.com/dotenvx/dotenvx/compare/v1.59.0...v1.59.1) (2026-03-28)
8
19
 
package/README.md CHANGED
@@ -1258,12 +1258,12 @@ $ dotenvx run -fk .env.keys -f apps/app1/.env -- yourcommand
1258
1258
  ```
1259
1259
 
1260
1260
  </details>
1261
- <details><summary>`run --ops-off`</summary><br>
1261
+ <details><summary>`run --no-ops`</summary><br>
1262
1262
 
1263
1263
  Turn off [Dotenvx Ops](https://dotenvx.com/ops) features.
1264
1264
 
1265
1265
  ```sh
1266
- $ dotenvx run --ops-off -- yourcommand
1266
+ $ dotenvx run --no-ops -- yourcommand
1267
1267
  ```
1268
1268
 
1269
1269
  </details>
@@ -1592,6 +1592,16 @@ $ dotenvx encrypt -f .env.production
1592
1592
  ⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
1593
1593
  ```
1594
1594
 
1595
+ </details>
1596
+ <details><summary>`encrypt --no-ops`</summary><br>
1597
+
1598
+ Turn off [Dotenvx Ops](https://dotenvx.com/ops) features for encrypt.
1599
+
1600
+ ```sh
1601
+ $ dotenvx encrypt --no-ops
1602
+ ◈ encrypted (.env)
1603
+ ```
1604
+
1595
1605
  </details>
1596
1606
  <details><summary>`encrypt -fk`</summary><br>
1597
1607
 
@@ -2503,13 +2513,13 @@ $ dotenvx run --convention=nextjs -- node index.js
2503
2513
  ```
2504
2514
 
2505
2515
  </details>
2506
- <details><summary>`config(opsOff:)` - opsOff</summary><br>
2516
+ <details><summary>`config(noOps:)` - noOps</summary><br>
2507
2517
 
2508
2518
  Turn off [Dotenvx Ops](https://dotenvx.com/ops) features.
2509
2519
 
2510
2520
  ```js
2511
2521
  // index.js
2512
- require('@dotenvx/dotenvx').config({opsOff: true})
2522
+ require('@dotenvx/dotenvx').config({noOps: true})
2513
2523
  ```
2514
2524
 
2515
2525
  </details>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "1.59.1",
2
+ "version": "1.60.0",
3
3
  "name": "@dotenvx/dotenvx",
4
- "description": "a secure dotenv–from the creator of `dotenv`",
4
+ "description": "secrets for agents–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
6
6
  "keywords": [
7
7
  "dotenv",
@@ -35,8 +35,8 @@
35
35
  "scripts": {
36
36
  "standard": "standard",
37
37
  "standard:fix": "standard --fix",
38
- "test": "tap run --test-env=DOTENVX_OPS_OFF=true --allow-empty-coverage --disable-coverage --timeout=60000",
39
- "test-coverage": "tap run --test-env=DOTENVX_OPS_OFF=true --show-full-coverage --timeout=60000",
38
+ "test": "tap run --test-env=DOTENVX_NO_OPS=true --allow-empty-coverage --disable-coverage --timeout=60000",
39
+ "test-coverage": "tap run --test-env=DOTENVX_NO_OPS=true --show-full-coverage --timeout=60000",
40
40
  "testshell": "bash shellspec",
41
41
  "prerelease": "npm test && npm run testshell",
42
42
  "release": "standard-version"
@@ -51,10 +51,11 @@
51
51
  "ignore": "^5.3.0",
52
52
  "object-treeify": "1.1.33",
53
53
  "picomatch": "^4.0.2",
54
- "which": "^4.0.0"
54
+ "which": "^4.0.0",
55
+ "yocto-spinner": "^1.1.0"
55
56
  },
56
57
  "devDependencies": {
57
- "@yao-pkg/pkg": "^5.14.2",
58
+ "@yao-pkg/pkg": "^6.14.2",
58
59
  "capture-console": "^1.0.2",
59
60
  "esbuild": "^0.25.8",
60
61
  "proxyquire": "^2.1.3",
@@ -3,13 +3,18 @@ const { logger } = require('./../../shared/logger')
3
3
 
4
4
  const Decrypt = require('./../../lib/services/decrypt')
5
5
  const catchAndLog = require('../../lib/helpers/catchAndLog')
6
+ const createSpinner = require('../../lib/helpers/createSpinner')
7
+ const Session = require('../../db/session')
6
8
 
7
- function decrypt () {
9
+ async function decrypt () {
8
10
  const options = this.opts()
11
+ const spinner = await createSpinner({ ...options, text: 'decrypting' })
12
+
9
13
  logger.debug(`options: ${JSON.stringify(options)}`)
10
14
 
15
+ const sesh = new Session()
11
16
  const envs = this.envs
12
- const opsOn = options.opsOff !== true
17
+ const noOps = options.ops === false || (await sesh.noOps())
13
18
 
14
19
  let errorCount = 0
15
20
 
@@ -17,8 +22,8 @@ function decrypt () {
17
22
  if (options.stdout) {
18
23
  const {
19
24
  processedEnvs
20
- } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
21
-
25
+ } = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noOps).run()
26
+ if (spinner) spinner.stop()
22
27
  for (const processedEnv of processedEnvs) {
23
28
  if (processedEnv.error) {
24
29
  errorCount += 1
@@ -39,7 +44,7 @@ function decrypt () {
39
44
  processedEnvs,
40
45
  changedFilepaths,
41
46
  unchangedFilepaths
42
- } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
47
+ } = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noOps).run()
43
48
 
44
49
  for (const processedEnv of processedEnvs) {
45
50
  logger.verbose(`decrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
@@ -48,18 +53,19 @@ function decrypt () {
48
53
  errorCount += 1
49
54
  logger.error(processedEnv.error.messageWithHelp)
50
55
  } else if (processedEnv.changed) {
51
- fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
56
+ await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
52
57
 
53
58
  logger.verbose(`decrypted ${processedEnv.envFilepath} (${processedEnv.filepath})`)
54
59
  } else {
55
- logger.verbose(`no changes ${processedEnv.envFilepath} (${processedEnv.filepath})`)
60
+ logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
56
61
  }
57
62
  }
58
63
 
64
+ if (spinner) spinner.stop()
59
65
  if (changedFilepaths.length > 0) {
60
66
  logger.success(`◇ decrypted (${changedFilepaths.join(',')})`)
61
67
  } else if (unchangedFilepaths.length > 0) {
62
- logger.info(`○ no changes (${unchangedFilepaths})`)
68
+ logger.info(`○ no change (${unchangedFilepaths})`)
63
69
  } else {
64
70
  // do nothing - scenario when no .env files found
65
71
  }
@@ -68,6 +74,7 @@ function decrypt () {
68
74
  process.exit(1)
69
75
  }
70
76
  } catch (error) {
77
+ if (spinner) spinner.stop()
71
78
  catchAndLog(error)
72
79
  process.exit(1)
73
80
  }
@@ -5,21 +5,26 @@ const Encrypt = require('./../../lib/services/encrypt')
5
5
 
6
6
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
7
  const localDisplayPath = require('../../lib/helpers/localDisplayPath')
8
+ const createSpinner = require('../../lib/helpers/createSpinner')
9
+ const Session = require('../../db/session')
8
10
 
9
- function encrypt () {
11
+ async function encrypt () {
10
12
  const options = this.opts()
13
+ const spinner = await createSpinner({ ...options, text: 'encrypting' })
14
+
11
15
  logger.debug(`options: ${JSON.stringify(options)}`)
12
16
 
17
+ const sesh = new Session()
13
18
  const envs = this.envs
14
- const opsOn = options.opsOff !== true
19
+ const noOps = options.ops === false || (await sesh.noOps())
15
20
  const noCreate = options.create === false
16
21
 
17
22
  // stdout - should not have a try so that exit codes can surface to stdout
18
23
  if (options.stdout) {
19
24
  const {
20
25
  processedEnvs
21
- } = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn, noCreate).run()
22
-
26
+ } = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noOps, noCreate).run()
27
+ if (spinner) spinner.stop()
23
28
  for (const processedEnv of processedEnvs) {
24
29
  console.log(processedEnv.envSrc)
25
30
  }
@@ -30,21 +35,22 @@ function encrypt () {
30
35
  processedEnvs,
31
36
  changedFilepaths,
32
37
  unchangedFilepaths
33
- } = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn, noCreate).run()
38
+ } = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noOps, noCreate).run()
34
39
 
35
40
  for (const processedEnv of processedEnvs) {
36
41
  logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
37
42
  if (processedEnv.error) {
38
43
  logger.warn(processedEnv.error.messageWithHelp)
39
44
  } else if (processedEnv.changed) {
40
- fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
45
+ await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
41
46
 
42
47
  logger.verbose(`encrypted ${processedEnv.envFilepath} (${processedEnv.filepath})`)
43
48
  } else {
44
- logger.verbose(`no changes ${processedEnv.envFilepath} (${processedEnv.filepath})`)
49
+ logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
45
50
  }
46
51
  }
47
52
 
53
+ if (spinner) spinner.stop()
48
54
  if (changedFilepaths.length > 0) {
49
55
  const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
50
56
  let msg = `◈ encrypted (${changedFilepaths.join(',')})`
@@ -54,7 +60,7 @@ function encrypt () {
54
60
  }
55
61
  logger.success(msg)
56
62
  } else if (unchangedFilepaths.length > 0) {
57
- logger.info(`○ no changes (${unchangedFilepaths})`)
63
+ logger.info(`○ no change (${unchangedFilepaths})`)
58
64
  } else {
59
65
  // do nothing - scenario when no .env files found
60
66
  }
@@ -65,6 +71,7 @@ function encrypt () {
65
71
  }
66
72
  }
67
73
  } catch (error) {
74
+ if (spinner) spinner.stop()
68
75
  catchAndLog(error)
69
76
  process.exit(1)
70
77
  }
@@ -20,12 +20,12 @@ function genexample (directory) {
20
20
 
21
21
  logger.verbose(`loading env from ${envFile}`)
22
22
 
23
- fsx.writeFileX(exampleFilepath, envExampleFile)
23
+ fsx.writeFileXSync(exampleFilepath, envExampleFile)
24
24
 
25
25
  if (addedKeys.length > 0) {
26
26
  logger.success(`▣ generated (${path.basename(exampleFilepath)})`)
27
27
  } else {
28
- logger.info('○ no changes (.env.example)')
28
+ logger.info('○ no change (.env.example)')
29
29
  }
30
30
  } catch (error) {
31
31
  catchAndLog(error)
@@ -18,13 +18,13 @@ class Generic {
18
18
  const changedPatterns = []
19
19
  if (!fsx.existsSync(this.filename)) {
20
20
  if (this.touchFile === true && this.patterns.length > 0) {
21
- fsx.writeFileX(this.filename, '')
21
+ fsx.writeFileXSync(this.filename, '')
22
22
  } else {
23
23
  return
24
24
  }
25
25
  }
26
26
 
27
- const lines = fsx.readFileX(this.filename).split(/\r?\n/)
27
+ const lines = fsx.readFileXSync(this.filename).split(/\r?\n/)
28
28
  this.patterns.forEach(pattern => {
29
29
  if (!lines.includes(pattern.trim())) {
30
30
  this.append(pattern)
@@ -36,7 +36,7 @@ class Generic {
36
36
  if (changedPatterns.length > 0) {
37
37
  logger.success(`▣ ignored ${this.patterns} (${this.filename})`)
38
38
  } else {
39
- logger.info(`○ no changes (${this.filename})`)
39
+ logger.info(`○ no change (${this.filename})`)
40
40
  }
41
41
  }
42
42
  }
@@ -3,18 +3,20 @@ const { logger } = require('./../../shared/logger')
3
3
  const conventions = require('./../../lib/helpers/conventions')
4
4
  const escape = require('./../../lib/helpers/escape')
5
5
  const catchAndLog = require('./../../lib/helpers/catchAndLog')
6
-
6
+ const createSpinner = require('../../lib/helpers/createSpinner')
7
+ const Session = require('../../db/session')
7
8
  const Get = require('./../../lib/services/get')
8
9
 
9
- function get (key) {
10
+ async function get (key) {
11
+ const options = this.opts()
12
+ const spinner = await createSpinner({ ...options, text: 'decrypting' })
13
+
14
+ logger.debug(`options: ${JSON.stringify(options)}`)
10
15
  if (key) {
11
16
  logger.debug(`key: ${key}`)
12
17
  }
13
18
 
14
- const options = this.opts()
15
- logger.debug(`options: ${JSON.stringify(options)}`)
16
19
  const prettyPrint = options.prettyPrint || options.pp
17
-
18
20
  const ignore = options.ignore || []
19
21
 
20
22
  let envs = []
@@ -26,8 +28,9 @@ function get (key) {
26
28
  }
27
29
 
28
30
  try {
29
- const opsOn = options.opsOff !== true
30
- const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, opsOn).run()
31
+ const sesh = new Session()
32
+ const noOps = options.ops === false || (await sesh.noOps())
33
+ const { parsed, errors } = await new Get(key, envs, options.overload, options.all, options.envKeysFile, noOps).run()
31
34
 
32
35
  for (const error of errors || []) {
33
36
  if (options.strict) throw error // throw immediately if strict
@@ -39,6 +42,7 @@ function get (key) {
39
42
  logger.error(error.messageWithHelp)
40
43
  }
41
44
 
45
+ if (spinner) spinner.stop()
42
46
  if (key) {
43
47
  const single = parsed[key]
44
48
  if (single === undefined) {
@@ -73,6 +77,7 @@ function get (key) {
73
77
  }
74
78
  }
75
79
  } catch (error) {
80
+ if (spinner) spinner.stop()
76
81
  catchAndLog(error)
77
82
  process.exit(1)
78
83
  }
@@ -1,18 +1,26 @@
1
1
  const { logger } = require('./../../shared/logger')
2
2
 
3
- const main = require('./../../lib/main')
3
+ const Keypair = require('./../../lib/services/keypair')
4
+ const createSpinner = require('../../lib/helpers/createSpinner')
5
+ const Session = require('../../db/session')
4
6
 
5
- function keypair (key) {
7
+ async function keypair (key) {
8
+ const options = this.opts()
9
+ const spinner = await createSpinner({ ...options, text: 'retrieving' })
10
+
11
+ logger.debug(`options: ${JSON.stringify(options)}`)
6
12
  if (key) {
7
13
  logger.debug(`key: ${key}`)
8
14
  }
9
15
 
10
- const options = this.opts()
11
- logger.debug(`options: ${JSON.stringify(options)}`)
12
16
  const prettyPrint = options.prettyPrint || options.pp
13
17
 
14
- const results = main.keypair(options.envFile, key, options.envKeysFile, options.opsOff)
18
+ const sesh = new Session()
19
+ const noOps = options.ops === false || await sesh.noOps()
20
+ const keypairs = await new Keypair(options.envFile, options.envKeysFile, noOps).run()
21
+ const results = key ? keypairs[key] : keypairs
15
22
 
23
+ if (spinner) spinner.stop()
16
24
  if (typeof results === 'object' && results !== null) {
17
25
  // inline shell format - env $(dotenvx keypair --format=shell) your-command
18
26
  if (options.format === 'shell') {
@@ -4,21 +4,26 @@ const { logger } = require('./../../shared/logger')
4
4
  const Rotate = require('./../../lib/services/rotate')
5
5
 
6
6
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
+ const createSpinner = require('../../lib/helpers/createSpinner')
7
8
  const localDisplayPath = require('../../lib/helpers/localDisplayPath')
9
+ const Session = require('../../db/session')
8
10
 
9
- function rotate () {
11
+ async function rotate () {
10
12
  const options = this.opts()
13
+ const spinner = await createSpinner({ ...options, text: 'rotating', frames: ['⟳', '⤾', '⥁'] })
14
+
11
15
  logger.debug(`options: ${JSON.stringify(options)}`)
12
16
 
13
17
  const envs = this.envs
14
- const opsOn = options.opsOff !== true
18
+ const sesh = new Session()
19
+ const noOps = options.ops === false || (await sesh.noOps())
15
20
 
16
21
  // stdout - should not have a try so that exit codes can surface to stdout
17
22
  if (options.stdout) {
18
23
  const {
19
24
  processedEnvs
20
- } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
21
-
25
+ } = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noOps).run()
26
+ if (spinner) spinner.stop()
22
27
  for (const processedEnv of processedEnvs) {
23
28
  console.log(processedEnv.envSrc)
24
29
  if (processedEnv.privateKeyAdded) {
@@ -33,24 +38,25 @@ function rotate () {
33
38
  processedEnvs,
34
39
  changedFilepaths,
35
40
  unchangedFilepaths
36
- } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
41
+ } = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noOps).run()
37
42
 
38
43
  for (const processedEnv of processedEnvs) {
39
44
  logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
40
45
  if (processedEnv.error) {
41
46
  logger.warn(processedEnv.error.messageWithHelp)
42
47
  } else if (processedEnv.changed) {
43
- fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
48
+ await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
44
49
  if (processedEnv.privateKeyAdded) {
45
- fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
50
+ await fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
46
51
  }
47
52
 
48
53
  logger.verbose(`rotated ${processedEnv.envFilepath} (${processedEnv.filepath})`)
49
54
  } else {
50
- logger.verbose(`no changes ${processedEnv.envFilepath} (${processedEnv.filepath})`)
55
+ logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
51
56
  }
52
57
  }
53
58
 
59
+ if (spinner) spinner.stop()
54
60
  if (changedFilepaths.length > 0) {
55
61
  const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
56
62
  let msg = `⟳ rotated (${changedFilepaths.join(',')})`
@@ -60,11 +66,12 @@ function rotate () {
60
66
  }
61
67
  logger.success(msg)
62
68
  } else if (unchangedFilepaths.length > 0) {
63
- logger.info(`○ no changes (${unchangedFilepaths})`)
69
+ logger.info(`○ no change (${unchangedFilepaths})`)
64
70
  } else {
65
71
  // do nothing - scenario when no .env files found
66
72
  }
67
73
  } catch (error) {
74
+ if (spinner) spinner.stop()
68
75
  catchAndLog(error)
69
76
  process.exit(1)
70
77
  }
@@ -4,22 +4,27 @@ const { logger } = require('./../../shared/logger')
4
4
  const executeCommand = require('./../../lib/helpers/executeCommand')
5
5
  const Run = require('./../../lib/services/run')
6
6
  const catchAndLog = require('./../../lib/helpers/catchAndLog')
7
+ const createSpinner = require('../../lib/helpers/createSpinner')
8
+ const Session = require('../../db/session')
7
9
 
8
10
  const conventions = require('./../../lib/helpers/conventions')
9
11
 
10
12
  async function run () {
13
+ const options = this.opts()
11
14
  const commandArgs = this.args
12
- logger.debug(`process command [${commandArgs.join(' ')}]`)
15
+ const spinner = await createSpinner({ ...options, text: 'injecting' })
13
16
 
14
- const options = this.opts()
15
17
  logger.debug(`options: ${JSON.stringify(options)}`)
18
+ logger.debug(`process command [${commandArgs.join(' ')}]`)
16
19
 
17
20
  const ignore = options.ignore || []
18
21
 
19
- // dotenvx-ops related
20
- const opsOn = options.opsOff !== true
22
+ const sesh = new Session()
23
+ const noOps = options.ops === false || options.opsOff === true || (await sesh.noOps())
21
24
 
22
25
  if (commandArgs.length < 1) {
26
+ if (spinner) spinner.stop()
27
+
23
28
  const hasSeparator = process.argv.indexOf('--') !== -1
24
29
 
25
30
  if (hasSeparator) {
@@ -46,7 +51,7 @@ async function run () {
46
51
  readableStrings,
47
52
  readableFilepaths,
48
53
  uniqueInjectedKeys
49
- } = new Run(envs, options.overload, process.env, options.envKeysFile, opsOn).run()
54
+ } = await new Run(envs, options.overload, process.env, options.envKeysFile, noOps).run()
50
55
 
51
56
  for (const processedEnv of processedEnvs) {
52
57
  if (processedEnv.type === 'envFile') {
@@ -88,7 +93,7 @@ async function run () {
88
93
  }
89
94
  }
90
95
 
91
- let msg = `injecting env (${uniqueInjectedKeys.length})`
96
+ let msg = `injected env (${uniqueInjectedKeys.length})`
92
97
  if (readableFilepaths.length > 0 && readableStrings.length > 0) {
93
98
  msg += ` from ${readableFilepaths.join(', ')}, and --env flag${readableStrings.length > 1 ? 's' : ''}`
94
99
  } else if (readableFilepaths.length > 0) {
@@ -97,8 +102,10 @@ async function run () {
97
102
  msg += ` from --env flag${readableStrings.length > 1 ? 's' : ''}`
98
103
  }
99
104
 
105
+ if (spinner) spinner.stop()
100
106
  logger.successv(msg)
101
107
  } catch (error) {
108
+ if (spinner) spinner.stop()
102
109
  catchAndLog(error)
103
110
  process.exit(1)
104
111
  }
@@ -1,35 +1,40 @@
1
1
  const fsx = require('./../../lib/helpers/fsx')
2
2
  const { logger } = require('./../../shared/logger')
3
3
 
4
- const Sets = require('./../../lib/services/sets')
5
-
6
4
  const catchAndLog = require('../../lib/helpers/catchAndLog')
5
+ const createSpinner = require('../../lib/helpers/createSpinner')
7
6
  const localDisplayPath = require('../../lib/helpers/localDisplayPath')
7
+ const Session = require('../../db/session')
8
+ const Sets = require('./../../lib/services/sets')
8
9
 
9
- function set (key, value) {
10
- logger.debug(`key: ${key}`)
11
- logger.debug(`value: ${value}`)
12
-
10
+ async function set (key, value) {
13
11
  const options = this.opts()
14
- logger.debug(`options: ${JSON.stringify(options)}`)
15
12
 
16
- // encrypt
17
13
  let encrypt = true
14
+ let settingMessage = 'encrypting'
18
15
  if (options.plain) {
19
16
  encrypt = false
17
+ settingMessage = 'setting'
20
18
  }
21
19
 
20
+ const spinner = await createSpinner({ ...options, text: settingMessage })
21
+
22
+ logger.debug(`key: ${key}`)
23
+ logger.debug(`value: ${value}`)
24
+ logger.debug(`options: ${JSON.stringify(options)}`)
25
+
22
26
  try {
27
+ const sesh = new Session()
23
28
  const envs = this.envs
24
29
  const envKeysFilepath = options.envKeysFile
25
- const opsOn = options.opsOff !== true
30
+ const noOps = options.ops === false || (await sesh.noOps())
26
31
  const noCreate = options.create === false
27
32
 
28
33
  const {
29
34
  processedEnvs,
30
35
  changedFilepaths,
31
36
  unchangedFilepaths
32
- } = new Sets(key, value, envs, encrypt, envKeysFilepath, opsOn, noCreate).run()
37
+ } = await new Sets(key, value, envs, encrypt, envKeysFilepath, noOps, noCreate).run()
33
38
 
34
39
  let withEncryption = ''
35
40
 
@@ -43,7 +48,7 @@ function set (key, value) {
43
48
  if (processedEnv.error) {
44
49
  logger.warn(processedEnv.error.messageWithHelp)
45
50
  } else {
46
- fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
51
+ await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
47
52
 
48
53
  logger.verbose(`${processedEnv.key} set${withEncryption} (${processedEnv.envFilepath})`)
49
54
  logger.debug(`${processedEnv.key} set${withEncryption} to ${processedEnv.value} (${processedEnv.envFilepath})`)
@@ -53,6 +58,7 @@ function set (key, value) {
53
58
  const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
54
59
  const keyAddedSuffix = keyAddedEnv ? ` + key (${localDisplayPath(keyAddedEnv.envKeysFilepath)})` : ''
55
60
 
61
+ if (spinner) spinner.stop()
56
62
  if (changedFilepaths.length > 0) {
57
63
  if (encrypt) {
58
64
  logger.success(`◈ encrypted ${key} (${changedFilepaths.join(',')})${keyAddedSuffix}`)
@@ -63,13 +69,14 @@ function set (key, value) {
63
69
  const keyAddedEnvFilepath = keyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
64
70
  logger.success(`◈ encrypted ${key} (${keyAddedEnvFilepath})${keyAddedSuffix}`)
65
71
  } else if (unchangedFilepaths.length > 0) {
66
- logger.info(`○ no changes (${unchangedFilepaths})`)
72
+ logger.info(`○ no change (${unchangedFilepaths})`)
67
73
  } else {
68
74
  // do nothing
69
75
  }
70
76
 
71
77
  // intentionally quiet: success line communicates key creation
72
78
  } catch (error) {
79
+ if (spinner) spinner.stop()
73
80
  catchAndLog(error)
74
81
  process.exit(1)
75
82
  }