@dotenvx/dotenvx 0.38.0 → 0.40.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.
@@ -0,0 +1,73 @@
1
+ const fs = require('fs')
2
+ const logger = require('./../../shared/logger')
3
+
4
+ const main = require('./../../lib/main')
5
+
6
+ const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
7
+
8
+ const ENCODING = 'utf8'
9
+
10
+ async function encryptme () {
11
+ const options = this.opts()
12
+ logger.debug(`options: ${JSON.stringify(options)}`)
13
+
14
+ try {
15
+ const {
16
+ processedEnvFiles,
17
+ changedFilepaths,
18
+ unchangedFilepaths
19
+ } = main.encryptme(options.envFile)
20
+
21
+ for (const processedEnvFile of processedEnvFiles) {
22
+ logger.verbose(`encrypting ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
23
+ if (processedEnvFile.error) {
24
+ if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
25
+ logger.warn(processedEnvFile.error)
26
+ logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx encryptme]`)
27
+ } else {
28
+ logger.warn(processedEnvFile.error)
29
+ }
30
+ } else if (processedEnvFile.changed) {
31
+ fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
32
+
33
+ logger.verbose(`encrypted ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
34
+ } else {
35
+ logger.verbose(`no changes ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
36
+ }
37
+ }
38
+
39
+ if (changedFilepaths.length > 0) {
40
+ logger.success(`✔ encrypted (${changedFilepaths.join(',')})`)
41
+ } else if (unchangedFilepaths.length > 0) {
42
+ logger.info(`no changes (${unchangedFilepaths})`)
43
+ } else {
44
+ // do nothing - scenario when no .env files found
45
+ }
46
+
47
+ for (const processedEnvFile of processedEnvFiles) {
48
+ if (processedEnvFile.privateKeyAdded) {
49
+ logger.success(`✔ key added to .env.keys (${processedEnvFile.privateKeyName})`)
50
+
51
+ if (!isIgnoringDotenvKeys()) {
52
+ logger.help2('ℹ add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
53
+ }
54
+
55
+ logger.help2(`ℹ run [${processedEnvFile.privateKeyName}='${processedEnvFile.privateKey}' dotenvx run -- yourcommand] to test decryption locally`)
56
+ }
57
+ }
58
+ } catch (error) {
59
+ logger.error(error.message)
60
+ if (error.help) {
61
+ logger.help(error.help)
62
+ }
63
+ if (error.debug) {
64
+ logger.debug(error.debug)
65
+ }
66
+ if (error.code) {
67
+ logger.debug(`ERROR_CODE: ${error.code}`)
68
+ }
69
+ process.exit(1)
70
+ }
71
+ }
72
+
73
+ module.exports = encryptme
@@ -1,5 +1,7 @@
1
1
  const logger = require('./../../shared/logger')
2
2
 
3
+ const conventions = require('./../../lib/helpers/conventions')
4
+
3
5
  const main = require('./../../lib/main')
4
6
 
