@dotenvx/dotenvx 1.4.0 → 1.6.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 (61) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +100 -2
  3. package/package.json +9 -10
  4. package/src/cli/actions/decrypt.js +71 -0
  5. package/src/cli/actions/encrypt.js +54 -42
  6. package/src/cli/actions/ext/genexample.js +4 -12
  7. package/src/cli/actions/ext/gitignore.js +5 -0
  8. package/src/cli/actions/ext/prebuild.js +1 -0
  9. package/src/cli/actions/ext/scan.js +9 -6
  10. package/src/cli/actions/ext/settings.js +5 -4
  11. package/src/cli/actions/ext/vault/decrypt.js +6 -13
  12. package/src/cli/actions/ext/vault/encrypt.js +5 -12
  13. package/src/cli/actions/ext/vault/migrate.js +22 -39
  14. package/src/cli/actions/get.js +6 -5
  15. package/src/cli/actions/run.js +3 -110
  16. package/src/cli/actions/set.js +2 -2
  17. package/src/cli/commands/ext.js +12 -1
  18. package/src/cli/dotenvx.js +16 -15
  19. package/src/cli/examples.js +17 -1
  20. package/src/lib/helpers/execute.js +9 -0
  21. package/src/lib/helpers/executeCommand.js +117 -0
  22. package/src/lib/helpers/executeExtension.js +38 -0
  23. package/src/lib/helpers/findOrCreatePublicKey.js +14 -8
  24. package/src/lib/helpers/isEncrypted.js +1 -2
  25. package/src/lib/helpers/isFullyEncrypted.js +2 -1
  26. package/src/lib/helpers/isIgnoringDotenvKeys.js +1 -1
  27. package/src/lib/helpers/isPublicKey.js +7 -0
  28. package/src/lib/helpers/keyPair.js +8 -2
  29. package/src/lib/helpers/smartDotenvPrivateKey.js +64 -6
  30. package/src/lib/main.js +16 -9
  31. package/src/lib/services/decrypt.js +79 -98
  32. package/src/lib/services/encrypt.js +12 -7
  33. package/src/lib/services/sets.js +9 -2
  34. package/src/lib/services/status.js +2 -2
  35. package/src/lib/services/vaultDecrypt.js +126 -0
  36. package/src/shared/logger.js +0 -3
  37. package/src/cli/actions/ext/hub/login.js +0 -126
  38. package/src/cli/actions/ext/hub/logout.js +0 -43
  39. package/src/cli/actions/ext/hub/open.js +0 -63
  40. package/src/cli/actions/ext/hub/pull.js +0 -105
  41. package/src/cli/actions/ext/hub/push.js +0 -112
  42. package/src/cli/actions/ext/hub/status.js +0 -8
  43. package/src/cli/actions/ext/hub/token.js +0 -9
  44. package/src/cli/commands/ext/hub.js +0 -89
  45. package/src/cli/commands/vault.js +0 -57
  46. package/src/lib/helpers/clipboardy/fallbacks/linux/xsel +0 -0
  47. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_i686.exe +0 -0
  48. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_x86_64.exe +0 -0
  49. package/src/lib/helpers/clipboardy/linux.js +0 -57
  50. package/src/lib/helpers/clipboardy/macos.js +0 -14
  51. package/src/lib/helpers/clipboardy/termux.js +0 -41
  52. package/src/lib/helpers/clipboardy/windows.js +0 -16
  53. package/src/lib/helpers/clipboardy.js +0 -51
  54. package/src/lib/helpers/extractUsernameName.js +0 -10
  55. package/src/lib/helpers/forgivingDirectory.js +0 -11
  56. package/src/lib/helpers/gitRoot.js +0 -14
  57. package/src/lib/helpers/gitUrl.js +0 -13
  58. package/src/lib/helpers/isGitRepo.js +0 -13
  59. package/src/lib/helpers/isGithub.js +0 -5
  60. package/src/lib/helpers/resolvePath.js +0 -8
  61. package/src/shared/createSpinner.js +0 -17
