@dotenvx/dotenvx 0.17.0 → 0.18.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/README.md CHANGED
@@ -56,6 +56,18 @@ see [extended quickstart guide](https://dotenvx.com/docs/quickstart)
56
56
 
57
57
  More examples
58
58
 
59
+ * <details><summary>TypeScript 📘</summary><br>
60
+
61
+ ```sh
62
+ $ echo "HELLO=World" > .env
63
+ $ echo "console.log('Hello ' + process.env.HELLO)" > index.ts
64
+
65
+ $ dotenvx run -- npx ts-node index.ts
66
+ Hello World
67
+ ```
68
+
69
+ </details>
70
+
59
71
  * <details><summary>Python 🐍</summary><br>
60
72
 
61
73
  ```sh
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.17.0",
2
+ "version": "0.18.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -4,6 +4,7 @@ const main = require('./../../lib/main')
4
4
  const logger = require('./../../shared/logger')
5
5
  const helpers = require('./../helpers')
6
6
  const createSpinner = require('./../../shared/createSpinner')
7
+ const parseEncryptionKeyFromDotenvKey = require('./../../lib/helpers/parseEncryptionKeyFromDotenvKey')
7
8
 
8
9
  const spinner = createSpinner('decrypting')
9
10
 
@@ -50,7 +51,7 @@ async function decrypt () {
50
51
 
51
52
  // give warning if not found
52
53
  if (ciphertext && ciphertext.length >= 1) {
53
- const key = helpers._parseEncryptionKeyFromDotenvKey(value.trim())
54
+ const key = parseEncryptionKeyFromDotenvKey(value.trim())
54
55
 
55
56
  // Decrypt
56
57
  const decrypted = main.decrypt(ciphertext, key)
@@ -5,7 +5,6 @@ const main = require('./../../lib/main')
5
5
  const logger = require('./../../shared/logger')
6
6
  const helpers = require('./../helpers')
7
7
  const createSpinner = require('./../../shared/createSpinner')
8
-
9
8
  const spinner = createSpinner('encrypting')
10
9
 
11
10
  async function encrypt (directory) {
@@ -17,8 +16,6 @@ async function encrypt (directory) {
17
16
  const options = this.opts()
18
17
  logger.debug(`options: ${JSON.stringify(options)}`)
19
18
 
20
- const optionEnvFile = options.envFile || helpers.findEnvFiles(directory)
21
-
22
19
  try {
23
20
  const {
24
21
  dotenvKeys,
@@ -28,10 +25,11 @@ async function encrypt (directory) {
28
25
  dotenvVaultFile,
29
26
  addedVaults,
30
27
  existingVaults,
31
- addedDotenvFilenames
32
- } = main.encrypt(directory, optionEnvFile)
28
+ addedDotenvFilenames,
29
+ envFile
30
+ } = main.encrypt(directory, options.envFile)
33
31
 
34
- logger.verbose(`generating .env.keys from ${optionEnvFile}`)
32
+ logger.verbose(`generating .env.keys from ${envFile}`)
35
33
  if (addedKeys.length > 0) {
36
34
  logger.verbose(`generated ${addedKeys}`)
37
35
  }
@@ -40,7 +38,7 @@ async function encrypt (directory) {
40
38
  }
41
39
  fs.writeFileSync(path.resolve(directory, '.env.keys'), dotenvKeysFile)
42
40
 
43
- logger.verbose(`generating .env.vault from ${optionEnvFile}`)
41
+ logger.verbose(`generating .env.vault from ${envFile}`)
44
42
  if (addedVaults.length > 0) {
45
43
  logger.verbose(`encrypting ${addedVaults}`)
46
44
  }
@@ -53,7 +51,7 @@ async function encrypt (directory) {
53
51
  spinner.succeed(`encrypted to .env.vault (${addedDotenvFilenames})`)
54
52
  logger.help2('ℹ commit .env.vault to code: [git commit -am ".env.vault"]')
55
53
  } else {
56
- spinner.done(`no changes (${optionEnvFile})`)
54
+ spinner.done(`no changes (${envFile})`)
57
55
  }
58
56
 
59
57
  if (addedKeys.length > 0) {
@@ -64,7 +62,7 @@ async function encrypt (directory) {
64
62
  if (addedVaults.length > 0) {
65
63
  const DOTENV_VAULT_X = addedVaults[addedVaults.length - 1]
66
64
  const DOTENV_KEY_X = DOTENV_VAULT_X.replace('_VAULT_', '_KEY_')
67
- const tryKey = dotenvKeys[DOTENV_KEY_X] || '<dotenv_key_environment>'
65
+ const tryKey = dotenvKeys[DOTENV_KEY_X]
68
66
 
69
67
  logger.help2(`ℹ run [DOTENV_KEY='${tryKey}' dotenvx run -- yourcommand] to test decryption locally`)
70
68
  }
@@ -0,0 +1,24 @@
1
+ const logger = require('./../../shared/logger')
2
+
3
+ const main = require('./../../lib/main')
4
+
5
+ function get (key) {
6
+ logger.debug(`key: ${key}`)
7
+
8
+ const options = this.opts()
9
+ logger.debug(`options: ${JSON.stringify(options)}`)
10
+
11
+ const value = main.get(key, options.envFile, options.overload, options.all)
12
+
13
+ if (typeof value === 'object' && value !== null) {
14
+ if (options.prettyPrint) {
15
+ logger.blank(JSON.stringify(value, null, 2))
16
+ } else {
17
+ logger.blank(value)
18
+ }
19
+ } else {
20
+ logger.blank(value)
21
+ }
22
+ }
23
+
24
+ module.exports = get
@@ -6,12 +6,21 @@ const { confirm } = require('@inquirer/prompts')
6
6
  const createSpinner = require('./../../../shared/createSpinner')
7
7
  const store = require('./../../../shared/store')
8
8
  const logger = require('./../../../shared/logger')
9
- const helpers = require('./../../helpers')
10
9
 
11
10
  const OAUTH_CLIENT_ID = 'oac_dotenvxcli'
12
11
 
13
12
  const spinner = createSpinner('waiting on user authorization')
14
13
 
14
+ const formatCode = function (str) {
15
+ const parts = []
16
+
17
+ for (let i = 0; i < str.length; i += 4) {
18
+ parts.push(str.substring(i, i + 4))
19
+ }
20
+
21
+ return parts.join('-')
22
+ }
23
+
15
24
  async function pollTokenUrl (tokenUrl, deviceCode, interval) {
16
25
  logger.http(`POST ${tokenUrl} with deviceCode ${deviceCode} at interval ${interval}`)
17
26
 
@@ -100,7 +109,7 @@ async function login () {
100
109
  pollTokenUrl(tokenUrl, deviceCode, interval)
101
110
 
102
111
  // optionally allow user to open browser
103
- const answer = await confirm({ message: `press Enter to open [${verificationUri}] and enter code [${helpers.formatCode(userCode)}]...` })
112
+ const answer = await confirm({ message: `press Enter to open [${verificationUri}] and enter code [${formatCode(userCode)}]...` })
104
113
 
105
114
  if (answer) {
106
115
  await open(verificationUri)
@@ -1,9 +1,102 @@
1
1
  const fs = require('fs')
2
+ const execa = require('execa')
2
3
  const logger = require('./../../shared/logger')
3
4
  const helpers = require('./../helpers')
4
5
  const main = require('./../../lib/main')
6
+ const parseEncryptionKeyFromDotenvKey = require('./../../lib/helpers/parseEncryptionKeyFromDotenvKey')
5
7
 
6
8
  const ENCODING = 'utf8'
9
+ const REPORT_ISSUE_LINK = 'https://github.com/dotenvx/dotenvx/issues/new'
10
+
11
+ const executeCommand = async function (commandArgs, env) {
12
+ const signals = [
13
+ 'SIGHUP', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
14
+ 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'
15
+ ]
16
+
17
+ logger.debug(`executing process command [${commandArgs.join(' ')}]`)
18
+
19
+ // handler for SIGINT
20
+ let commandProcess
21
+ const sigintHandler = () => {
22
+ logger.debug('received SIGINT')
23
+ logger.debug('checking command process')
24
+ logger.debug(commandProcess)
25
+
26
+ if (commandProcess) {
27
+ logger.debug('sending SIGINT to command process')
28
+ commandProcess.kill('SIGINT') // Send SIGINT to the command process
29
+ } else {
30
+ logger.debug('no command process to send SIGINT to')
31
+ }
32
+ }
33
+
34
+ const handleOtherSignal = (signal) => {
35
+ logger.debug(`received ${signal}`)
36
+ }
37
+
38
+ try {
39
+ commandProcess = execa(commandArgs[0], commandArgs.slice(1), {
40
+ stdio: 'inherit',
41
+ env: { ...process.env, ...env }
42
+ })
43
+
44
+ process.on('SIGINT', sigintHandler)
45
+
46
+ signals.forEach(signal => {
47
+ process.on(signal, () => handleOtherSignal(signal))
48
+ })
49
+
50
+ // Wait for the command process to finish
51
+ const { exitCode } = await commandProcess
52
+
53
+ if (exitCode !== 0) {
54
+ logger.debug(`received exitCode ${exitCode}`)
55
+ throw new Error(`Command failed with exit code ${exitCode}`)
56
+ }
57
+ } catch (error) {
58
+ if (error.signal !== 'SIGINT') {
59
+ logger.error(error.message)
60
+ logger.error(`command [${commandArgs.join(' ')}] failed`)
61
+ logger.error('')
62
+ logger.error(` try without dotenvx: [${commandArgs.join(' ')}]`)
63
+ logger.error('')
64
+ logger.error('if that succeeds, then dotenvx is the culprit. report issue:')
65
+ logger.error(`<${REPORT_ISSUE_LINK}>`)
66
+ }
67
+
68
+ // Exit with the error code from the command process, or 1 if unavailable
69
+ process.exit(error.exitCode || 1)
70
+ } finally {
71
+ // Clean up: Remove the SIGINT handler
72
+ process.removeListener('SIGINT', sigintHandler)
73
+ }
74
+ }
75
+
76
+ const _parseCipherTextFromDotenvKeyAndParsedVault = function (dotenvKey, parsedVault) {
77
+ // Parse DOTENV_KEY. Format is a URI
78
+ let uri
79
+ try {
80
+ uri = new URL(dotenvKey)
81
+ } catch (e) {
82
+ throw new Error(`INVALID_DOTENV_KEY: ${e.message}`)
83
+ }
84
+
85
+ // Get environment
86
+ const environment = uri.searchParams.get('environment')
87
+ if (!environment) {
88
+ throw new Error('INVALID_DOTENV_KEY: Missing environment part')
89
+ }
90
+
91
+ // Get ciphertext payload
92
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
93
+ const ciphertext = parsedVault[environmentKey] // DOTENV_VAULT_PRODUCTION
94
+ if (!ciphertext) {
95
+ throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: cannot locate environment ${environmentKey} in your .env.vault file`)
96
+ }
97
+
98
+ return ciphertext
99
+ }
7
100
 