5
7
  function get (key) {
@@ -8,7 +10,15 @@ function get (key) {
8
10
  const options = this.opts()
9
11
  logger.debug(`options: ${JSON.stringify(options)}`)
10
12
 
11
- const value = main.get(key, this.envs, options.overload, process.env.DOTENV_KEY, options.all)
13
+ let envs = []
14
+ // handle shorthand conventions - like --convention=nextjs
15
+ if (options.convention) {
16
+ envs = conventions(options.convention).concat(this.envs)
17
+ } else {
18
+ envs = this.envs
19
+ }
20
+
21
+ const value = main.get(key, envs, options.overload, process.env.DOTENV_KEY, options.all)
12
22
 
13
23
  if (typeof value === 'object' && value !== null) {
14
24
  if (options.prettyPrint) {
@@ -136,10 +136,11 @@ async function run () {
136
136
 
137
137
  if (processedEnv.error) {
138
138
  if (processedEnv.error.code === 'MISSING_ENV_FILE') {
139
- logger.warnv(processedEnv.error)
140
- logger.help(`? in development: add one with [echo "HELLO=World" > ${processedEnv.filepath}] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
141
- logger.help('? for production: set [DOTENV_KEY] on your server and re-deploy')
142
- logger.help('? for ci: set [DOTENV_KEY] on your ci and re-build')
139
+ // do not warn for conventions (too noisy)
140
+ if (!options.convention) {
141
+ logger.warnv(processedEnv.error)
142
+ logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.filepath}] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
143
+ }
143
144
  } else {
144
145
  logger.warnv(processedEnv.error)
145
146
  }
@@ -1,7 +1,12 @@
1
+ const fs = require('fs')
1
2
  const logger = require('./../../shared/logger')
2
3
 
3
4
  const main = require('./../../lib/main')
4
5
 
6
+ const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
7
+
8
+ const ENCODING = 'utf8'
9
+
5
10
  function set (key, value) {
6
11
  logger.debug(`key: ${key}`)
7
12
  logger.debug(`value: ${value}`)
@@ -12,36 +17,65 @@ function set (key, value) {
12
17
  try {
13
18
  const {
14
19
  processedEnvFiles,
15
- settableFilepaths
20
+ changedFilepaths,
21
+ unchangedFilepaths
16
22
  } = main.set(key, value, options.envFile, options.encrypt)
17
23
 
18
- let atLeastOneSuccess = false
24
+ let withEncryption = ''
25
+
26
+ if (options.encrypt) {
27
+ withEncryption = ' with encryption'
28
+ }
19
29
 
20
30
  for (const processedEnvFile of processedEnvFiles) {
21
- logger.verbose(`setting for ${processedEnvFile.filepath}`)
31
+ logger.verbose(`setting for ${processedEnvFile.envFilepath}`)
22
32
 
23
33
  if (processedEnvFile.error) {
24
34
  if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
25
35
  logger.warn(processedEnvFile.error)
26
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.filepath}] and re-run [dotenvx set]`)
36
+ logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx set]`)
27
37
  } else {
28
38
  logger.warn(processedEnvFile.error)
29
39
  }
30
40
  } else {
31
- atLeastOneSuccess = true
32
- logger.verbose(`${processedEnvFile.key} set`)
33
- logger.debug(`${processedEnvFile.key} set to ${processedEnvFile.value}`)
41
+ fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
42
+
43
+ logger.verbose(`${processedEnvFile.key} set${withEncryption} (${processedEnvFile.envFilepath})`)
44
+ logger.debug(`${processedEnvFile.key} set${withEncryption} to ${processedEnvFile.value} (${processedEnvFile.envFilepath})`)
34
45
  }
35
46
  }
36
47
 
37
- if (atLeastOneSuccess) {
38
- logger.success(`set ${key} (${settableFilepaths.join(', ')})`)
48
+ if (changedFilepaths.length > 0) {
49
+ logger.success(`✔ set ${key}${withEncryption} (${changedFilepaths.join(',')})`)
50
+ } else if (unchangedFilepaths.length > 0) {
51
+ logger.info(`no changes (${unchangedFilepaths})`)
52
+ } else {
53
+ // do nothing
54
+ }
55
+
56
+ for (const processedEnvFile of processedEnvFiles) {
57
+ if (processedEnvFile.privateKeyAdded) {
58
+ logger.success(`✔ key added to .env.keys (${processedEnvFile.privateKeyName})`)
59
+
60
+ if (!isIgnoringDotenvKeys()) {
61
+ logger.help2('ℹ add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
62
+ }
63
+
64
+ logger.help2(`ℹ run [${processedEnvFile.privateKeyName}='${processedEnvFile.privateKey}' dotenvx get ${key}] to test decryption locally`)
65
+ }
39
66
  }
40
67
  } catch (error) {
41
68
  logger.error(error.message)
42
69
  if (error.help) {
43
70
  logger.help(error.help)
44
71
  }
72
+ if (error.debug) {
73
+ logger.debug(error.debug)
74
+ }
75
+ if (error.code) {
76
+ logger.debug(`ERROR_CODE: ${error.code}`)
77
+ }
78
+ process.exit(1)
45
79
  }
46
80
  }
47
81
 
@@ -58,7 +58,6 @@ async function encrypt (directory) {
58
58
 
59
59
  if (addedKeys.length > 0) {
60
60
  spinner.succeed(`${pluralize('key', addedKeys.length)} added to .env.keys (${addedKeys})`)
61
- logger.help2('ℹ push .env.keys up to hub: [dotenvx hub push]')
62
61
  }
63
62
 
64
63
  if (addedVaults.length > 0) {
@@ -6,7 +6,7 @@ const logger = require('./../../shared/logger')
6
6
  const hub = new Command('hub')
7
7
 
8
8
  hub
9
- .description('interact with dotenvx hub')
9
+ .description('DEPRECATED: interact with dotenvx hub')
10
10
 
11
11
  const loginAction = require('./../actions/hub/login')
12
12
  hub
@@ -14,7 +14,7 @@ hub
14
14
  .description('authenticate to dotenvx hub')
15
15
  .option('-h, --hostname <url>', 'set hostname', store.getHostname())
16
16
  .action(function (...args) {
17
- logger.warn('DEPRECATION NOTECE: [dotenvx hub login] will be removed in 1.0.0 release soon')
17
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub login] will be removed in 1.0.0 release soon')
18
18
 
19
19
  loginAction.apply(this, args)
20
20
  })
@@ -26,7 +26,7 @@ hub
26
26
  .argument('[directory]', 'directory to push', '.')
27
27
  .option('-h, --hostname <url>', 'set hostname', store.getHostname())
28
28
  .action(function (...args) {
29
- logger.warn('DEPRECATION NOTECE: [dotenvx hub push] will be removed in 1.0.0 release soon')
29
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub push] will be removed in 1.0.0 release soon')
30
30
 
31
31
  pushAction.apply(this, args)
32
32
  })
@@ -38,7 +38,7 @@ hub
38
38
  .argument('[directory]', 'directory to pull', '.')
39
39
  .option('-h, --hostname <url>', 'set hostname', store.getHostname())
40
40
  .action(function (...args) {
41
- logger.warn('DEPRECATION NOTECE: [dotenvx hub pull] will be removed in 1.0.0 release soon')
41
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub pull] will be removed in 1.0.0 release soon')
42
42
 
43
43
  pullAction.apply(this, args)
44
44
  })
@@ -49,7 +49,7 @@ hub
49
49
  .description('view repository on dotenvx hub')
50
50
  .option('-h, --hostname <url>', 'set hostname', store.getHostname())
51
51
  .action(function (...args) {
52
- logger.warn('DEPRECATION NOTECE: [dotenvx hub open] will be removed in 1.0.0 release soon')
52
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub open] will be removed in 1.0.0 release soon')
53
53
 
54
54
  openAction.apply(this, args)
55
55
  })
@@ -60,7 +60,7 @@ hub
60
60
  .description('print the auth token dotenvx hub is configured to use')
61
61
  .option('-h, --hostname <url>', 'set hostname', 'https://hub.dotenvx.com')
62
62
  .action(function (...args) {
63
- logger.warn('DEPRECATION NOTECE: [dotenvx hub token] will be removed in 1.0.0 release soon')
63
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub token] will be removed in 1.0.0 release soon')
64
64
 
65
65
  tokenAction.apply(this, args)
66
66
  })
@@ -70,7 +70,7 @@ hub
70
70
  .command('status')
71
71
  .description('display logged in user')
72
72
  .action(function (...args) {
73
- logger.warn('DEPRECATION NOTECE: [dotenvx hub status] will be removed in 1.0.0 release soon')
73
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub status] will be removed in 1.0.0 release soon')
74
74
 
75
75
  statusAction.apply(this, args)
76
76
  })
@@ -81,7 +81,7 @@ hub
81
81
  .description('log out this machine from dotenvx hub')
82
82
  .option('-h, --hostname <url>', 'set hostname', store.getHostname())
83
83
  .action(function (...args) {
84
- logger.warn('DEPRECATION NOTECE: [dotenvx hub logout] will be removed in 1.0.0 release soon')
84
+ logger.warn('DEPRECATION NOTICE: [dotenvx hub logout] will be removed in 1.0.0 release soon')
85
85
 
86
86
  logoutAction.apply(this, args)
87
87
  })
@@ -85,6 +85,7 @@ program.command('get')
85
85
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
86
86
  .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
87
87
  .option('-o, --overload', 'override existing env variables')
88
+ .option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
88
89
  .option('-a, --all', 'include all machine envs as well')
89
90
  .option('-pp, --pretty-print', 'pretty print output')
90
91
  .action(function (...args) {
@@ -102,6 +103,19 @@ program.command('set')
102
103
  .option('-c, --encrypt', 'encrypt value')
103
104
  .action(require('./actions/set'))
104
105
 
106
+ // dotenvx encryptme
107
+ program.command('encryptme')
108
+ .description('encrypt env file(s) environment variables')
109
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
110
+ .action(require('./actions/encryptme'))
111
+
112
+ // dotenvx ls
113
+ program.command('ls')
114
+ .description('print all .env files in a tree structure')
115
+ .argument('[directory]', 'directory to list .env files from', '.')
116
+ .option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
117
+ .action(require('./actions/ls'))
118
+
105
119
  // dotenvx genexample
106
120
  program.command('genexample')
107
121
  .description('generate .env.example')
@@ -133,13 +147,6 @@ program.command('scan')
133
147
  .description('scan for leaked secrets')
134
148
  .action(require('./actions/scan'))
135
149
 
136
- // dotenvx ls
137
- program.command('ls')
138
- .description('print all .env files in a tree structure')
139
- .argument('[directory]', 'directory to list .env files from', '.')
140
- .option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
141
- .action(require('./actions/ls'))
142
-
143
150
  // dotenvx settings
144
151
  program.command('settings')
145
152
  .description('print current dotenvx settings')
@@ -147,6 +154,9 @@ program.command('settings')
147
154
  .option('-pp, --pretty-print', 'pretty print output')
148
155
  .action(require('./actions/settings'))
149
156
 
157
+ // dotenvx vault
158
+ program.addCommand(require('./commands/vault'))
159
+
150
160
  // dotenvx encrypt
151
161
  const encryptAction = require('./actions/vault/encrypt')
152
162
  program.command('encrypt')
@@ -156,6 +166,7 @@ program.command('encrypt')
156
166
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
157
167
  .action(function (...args) {
158
168
  logger.warn('DEPRECATION NOTICE: [dotenvx encrypt] has moved. change your command to [dotenvx vault encrypt]')
169
+ logger.warn('DEPRECATION NOTICE: [dotenvx encryptme] will become [dotenvx encrypt] in a 1.0.0 release scheduled for middle of June 2024.')
159
170
 
160
171
  encryptAction.apply(this, args)
161
172
  })
@@ -167,7 +178,7 @@ program.command('decrypt')
167
178
  .argument('[directory]', 'directory to decrypt', '.')
168
179
  .option('-e, --environment <environments...>', 'environment(s) to decrypt')
169
180
  .action(function (...args) {
170
- logger.warn('DEPRECATION NOTECE: [dotenvx decrypt] has moved. change your command to [dotenvx vault decrypt]')
181
+ logger.warn('DEPRECATION NOTICE: [dotenvx decrypt] has moved. change your command to [dotenvx vault decrypt]')
171
182
 
172
183
  decryptAction.apply(this, args)
173
184
  })
@@ -183,9 +194,6 @@ program.command('status')
183
194
  statusAction.apply(this, args)
184
195
  })
185
196
 
186
- // dotenvx vault
187
- program.addCommand(require('./commands/vault'))
188
-
189
197
  // dotenvx hub
190
198
  program.addCommand(require('./commands/hub'))
191
199
 
@@ -31,7 +31,8 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
31
31
  envSrc,
32
32
  keysSrc,
33
33
  publicKey: envParsed[publicKeyName],
34
- privateKey: keysParsed[privateKeyName]
34
+ privateKey: keysParsed[privateKeyName],
35
+ privateKeyAdded: false
35
36
  }
36
37
  }
37
38
 
@@ -73,7 +74,8 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
73
74
  envSrc,
74
75
  keysSrc,
75
76
  publicKey,
76
- privateKey
77
+ privateKey,
78
+ privateKeyAdded: true
77
79
  }
78
80
  }
79
81
 
@@ -0,0 +1,8 @@
1
+ const ENCRYPTION_PATTERN = /^encrypted:.+/
2
+ const PUBLIC_KEY_PATTERN = /^DOTENV_PUBLIC_KEY/
3
+
4
+ function isEncrypted (key, value) {
5
+ return PUBLIC_KEY_PATTERN.test(key) || ENCRYPTION_PATTERN.test(value)
6
+ }
7
+
8
+ module.exports = isEncrypted
@@ -0,0 +1,18 @@
1
+ const dotenv = require('dotenv')
2
+
3
+ const isEncrypted = require('./isEncrypted')
4
+
5
+ function isFullyEncrypted (src) {
6
+ const parsed = dotenv.parse(src)
7
+
8
+ for (const [key, value] of Object.entries(parsed)) {
9
+ const result = isEncrypted(key, value)
10
+ if (!result) {
11
+ return false
12
+ }
13
+ }
14
+
15
+ return true
16
+ }
17
+
18
+ module.exports = isFullyEncrypted
@@ -0,0 +1,19 @@
1
+ const fs = require('fs')
2
+ const ignore = require('ignore')
3
+
4
+ function isIgnoringDotenvKeys () {
5
+ if (!fs.existsSync('.gitignore')) {
6
+ return false
7
+ }
8
+
9
+ const gitignore = fs.readFileSync('.gitignore').toString()
10
+ const ig = ignore(gitignore).add(gitignore)
11
+
12
+ if (!ig.ignores('.env.keys')) {
13
+ return false
14
+ }
15
+
16
+ return false
17
+ }
18
+
19
+ module.exports = isIgnoringDotenvKeys
package/src/lib/main.js CHANGED
@@ -3,13 +3,14 @@ const dotenv = require('dotenv')
3
3
  const dotenvExpand = require('dotenv-expand')
4
4
 
5
5
  // services
6
- const Encrypt = require('./services/encrypt')
7
6
  const Ls = require('./services/ls')
8
7
  const Get = require('./services/get')
9
8
  const Sets = require('./services/sets')
10
9
  const Status = require('./services/status')
10
+ const Encrypt = require('./services/encrypt')
11
11
  const Genexample = require('./services/genexample')
12
12
  const Settings = require('./services/settings')
13
+ const VaultEncrypt = require('./services/vaultEncrypt')
13
14
 
14
15
  // helpers
15
16
  const dotenvEval = require('./helpers/dotenvEval')
@@ -41,9 +42,13 @@ const parse = function (src) {
41
42
  return dotenv.parse(src)
42
43
  }
43
44
 
44
- // actions related
45
+ // DEPRECATED: will became the same function as encryptme
45
46
  const encrypt = function (directory, envFile) {
46
- return new Encrypt(directory, envFile).run()
47
+ return new VaultEncrypt(directory, envFile).run()
48
+ }
49
+
50
+ const vaultEncrypt = function (directory, envFile) {
51
+ return new VaultEncrypt(directory, envFile).run()
47
52
  }
48
53
 
49
54
  const ls = function (directory, envFile) {
@@ -62,6 +67,10 @@ const set = function (key, value, envFile, encrypt) {
62
67
  return new Sets(key, value, envFile, encrypt).run()
63
68
  }
64
69
 
70
+ const encryptme = function (envFile) {
71
+ return new Encrypt(envFile).run()
72
+ }
73
+
65
74
  const status = function (directory) {
66
75
  return new Status(directory).run()
67
76
  }
@@ -96,9 +105,11 @@ module.exports = {
96
105
  parse,
97
106
  // actions related
98
107
  encrypt,
108
+ vaultEncrypt,
99
109
  ls,
100
110
  get,
101
111
  set,
112
+ encryptme,
102
113
  status,
103
114
  genexample,
104
115
  // settings
@@ -2,91 +2,93 @@ const fs = require('fs')
2
2
  const path = require('path')
3
3
  const dotenv = require('dotenv')
4
4
 
5
- const DotenvKeys = require('./../helpers/dotenvKeys')
6
- const DotenvVault = require('./../helpers/dotenvVault')
5
+ const findOrCreatePublicKey = require('./../helpers/findOrCreatePublicKey')
6
+ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
7
+ const encryptValue = require('./../helpers/encryptValue')
8
+ const isEncrypted = require('./../helpers/isEncrypted')
9
+ const replace = require('./../helpers/replace')
7
10
 
8
11
  const ENCODING = 'utf8'
9
12
 
10
- const findEnvFiles = require('../helpers/findEnvFiles')
11
-
12
13
  class Encrypt {
13
- constructor (directory = '.', envFile) {
14
- this.directory = directory
15
- this.envFile = envFile || findEnvFiles(directory)
16
- // calculated
17
- this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
18
- this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
14
+ constructor (envFile = '.env') {
15
+ this.envFile = envFile
16
+ this.processedEnvFiles = []
17
+ this.changedFilepaths = new Set()
18
+ this.unchangedFilepaths = new Set()
19
19
  }
20
20
 
21
21
  run () {
22
- if (this.envFile.length < 1) {
23
- const code = 'MISSING_ENV_FILES'
24
- const message = 'no .env* files found'
25
- const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx encrypt]'
26
-
27
- const error = new Error(message)
28
- error.code = code
29
- error.help = help
30
- throw error
31
- }
32
-
33
- const parsedDotenvKeys = this._parsedDotenvKeys()
34
- const parsedDotenvVaults = this._parsedDotenvVault()
35
22
  const envFilepaths = this._envFilepaths()
36
-
37
- // build filepaths to be passed to DotenvKeys
38
- const uniqueEnvFilepaths = new Set()
39
23
  for (const envFilepath of envFilepaths) {
40
- const filepath = path.resolve(this.directory, envFilepath)
41
- if (!fs.existsSync(filepath)) {
42
- const code = 'MISSING_ENV_FILE'
43
- const message = `file does not exist at [${filepath}]`
44
- const help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx encrypt]`
45
-
46
- const error = new Error(message)
47
- error.code = code
48
- error.help = help
49
- throw error
24
+ const filepath = path.resolve(envFilepath)
25
+
26
+ const row = {}
27
+ row.keys = []
28
+ row.filepath = filepath
29
+ row.envFilepath = envFilepath
30
+
31
+ try {
32
+ // get the original src
33
+ let src = fs.readFileSync(filepath, { encoding: ENCODING })
34
+ // get/generate the public key
35
+ const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
36
+ const {
37
+ envSrc,
38
+ publicKey,
39
+ privateKey,
40
+ privateKeyAdded
41
+ } = findOrCreatePublicKey(filepath, envKeysFilepath)
42
+ row.publicKey = publicKey
43
+ row.privateKey = privateKey
44
+ row.privateKeyName = guessPrivateKeyName(filepath)
45
+ row.privateKeyAdded = privateKeyAdded
46
+
47
+ src = envSrc // src was potentially changed by findOrCreatePublicKey so we set it again here
48
+
49
+ // track possible changes
50
+ row.changed = false
51
+
52
+ // iterate over all non-encrypted values and encrypt them
53
+ const parsed = dotenv.parse(src)
54
+ for (const [key, value] of Object.entries(parsed)) {
55
+ const encrypted = isEncrypted(key, value)
56
+ if (!encrypted) {
57
+ row.keys.push(key) // track key(s)
58
+
59
+ const encryptedValue = encryptValue(value, publicKey)
60
+ // once newSrc is built write it out
61
+ src = replace(src, key, encryptedValue)
62
+
63
+ row.changed = true // track change
64
+ }
65
+ }
66
+
67
+ if (row.changed) {
68
+ row.envSrc = src
69
+ this.changedFilepaths.add(envFilepath)
70
+ } else {
71
+ row.envSrc = src
72
+ this.unchangedFilepaths.add(envFilepath)
73
+ }
74
+ } catch (e) {
75
+ if (e.code === 'ENOENT') {
76
+ const error = new Error(`missing ${envFilepath} file (${filepath})`)
77
+ error.code = 'MISSING_ENV_FILE'
78
+
79
+ row.error = error
80
+ } else {
81
+ row.error = e
82
+ }
50
83
  }
51
84
 
52
- uniqueEnvFilepaths.add(filepath)
85
+ this.processedEnvFiles.push(row)
53
86
  }
54
87
 
55
- // generate .env.keys string
56
- const {
57
- dotenvKeys,
58
- dotenvKeysFile,
59
- addedKeys,
60
- existingKeys
61
- } = new DotenvKeys([...uniqueEnvFilepaths], parsedDotenvKeys).run()
62
-
63
- // build look up of .env filepaths and their raw content
64
- const dotenvFiles = {}
65
- for (const filepath of [...uniqueEnvFilepaths]) {
66
- const raw = fs.readFileSync(filepath, ENCODING)
67
- dotenvFiles[filepath] = raw
68
- }
69
-
70
- // generate .env.vault string
71
- const {
72
- dotenvVaultFile,
73
- addedVaults,
74
- existingVaults,
75
- addedDotenvFilenames
76
- } = new DotenvVault(dotenvFiles, dotenvKeys, parsedDotenvVaults).run()
77
-
78
88
  return {
79
- // from DotenvKeys
80
- dotenvKeys,
81
- dotenvKeysFile,
82
- addedKeys,
83
- existingKeys,
84
- // from DotenvVault
85
- dotenvVaultFile,
86
- addedVaults,
87
- existingVaults,
88
- addedDotenvFilenames,
89
- envFile: this.envFile
89
+ processedEnvFiles: this.processedEnvFiles,
90
+ changedFilepaths: [...this.changedFilepaths],
91
+ unchangedFilepaths: [...this.unchangedFilepaths]
90
92
  }
91
93
  }
92
94
 
@@ -97,22 +99,6 @@ class Encrypt {
97
99
 
98
100
  return this.envFile
99
101
  }
100
-
101
- _parsedDotenvKeys () {
102
- const options = {
103
- path: this.envKeysFilepath
104
- }
105
-
106
- return dotenv.configDotenv(options).parsed
107
- }
108
-
109
- _parsedDotenvVault () {
110
- const options = {
111
- path: this.envVaultFilepath
112
- }
113
-
114
- return dotenv.configDotenv(options).parsed
115
- }
116
102
  }
117
103
 
118
104
  module.exports = Encrypt