@@ -2,27 +2,85 @@ const fs = require('fs')
2
2
  const path = require('path')
3
3
  const dotenv = require('dotenv')
4
4
 
5
- const guessPrivateKeyName = require('./guessPrivateKeyName')
5
+ const ENCODING = 'utf8'
6
+ const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
7
+ const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
6
8
 
7
- function smartDotenvPrivateKey (envFilepath) {
8
- const privateKeyName = guessPrivateKeyName(envFilepath) // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
9
+ const guessPrivateKeyName = require('./guessPrivateKeyName')
9
10
 
10
- // process.env wins
11
+ function searchProcessEnv (privateKeyName) {
11
12
  if (process.env[privateKeyName] && process.env[privateKeyName].length > 0) {
12
13
  return process.env[privateKeyName]
13
14
  }
15
+ }
14
16
 
15
- // fallback to presence of .env.keys - path/to/.env.keys
17
+ function searchKeysFile (privateKeyName, envFilepath) {
16
18
  const directory = path.dirname(envFilepath)
17
19
  const envKeysFilepath = path.resolve(directory, '.env.keys')
20
+
18
21
  if (fs.existsSync(envKeysFilepath)) {
19
- const keysSrc = fs.readFileSync(envKeysFilepath)
22
+ const keysSrc = fs.readFileSync(envKeysFilepath, { encoding: ENCODING })
20
23
  const keysParsed = dotenv.parse(keysSrc)
21
24
 
22
25
  if (keysParsed[privateKeyName] && keysParsed[privateKeyName].length > 0) {
23
26
  return keysParsed[privateKeyName]
24
27
  }
25
28
  }
29
+ }
30
+
31
+ function invertForPrivateKeyName (envFilepath) {
32
+ if (!fs.existsSync(envFilepath)) {
33
+ return null
34
+ }
35
+
36
+ const envSrc = fs.readFileSync(envFilepath, { encoding: ENCODING })
37
+ const envParsed = dotenv.parse(envSrc)
38
+
39
+ let publicKeyName
40
+ for (const keyName of Object.keys(envParsed)) {
41
+ if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
42
+ publicKeyName = keyName // find DOTENV_PUBLIC_KEY* in filename
43
+ }
44
+ }
45
+
46
+ if (publicKeyName) {
47
+ return publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA) // return inverted (DOTENV_PUBLIC_KEY* -> DOTENV_PRIVATE_KEY*) if found
48
+ }
49
+
50
+ return null
51
+ }
52
+
53
+ function smartDotenvPrivateKey (envFilepath) {
54
+ let privateKey = null
55
+ let privateKeyName = guessPrivateKeyName(envFilepath) // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
56
+
57
+ // 1. attempt process.env first
58
+ privateKey = searchProcessEnv(privateKeyName)
59
+ if (privateKey) {
60
+ return privateKey
61
+ }
62
+
63
+ // 2. attempt .env.keys second (path/to/.env.keys)
64
+ privateKey = searchKeysFile(privateKeyName, envFilepath)
65
+ if (privateKey) {
66
+ return privateKey
67
+ }
68
+
69
+ // 3. attempt inverting `DOTENV_PUBLIC_KEY*` name inside file (unlocks custom filenames not matching .env.${ENVIRONMENT} pattern)
70
+ privateKeyName = invertForPrivateKeyName(envFilepath)
71
+ if (privateKeyName) {
72
+ // 3.1 attempt process.env first
73
+ privateKey = searchProcessEnv(privateKeyName)
74
+ if (privateKey) {
75
+ return privateKey
76
+ }
77
+
78
+ // 3.2. attempt .env.keys second (path/to/.env.keys)
79
+ privateKey = searchKeysFile(privateKeyName, envFilepath)
80
+ if (privateKey) {
81
+ return privateKey
82
+ }
83
+ }
26
84
 
27
85
  return null
28
86
  }
package/src/lib/main.js CHANGED
@@ -10,6 +10,7 @@ const Run = require('./services/run')
10
10
  const Sets = require('./services/sets')
11
11
  const Status = require('./services/status')