8
101
  async function run () {
9
102
  const commandArgs = this.args
@@ -38,8 +131,8 @@ async function run () {
38
131
  // Get full dotenvKey
39
132
  const dotenvKey = dotenvKeys[i].trim()
40
133
 
41
- const key = helpers._parseEncryptionKeyFromDotenvKey(dotenvKey)
42
- const ciphertext = helpers._parseCipherTextFromDotenvKeyAndParsedVault(dotenvKey, parsedVault)
134
+ const key = parseEncryptionKeyFromDotenvKey(dotenvKey)
135
+ const ciphertext = _parseCipherTextFromDotenvKeyAndParsedVault(dotenvKey, parsedVault)
43
136
 
44
137
  // Decrypt
45
138
  decrypted = main.decrypt(ciphertext, key)
@@ -124,7 +217,7 @@ async function run () {
124
217
  process.exit(1)
125
218
  } else {
126
219
  // const commandArgs = process.argv.slice(commandIndex + 1)
127
- await helpers.executeCommand(commandArgs, process.env)
220
+ await executeCommand(commandArgs, process.env)
128
221
  }
129
222
  }
130
223
 
@@ -104,6 +104,16 @@ program.command('ls')
104
104
  .option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
105
105
  .action(require('./actions/ls'))
106
106
 
107
+ // dotenvx get
108
+ program.command('get')
109
+ .description('Return environment variable(s)')
110
+ .argument('[key]', 'environment variable name')
111
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
112
+ .option('-o, --overload', 'override existing env variables')
113
+ .option('-a, --all', 'include all machine envs as well')
114
+ .option('-pp, --pretty-print', 'pretty print output')
115
+ .action(require('./actions/get'))
116
+
107
117
  // dotenvx hub
108
118
  program.addCommand(require('./commands/hub'))
109
119
 
@@ -1,18 +1,5 @@
1
- const fs = require('fs')
2
1
  const path = require('path')
3
- const execa = require('execa')
4
- const crypto = require('crypto')
5
- const { execSync } = require('child_process')
6
- const xxhash = require('xxhashjs')
7
-
8
- const XXHASH_SEED = 0xABCD
9
- const NONCE_BYTES = 12
10
-
11
- const main = require('./../lib/main')
12
- const logger = require('./../shared/logger')
13
-
14
- const RESERVED_ENV_FILES = ['.env.vault', '.env.projects', '.env.keys', '.env.me', '.env.x']
15
- const REPORT_ISSUE_LINK = 'https://github.com/dotenvx/dotenvx/issues/new'
2
+ const childProcess = require('child_process')
16
3
 
17
4
  const sleep = function (ms) {
18
5
  return new Promise(resolve => setTimeout(resolve, ms))
@@ -23,71 +10,6 @@ const resolvePath = function (filepath) {
23
10
  return path.resolve(process.cwd(), filepath)
24
11
  }
25
12
 
26
- const executeCommand = async function (commandArgs, env) {
27
- const signals = [
28
- 'SIGHUP', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
29
- 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'
30
- ]
31
-
32
- logger.debug(`executing process command [${commandArgs.join(' ')}]`)
33
-
34
- // handler for SIGINT
35
- let commandProcess
36
- const sigintHandler = () => {
37
- logger.debug('received SIGINT')
38
- logger.debug('checking command process')
39
- logger.debug(commandProcess)
40
-
41
- if (commandProcess) {
42
- logger.debug('sending SIGINT to command process')
43
- commandProcess.kill('SIGINT') // Send SIGINT to the command process
44
- } else {
45
- logger.debug('no command process to send SIGINT to')
46
- }
47
- }
48
-
49
- const handleOtherSignal = (signal) => {
50
- logger.debug(`received ${signal}`)
51
- }
52
-
53
- try {
54
- commandProcess = execa(commandArgs[0], commandArgs.slice(1), {
55
- stdio: 'inherit',
56
- env: { ...process.env, ...env }
57
- })
58
-
59
- process.on('SIGINT', sigintHandler)
60
-
61
- signals.forEach(signal => {
62
- process.on(signal, () => handleOtherSignal(signal))
63
- })
64
-
65
- // Wait for the command process to finish
66
- const { exitCode } = await commandProcess
67
-
68
- if (exitCode !== 0) {
69
- logger.debug(`received exitCode ${exitCode}`)
70
- throw new Error(`Command failed with exit code ${exitCode}`)
71
- }
72
- } catch (error) {
73
- if (error.signal !== 'SIGINT') {
74
- logger.error(error.message)
75
- logger.error(`command [${commandArgs.join(' ')}] failed`)
76
- logger.error('')
77
- logger.error(` try without dotenvx: [${commandArgs.join(' ')}]`)
78
- logger.error('')
79
- logger.error('if that succeeds, then dotenvx is the culprit. report issue:')
80
- logger.error(`<${REPORT_ISSUE_LINK}>`)
81
- }
82
-
83
- // Exit with the error code from the command process, or 1 if unavailable
84
- process.exit(error.exitCode || 1)
85
- } finally {
86
- // Clean up: Remove the SIGINT handler
87
- process.removeListener('SIGINT', sigintHandler)
88
- }
89
- }
90
-
91
13
  const pluralize = function (word, count) {
92
14
  // simple pluralization: add 's' at the end
93
15
  if (count === 0 || count > 1) {
@@ -97,133 +19,9 @@ const pluralize = function (word, count) {
97
19
  }
98
20
  }
99
21
 
100
- const findEnvFiles = function (directory) {
101
- const files = fs.readdirSync(directory)
102
-
103
- const envFiles = files.filter(file =>
104
- file.startsWith('.env') &&
105
- !file.endsWith('.previous') &&
106
- !RESERVED_ENV_FILES.includes(file)
107
- )
108
-
109
- return envFiles
110
- }
111
-
112
- const guessEnvironment = function (filepath) {
113
- const filename = path.basename(filepath)
114
- const parts = filename.split('.')
115
- const possibleEnvironment = parts[2] // ['', 'env', environment', 'previous']
116
-
117
- if (!possibleEnvironment || possibleEnvironment.length === 0) {
118
- return 'development'
119
- }
120
-
121
- return possibleEnvironment
122
- }
123
-
124
- const generateDotenvKey = function (environment) {
125
- const rand = crypto.randomBytes(32).toString('hex')
126
-
127
- return `dotenv://:key_${rand}@dotenvx.com/vault/.env.vault?environment=${environment.toLowerCase()}`
128
- }
129
-
130
- const encryptFile = function (filepath, dotenvKey, encoding) {
131
- const key = _parseEncryptionKeyFromDotenvKey(dotenvKey)
132
- const message = fs.readFileSync(filepath, encoding)
133
-
134
- const ciphertext = encrypt(key, message)
135
-
136
- return ciphertext
137
- }
138
-
139
- const encrypt = function (key, message) {
140
- // set up nonce
141
- const nonce = crypto.randomBytes(NONCE_BYTES)
142
-
143
- // set up cipher
144
- const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce)
145
-
146
- // generate ciphertext
147
- let ciphertext = ''
148
- ciphertext += cipher.update(message, 'utf8', 'hex')
149
- ciphertext += cipher.final('hex')
150
- ciphertext += cipher.getAuthTag().toString('hex')
151
-
152
- // prepend nonce
153
- ciphertext = nonce.toString('hex') + ciphertext
154
-
155
- // base64 encode output
156
- return Buffer.from(ciphertext, 'hex').toString('base64')
157
- }
158
-
159
- const changed = function (ciphertext, dotenvKey, filepath, encoding) {
160
- const key = _parseEncryptionKeyFromDotenvKey(dotenvKey)
161
- const decrypted = main.decrypt(ciphertext, key)
162
- const raw = fs.readFileSync(filepath, encoding)
163
-
164
- return hash(decrypted) !== hash(raw)
165
- }
166
-
167
- const hash = function (str) {
168
- return xxhash.h32(str, XXHASH_SEED).toString(16)
169
- }
170
-
171
- const _parseEncryptionKeyFromDotenvKey = function (dotenvKey) {
172
- // Parse DOTENV_KEY. Format is a URI
173
- let uri
174
- try {
175
- uri = new URL(dotenvKey)
176
- } catch (e) {
177
- throw new Error(`INVALID_DOTENV_KEY: ${e.message}`)
178
- }
179
-
180
- // Get decrypt key
181
- const key = uri.password
182
- if (!key) {
183
- throw new Error('INVALID_DOTENV_KEY: Missing key part')
184
- }
185
-
186
- return Buffer.from(key.slice(-64), 'hex')
187
- }
188
-
189
- const _parseCipherTextFromDotenvKeyAndParsedVault = function (dotenvKey, parsedVault) {
190
- // Parse DOTENV_KEY. Format is a URI
191
- let uri
192
- try {
193
- uri = new URL(dotenvKey)
194
- } catch (e) {
195
- throw new Error(`INVALID_DOTENV_KEY: ${e.message}`)
196
- }
197
-
198
- // Get environment
199
- const environment = uri.searchParams.get('environment')
200
- if (!environment) {
201
- throw new Error('INVALID_DOTENV_KEY: Missing environment part')
202
- }
203
-
204
- // Get ciphertext payload
205
- const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
206
- const ciphertext = parsedVault[environmentKey] // DOTENV_VAULT_PRODUCTION
207
- if (!ciphertext) {
208
- throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: cannot locate environment ${environmentKey} in your .env.vault file`)
209
- }
210
-
211
- return ciphertext
212
- }
213
-
214
- const formatCode = function (str) {
215
- const parts = []
216
-
217
- for (let i = 0; i < str.length; i += 4) {
218
- parts.push(str.substring(i, i + 4))
219
- }
220
-
221
- return parts.join('-')
222
- }
223
-
224
22
  const getRemoteOriginUrl = function () {
225
23
  try {
226
- const url = execSync('git remote get-url origin 2> /dev/null').toString().trim()
24
+ const url = childProcess.execSync('git remote get-url origin 2> /dev/null').toString().trim()
227
25
  return url
228
26
  } catch (_error) {
229
27
  return null
@@ -242,18 +40,7 @@ const extractUsernameName = function (url) {
242
40
  module.exports = {
243
41
  sleep,
244
42
  resolvePath,
245
- executeCommand,
246
43
  pluralize,
247
- findEnvFiles,
248
- guessEnvironment,
249
- generateDotenvKey,
250
- encryptFile,
251
- encrypt,
252
- changed,
253
- hash,
254
- formatCode,
255
44
  getRemoteOriginUrl,
256
- extractUsernameName,
257
- _parseEncryptionKeyFromDotenvKey,
258
- _parseCipherTextFromDotenvKeyAndParsedVault
45
+ extractUsernameName
259
46
  }
@@ -0,0 +1,19 @@
1
+ function parseEncryptionKeyFromDotenvKey (dotenvKey) {
2
+ // Parse DOTENV_KEY. Format is a URI
3
+ let uri
4
+ try {
5
+ uri = new URL(dotenvKey)
6
+ } catch (e) {
7
+ throw new Error(`INVALID_DOTENV_KEY: ${e.message}`)
8
+ }
9
+
10
+ // Get decrypt key
11
+ const key = uri.password
12
+ if (!key) {
13
+ throw new Error('INVALID_DOTENV_KEY: Missing key part')
14
+ }
15
+
16
+ return Buffer.from(key.slice(-64), 'hex')
17
+ }
18
+
19
+ module.exports = parseEncryptionKeyFromDotenvKey
package/src/lib/main.js CHANGED
@@ -5,6 +5,7 @@ const dotenvExpand = require('dotenv-expand')
5
5
  // services
6
6
  const Encrypt = require('./services/encrypt')
7
7
  const Ls = require('./services/ls')
8
+ const Get = require('./services/get')
8
9
 
9
10
  const config = function (options) {
10
11
  return dotenv.config(options)
@@ -114,6 +115,10 @@ const ls = function (directory, envFile) {
114
115
  return new Ls(directory, envFile).run()
115
116
  }
116
117
 
118
+ const get = function (key, envFile, overload, all) {
119
+ return new Get(key, envFile, overload, all).run()
120
+ }
121
+
117
122
  module.exports = {
118
123
  config,
119
124
  configDotenv,
@@ -121,6 +126,7 @@ module.exports = {
121
126
  parse,
122
127
  parseExpand,
123
128
  inject,
129
+ encrypt,
124
130
  ls,
125
- encrypt
131
+ get
126
132
  }
@@ -6,11 +6,12 @@ const DotenvKeys = require('./../helpers/dotenvKeys')
6
6
  const DotenvVault = require('./../helpers/dotenvVault')
7
7
 
8
8
  const ENCODING = 'utf8'
9
+ const RESERVED_ENV_FILES = ['.env.vault', '.env.project', '.env.keys', '.env.me', '.env.x']
9
10
 
10
11
  class Encrypt {
11
- constructor (directory = '.', envFile = '.env') {
12
+ constructor (directory = '.', envFile) {
12
13
  this.directory = directory
13
- this.envFile = envFile
14
+ this.envFile = envFile || this._findEnvFiles()
14
15
  // calculated
15
16
  this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
16
17
  this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
@@ -83,7 +84,8 @@ class Encrypt {
83
84
  dotenvVaultFile,
84
85
  addedVaults,
85
86
  existingVaults,
86
- addedDotenvFilenames
87
+ addedDotenvFilenames,
88
+ envFile: this.envFile
87
89
  }
88
90
  }
89
91
 
@@ -110,6 +112,17 @@ class Encrypt {
110
112
 
111
113
  return dotenv.configDotenv(options).parsed
112
114
  }
115
+
116
+ _findEnvFiles () {
117
+ const files = fs.readdirSync(this.directory)
118
+ const envFiles = files.filter(file =>
119
+ file.startsWith('.env') &&
120
+ !file.endsWith('.previous') &&
121
+ !RESERVED_ENV_FILES.includes(file)
122
+ )
123
+
124
+ return envFiles
125
+ }
113
126
  }
114
127
 
115
128
  module.exports = Encrypt
@@ -0,0 +1,47 @@
1
+ const dotenv = require('dotenv')
2
+ const dotenvExpand = require('dotenv-expand')
3
+
4
+ class Get {
5
+ constructor (key, envFile = '.env', overload = false, all = false) {
6
+ this.key = key
7
+ this.envFile = envFile
8
+ this.overload = overload
9
+ this.all = all
10
+ }
11
+
12
+ run () {
13
+ const clonedEnv = { ...process.env }
14
+ const options = {
15
+ processEnv: clonedEnv,
16
+ path: this.envFile,
17
+ override: this.overload
18
+ }
19
+ const parsed = dotenv.config(options).parsed
20
+
21
+ const expandedEnv = { ...clonedEnv }
22
+ const expandOptions = {
23
+ processEnv: expandedEnv,
24
+ parsed
25
+ }
26
+ dotenvExpand.expand(expandOptions)
27
+
28
+ if (!this.key) {
29
+ // if user wants to return ALL envs (even prior set on machine)
30
+ if (this.all) {
31
+ return expandedEnv
32
+ }
33
+
34
+ // typical scenario - return only envs that were identified in the .env file
35
+ const result = {}
36
+ for (const key of Object.keys(parsed)) {
37
+ result[key] = expandedEnv[key]
38
+ }
39
+
40
+ return result
41
+ }
42
+
43
+ return expandedEnv[this.key]
44
+ }
45
+ }
46
+
47
+ module.exports = Get