12
12
  const Encrypt = require('./services/encrypt')
13
+ const Decrypt = require('./services/decrypt')
13
14
  const Genexample = require('./services/genexample')
14
15
  const Settings = require('./services/settings')
15
16
  const VaultEncrypt = require('./services/vaultEncrypt')
@@ -162,11 +163,6 @@ const parse = function (src) {
162
163
  return dotenv.parse(src)
163
164
  }
164
165
 
165
- /** @type {import('./main').vaultEncrypt} */
166
- const vaultEncrypt = function (directory, envFile) {
167
- return new VaultEncrypt(directory, envFile).run()
168
- }
169
-
170
166
  /** @type {import('./main').ls} */
171
167
  const ls = function (directory, envFile) {
172
168
  return new Ls(directory, envFile).run()
@@ -198,6 +194,11 @@ const encrypt = function (envFile, key) {
198
194
  return new Encrypt(envFile, key).run()
199
195
  }
200
196
 
197
+ /** @type {import('./main').encrypt} */
198
+ const decrypt = function (envFile, key) {
199
+ return new Decrypt(envFile, key).run()
200
+ }
201
+
201
202
  /** @type {import('./main').status} */
202
203
  const status = function (directory) {
203
204
  return new Status(directory).run()
@@ -211,8 +212,8 @@ const settings = function (key = null) {
211
212
 
212
213
  // misc/cleanup
213
214
 
214
- /** @type {import('./main').decrypt} */
215
- const decrypt = function (encrypted, keyStr) {
215
+ /** @type {import('./main').vaultDecrypt} */
216
+ const vaultDecrypt = function (encrypted, keyStr) {
216
217
  try {
217
218
  return dotenv.decrypt(encrypted, keyStr)
218
219
  } catch (e) {
@@ -236,6 +237,11 @@ const decrypt = function (encrypted, keyStr) {
236
237
  }
237
238
  }
238
239
 
240
+ /** @type {import('./main').vaultEncrypt} */
241
+ const vaultEncrypt = function (directory, envFile) {
242
+ return new VaultEncrypt(directory, envFile).run()
243
+ }
244
+
239
245
  module.exports = {
240
246
  // dotenv proxies
241
247
  config,
@@ -243,7 +249,7 @@ module.exports = {
243
249
  parse,
244
250
  // actions related
245
251
  encrypt,
246
- vaultEncrypt,
252
+ decrypt,
247
253
  ls,
248
254
  get,
249
255
  set,
@@ -252,5 +258,6 @@ module.exports = {
252
258
  // settings
253
259
  settings,
254
260
  // misc/cleanup
255
- decrypt
261
+ vaultEncrypt,
262
+ vaultDecrypt
256
263
  }
@@ -2,124 +2,105 @@ const fs = require('fs')
2
2
  const path = require('path')
3
3
  const dotenv = require('dotenv')
4
4
 
5
- const ENCODING = 'utf8'
5
+ const smartDotenvPrivateKey = require('./../helpers/smartDotenvPrivateKey')
6
+ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
7
+ const decryptValue = require('./../helpers/decryptValue')
8
+ const isEncrypted = require('./../helpers/isEncrypted')
9
+ const replace = require('./../helpers/replace')
6
10
 
7
- const libDecrypt = require('./../../lib/helpers/decrypt')
11
+ const ENCODING = 'utf8'
8
12
 
9
13
  class Decrypt {
10
- constructor (directory = '.', environment) {
11
- this.directory = directory
12
- this.environment = environment
13
-
14
- this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
15
- this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
16
-
17
- this.processedEnvs = []
18
- this.changedFilenames = new Set()
19
- this.unchangedFilenames = new Set()
14
+ constructor (envFile = '.env', key = []) {
15
+ this.envFile = envFile
16
+ this.key = key
17
+ this.processedEnvFiles = []
18
+ this.changedFilepaths = new Set()
19
+ this.unchangedFilepaths = new Set()
20
20
  }
21
21
 
22
22
  run () {
23
- if (!fs.existsSync(this.envVaultFilepath)) {
24
- const code = 'MISSING_ENV_VAULT_FILE'
25
- const message = `missing .env.vault (${this.envVaultFilepath})`
26
- const help = `? generate one with [dotenvx encrypt ${this.directory}]`
27
-
28
- const error = new Error(message)
29
- error.code = code
30
- error.help = help
31
- throw error
32
- }
33
-
34
- if (!fs.existsSync(this.envKeysFilepath)) {
35
- const code = 'MISSING_ENV_KEYS_FILE'
36
- const message = `missing .env.keys (${this.envKeysFilepath})`
37
- const help = '? a .env.keys file must be present in order to decrypt your .env.vault contents to .env file(s)'
38
-
39
- const error = new Error(message)
40
- error.code = code
41
- error.help = help
42
- throw error
43
- }
44
-
45
- const dotenvKeys = dotenv.configDotenv({ path: this.envKeysFilepath }).parsed
46
- const dotenvVault = dotenv.configDotenv({ path: this.envVaultFilepath }).parsed
47
- const environments = this._environments()
48
-
49
- if (environments.length > 0) {
50
- // iterate over the environments
51
- for (const environment of environments) {
52
- const value = dotenvKeys[`DOTENV_KEY_${environment.toUpperCase()}`]
53
- const row = this._processRow(value, dotenvVault, environment)
54
-
55
- this.processedEnvs.push(row)
23
+ const envFilepaths = this._envFilepaths()
24
+ const keys = this._keys()
25
+ for (const envFilepath of envFilepaths) {
26
+ const filepath = path.resolve(envFilepath)
27
+
28
+ const row = {}
29
+ row.keys = []
30
+ row.filepath = filepath
31
+ row.envFilepath = envFilepath
32
+
33
+ try {
34
+ // get the src
35
+ let src = fs.readFileSync(filepath, { encoding: ENCODING })
36
+
37
+ // if DOTENV_PRIVATE_KEY_* already set in process.env then use it
38
+ const privateKey = smartDotenvPrivateKey(envFilepath)
39
+ row.privateKey = privateKey
40
+ row.privateKeyName = guessPrivateKeyName(filepath)
41
+
42
+ // track possible changes
43
+ row.changed = false
44
+
45
+ // iterate over all non-encrypted values and encrypt them
46
+ const parsed = dotenv.parse(src)
47
+ for (const [key, value] of Object.entries(parsed)) {
48
+ if (keys.length < 1 || keys.includes(key)) { // optionally control which key to decrypt
49
+ const encrypted = isEncrypted(key, value)
50
+ if (encrypted) {
51
+ row.keys.push(key) // track key(s)
52
+
53
+ const plainValue = decryptValue(value, privateKey)
54
+ // once newSrc is built write it out
55
+ src = replace(src, key, plainValue)
56
+
57
+ row.changed = true // track change
58
+ }
59
+ }
60
+ }
61
+
62
+ if (row.changed) {
63
+ row.envSrc = src
64
+ this.changedFilepaths.add(envFilepath)
65
+ } else {
66
+ row.envSrc = src
67
+ this.unchangedFilepaths.add(envFilepath)
68
+ }
69
+ } catch (e) {
70
+ if (e.code === 'ENOENT') {
71
+ const error = new Error(`missing ${envFilepath} file (${filepath})`)
72
+ error.code = 'MISSING_ENV_FILE'
73
+
74
+ row.error = error
75
+ } else {
76
+ row.error = e
77
+ }
56
78
  }
57
- } else {
58
- for (const [dotenvKey, value] of Object.entries(dotenvKeys)) {
59
- const environment = dotenvKey.replace('DOTENV_KEY_', '').toLowerCase()
60
- const row = this._processRow(value, dotenvVault, environment)
61
79
 
62
- this.processedEnvs.push(row)
63
- }
80
+ this.processedEnvFiles.push(row)
64
81
  }
65
82
 
66
83
  return {
67
- processedEnvs: this.processedEnvs,
68
- changedFilenames: [...this.changedFilenames],
69
- unchangedFilenames: [...this.unchangedFilenames]
84
+ processedEnvFiles: this.processedEnvFiles,
85
+ changedFilepaths: [...this.changedFilepaths],
86
+ unchangedFilepaths: [...this.unchangedFilepaths]
70
87
  }
71
88
  }
72
89
 
73
- _processRow (value, dotenvVault, environment) {
74
- environment = environment.toLowerCase() // important so we don't later write .env.DEVELOPMENT for example
75
- const vaultKey = `DOTENV_VAULT_${environment.toUpperCase()}`
76
- const ciphertext = dotenvVault[vaultKey] // attempt to find ciphertext
77
-
78
- const row = {}
79
- row.environment = environment
80
- row.dotenvKey = value ? value.trim() : value
81
- row.ciphertext = ciphertext
82
-
83
- if (ciphertext && ciphertext.length >= 1) {
84
- // Decrypt
85
- const decrypted = libDecrypt(ciphertext, value.trim())
86
- row.decrypted = decrypted
87
-
88
- // envFilename
89
- // replace _ with . to support filenames like .env.development.local
90
- let envFilename = `.env.${environment.replace('_', '.')}`
91
- if (environment === 'development') {
92
- envFilename = '.env'
93
- }
94
- row.filename = envFilename
95
- row.filepath = path.resolve(this.directory, envFilename)
96
-
97
- // check if exists and is changing
98
- if (fs.existsSync(row.filepath) && (fs.readFileSync(row.filepath, { encoding: ENCODING }).toString() === decrypted)) {
99
- this.unchangedFilenames.add(envFilename)
100
- } else {
101
- row.shouldWrite = true
102
- this.changedFilenames.add(envFilename)
103
- }
104
- } else {
105
- const message = `${vaultKey} missing in .env.vault: ${this.envVaultFilepath}`
106
- const warning = new Error(message)
107
- row.warning = warning
90
+ _envFilepaths () {
91
+ if (!Array.isArray(this.envFile)) {
92
+ return [this.envFile]
108
93
  }
109
94
 
110
- return row
95
+ return this.envFile
111
96
  }
112
97
 
113
- _environments () {
114
- if (this.environment === undefined) {
115
- return []
116
- }
117
-
118
- if (!Array.isArray(this.environment)) {
119
- return [this.environment]
98
+ _keys () {
99
+ if (!Array.isArray(this.key)) {
100
+ return [this.key]
120
101
  }
121
102
 
122
- return this.environment
103
+ return this.key
123
104
  }
124
105
  }
125
106
 
@@ -6,6 +6,7 @@ const findOrCreatePublicKey = require('./../helpers/findOrCreatePublicKey')
6
6
  const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
7
7
  const encryptValue = require('./../helpers/encryptValue')
8
8
  const isEncrypted = require('./../helpers/isEncrypted')
9
+ const isPublicKey = require('./../helpers/isPublicKey')
9
10
  const replace = require('./../helpers/replace')
10
11
 
11
12
  const ENCODING = 'utf8'
@@ -37,25 +38,29 @@ class Encrypt {
37
38
  const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
38
39
  const {
39
40
  envSrc,
41
+ keysSrc,
40
42
  publicKey,
41
43
  privateKey,
44
+ publicKeyAdded,
42
45
  privateKeyAdded
43
46
  } = findOrCreatePublicKey(filepath, envKeysFilepath)
47
+
48
+ // handle .env.keys write
49
+ fs.writeFileSync(envKeysFilepath, keysSrc)
50
+
51
+ src = envSrc // src was potentially modified by findOrCreatePublicKey so we set it again here
52
+
53
+ row.changed = publicKeyAdded // track change
44
54
  row.publicKey = publicKey
45
55
  row.privateKey = privateKey
46
- row.privateKeyName = guessPrivateKeyName(filepath)
47
56
  row.privateKeyAdded = privateKeyAdded
48
-
49
- src = envSrc // src was potentially changed by findOrCreatePublicKey so we set it again here
50
-
51
- // track possible changes
52
- row.changed = false
57
+ row.privateKeyName = guessPrivateKeyName(filepath)
53
58
 
54
59
  // iterate over all non-encrypted values and encrypt them
55
60
  const parsed = dotenv.parse(src)
56
61
  for (const [key, value] of Object.entries(parsed)) {
57
62
  if (keys.length < 1 || keys.includes(key)) { // optionally control which key to encrypt
58
- const encrypted = isEncrypted(key, value)
63
+ const encrypted = isEncrypted(key, value) || isPublicKey(key, value)
59
64
  if (!encrypted) {
60
65
  row.keys.push(key) // track key(s)
61
66
 
@@ -43,14 +43,21 @@ class Sets {
43
43
  const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
44
44
  const {
45
45
  envSrc,
46
+ keysSrc,
46
47
  publicKey,
47
48
  privateKey,
49
+ publicKeyAdded,
48
50
  privateKeyAdded
49
51
  } = findOrCreatePublicKey(filepath, envKeysFilepath)
50
- src = envSrc // overwrite the original read (because findOrCreatePublicKey) rewrite to it
52
+
53
+ // handle .env.keys write
54
+ fs.writeFileSync(envKeysFilepath, keysSrc)
55
+
56
+ src = envSrc // src was potentially modified by findOrCreatePublicKey so we set it again here
57
+
51
58
  value = encryptValue(value, publicKey)
52
59
 
53
- row.changed = true // track change
60
+ row.changed = publicKeyAdded // track change
54
61
  row.encryptedValue = value
55
62
  row.publicKey = publicKey
56
63
  row.privateKey = privateKey
@@ -4,7 +4,7 @@ const diff = require('diff')
4
4
  const chalk = require('chalk')
5
5
 
6
6
  const Ls = require('./ls')
7
- const Decrypt = require('./decrypt')
7
+ const VaultDecrypt = require('./vaultDecrypt')
8
8
 
9
9
  const containsDirectory = require('./../helpers/containsDirectory')
10
10
  const guessEnvironment = require('./../helpers/guessEnvironment')
@@ -60,7 +60,7 @@ class Status {
60
60
  row.raw = fs.readFileSync(filepath, { encoding: ENCODING })
61
61
 
62
62
  // grab decrypted
63
- const { processedEnvs } = new Decrypt(this.directory, row.environment).run()
63
+ const { processedEnvs } = new VaultDecrypt(this.directory, row.environment).run()
64
64
  const result = processedEnvs[0]
65
65
 
66
66
  // handle warnings
@@ -0,0 +1,126 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const dotenv = require('dotenv')
4
+
5
+ const ENCODING = 'utf8'
6
+
7
+ const libDecrypt = require('./../../lib/helpers/decrypt')
8
+
9
+ class VaultDecrypt {
10
+ constructor (directory = '.', environment) {
11
+ this.directory = directory
12
+ this.environment = environment
13
+
14
+ this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
15
+ this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
16
+
17
+ this.processedEnvs = []
18
+ this.changedFilenames = new Set()
19
+ this.unchangedFilenames = new Set()
20
+ }
21
+
22
+ run () {
23
+ if (!fs.existsSync(this.envVaultFilepath)) {
24
+ const code = 'MISSING_ENV_VAULT_FILE'
25
+ const message = `missing .env.vault (${this.envVaultFilepath})`
26
+ const help = `? generate one with [dotenvx encrypt ${this.directory}]`
27
+
28
+ const error = new Error(message)
29
+ error.code = code
30
+ error.help = help
31
+ throw error
32
+ }
33
+
34
+ if (!fs.existsSync(this.envKeysFilepath)) {
35
+ const code = 'MISSING_ENV_KEYS_FILE'
36
+ const message = `missing .env.keys (${this.envKeysFilepath})`
37
+ const help = '? a .env.keys file must be present in order to decrypt your .env.vault contents to .env file(s)'
38
+
39
+ const error = new Error(message)
40
+ error.code = code
41
+ error.help = help
42
+ throw error
43
+ }
44
+
45
+ const dotenvKeys = dotenv.configDotenv({ path: this.envKeysFilepath }).parsed
46
+ const dotenvVault = dotenv.configDotenv({ path: this.envVaultFilepath }).parsed
47
+ const environments = this._environments()
48
+
49
+ if (environments.length > 0) {
50
+ // iterate over the environments
51
+ for (const environment of environments) {
52
+ const value = dotenvKeys[`DOTENV_KEY_${environment.toUpperCase()}`]
53
+ const row = this._processRow(value, dotenvVault, environment)
54
+
55
+ this.processedEnvs.push(row)
56
+ }
57
+ } else {
58
+ for (const [dotenvKey, value] of Object.entries(dotenvKeys)) {
59
+ const environment = dotenvKey.replace('DOTENV_KEY_', '').toLowerCase()
60
+ const row = this._processRow(value, dotenvVault, environment)
61
+
62
+ this.processedEnvs.push(row)
63
+ }
64
+ }
65
+
66
+ return {
67
+ processedEnvs: this.processedEnvs,
68
+ changedFilenames: [...this.changedFilenames],
69
+ unchangedFilenames: [...this.unchangedFilenames]
70
+ }
71
+ }
72
+
73
+ _processRow (value, dotenvVault, environment) {
74
+ environment = environment.toLowerCase() // important so we don't later write .env.DEVELOPMENT for example
75
+ const vaultKey = `DOTENV_VAULT_${environment.toUpperCase()}`
76
+ const ciphertext = dotenvVault[vaultKey] // attempt to find ciphertext
77
+
78
+ const row = {}
79
+ row.environment = environment
80
+ row.dotenvKey = value ? value.trim() : value
81
+ row.ciphertext = ciphertext
82
+
83
+ if (ciphertext && ciphertext.length >= 1) {
84
+ // Decrypt
85
+ const decrypted = libDecrypt(ciphertext, value.trim())
86
+ row.decrypted = decrypted
87
+
88
+ // envFilename
89
+ // replace _ with . to support filenames like .env.development.local
90
+ let envFilename = `.env.${environment.replace('_', '.')}`
91
+ if (environment === 'development') {
92
+ envFilename = '.env'
93
+ }
94
+ row.filename = envFilename
95
+ row.filepath = path.resolve(this.directory, envFilename)
96
+
97
+ // check if exists and is changing
98
+ if (fs.existsSync(row.filepath) && (fs.readFileSync(row.filepath, { encoding: ENCODING }).toString() === decrypted)) {
99
+ this.unchangedFilenames.add(envFilename)
100
+ } else {
101
+ row.shouldWrite = true
102
+ this.changedFilenames.add(envFilename)
103
+ }
104
+ } else {
105
+ const message = `${vaultKey} missing in .env.vault: ${this.envVaultFilepath}`
106
+ const warning = new Error(message)
107
+ row.warning = warning
108
+ }
109
+
110
+ return row
111
+ }
112
+
113
+ _environments () {
114
+ if (this.environment === undefined) {
115
+ return []
116
+ }
117
+
118
+ if (!Array.isArray(this.environment)) {
119
+ return [this.environment]
120
+ }
121
+
122
+ return this.environment
123
+ }
124
+ }
125
+
126
+ module.exports = VaultDecrypt
@@ -9,7 +9,6 @@ const transports = winston.transports
9
9
  const packageJson = require('./../lib/helpers/packageJson')
10
10
 
11
11
  const levels = {
12
- blank0: 0,
13
12
  error: 0,
14
13
  errorv: 0,
15
14
  errorvp: 0,
@@ -47,8 +46,6 @@ const dotenvxFormat = printf(({ level, message, label, timestamp }) => {
47
46
  const formattedMessage = typeof message === 'object' ? JSON.stringify(message) : message
48
47
 
49
48
  switch (level.toLowerCase()) {
50
- case 'blank0': // special blank that always displays - even with quiet flag (for use with get action)
51
- return formattedMessage
52
49
  case 'error':
53
50
  return error(formattedMessage)
54
51
  case 'errorv':