@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
@@ -1,47 +1,30 @@
1
1
  const { logger } = require('./../../../../shared/logger')
2
2
 
3
- function migrate (directory) {
4
- // debug args
5
- logger.debug(`directory: ${directory}`)
6
-
3
+ function migrate () {
7
4
  const options = this.opts()
8
5
  logger.debug(`options: ${JSON.stringify(options)}`)
9
6
 
10
- try {
11
- logger.help2('To migrate your .env.vault file to encrypted .env file(s):')
12
- logger.help('')
13
- logger.help(' 1. Run [dotenvx ext vault decrypt]')
14
- logger.help(' 2. Run [ls -a .env*]')
15
- logger.help('')
16
- logger.help2('Lastly, encrypt each .env(.environment) file:')
17
- logger.help('')
18
- logger.help(' 3. Run [dotenvx encrypt -f .env.production]')
19
- logger.help2('')
20
- logger.help2('For example:')
21
- logger.help2('')
22
- logger.help2(' $ dotenvx encrypt -f .env')
23
- logger.help2(' $ dotenvx encrypt -f .env.ci')
24
- logger.help2(' $ dotenvx encrypt -f .env.production')
25
- logger.help2('')
26
- logger.help2('Afterward:')
27
- logger.help2('')
28
- logger.help2('Update production with your new DOTENV_PRIVATE_KEY_PRODUCTION located in .env.keys')
29
- logger.help2('')
30
- logger.success('Learn more at [https://dotenvx.com/docs/quickstart#add-encryption]')
31
- logger.help2('')
32
- } catch (error) {
33
- logger.error(error.message)
34
- if (error.help) {
35
- logger.help(error.help)
36
- }
37
- if (error.debug) {
38
- logger.debug(error.debug)
39
- }
40
- if (error.code) {
41
- logger.debug(`ERROR_CODE: ${error.code}`)
42
- }
43
- process.exit(1)
44
- }
7
+ logger.help2('To migrate your .env.vault file to encrypted .env file(s):')
8
+ logger.help('')
9
+ logger.help(' 1. Run [dotenvx ext vault decrypt]')
10
+ logger.help(' 2. Run [ls -a .env*]')
11
+ logger.help('')
12
+ logger.help2('Lastly, encrypt each .env(.environment) file:')
13
+ logger.help('')
14
+ logger.help(' 3. Run [dotenvx encrypt -f .env.production]')
15
+ logger.help2('')
16
+ logger.help2('For example:')
17
+ logger.help2('')
18
+ logger.help2(' $ dotenvx encrypt -f .env')
19
+ logger.help2(' $ dotenvx encrypt -f .env.ci')
20
+ logger.help2(' $ dotenvx encrypt -f .env.production')
21
+ logger.help2('')
22
+ logger.help2('Afterward:')
23
+ logger.help2('')
24
+ logger.help2('Update production with your new DOTENV_PRIVATE_KEY_PRODUCTION located in .env.keys')
25
+ logger.help2('')
26
+ logger.success('Learn more at [https://dotenvx.com/docs/quickstart#add-encryption]')
27
+ logger.help2('')
45
28
  }
46
29
 
47
30
  module.exports = migrate
@@ -21,17 +21,18 @@ function get (key) {
21
21
  const value = main.get(key, envs, options.overload, process.env.DOTENV_KEY, options.all)
22
22
 
23
23
  if (typeof value === 'object' && value !== null) {
24
+ let space = 0
24
25
  if (options.prettyPrint) {
25
- logger.blank0(JSON.stringify(value, null, 2))
26
- } else {
27
- logger.blank0(value)
26
+ space = 2
28
27
  }
28
+
29
+ process.stdout.write(JSON.stringify(value, null, space))
29
30
  } else {
30
31
  if (value === undefined) {
31
- logger.blank0('')
32
+ process.stdout.write('')
32
33
  process.exit(1)
33
34
  } else {
34
- logger.blank0(value)
35
+ process.stdout.write(value)
35
36
  }
36
37
  }
37
38
  }
@@ -1,118 +1,11 @@
1
1
  const path = require('path')
2
- const execa = require('execa')
3
- const which = require('which')
4
2
  const { logger } = require('./../../shared/logger')
5
3
 
4
+ const executeCommand = require('./../../lib/helpers/executeCommand')
6
5
  const Run = require('./../../lib/services/run')
7
6
 
8
7
  const conventions = require('./../../lib/helpers/conventions')
9
8
 
10
- const executeCommand = async function (commandArgs, env) {
11
- const signals = [
12
- 'SIGHUP', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
13
- 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2'
14
- ]
15
-
16
- logger.debug(`executing process command [${commandArgs.join(' ')}]`)
17
-
18
- // handler for SIGINT
19
- let commandProcess
20
- const sigintHandler = () => {
21
- logger.debug('received SIGINT')
22
- logger.debug('checking command process')
23
- logger.debug(commandProcess)
24
-
25
- if (commandProcess) {
26
- logger.debug('sending SIGINT to command process')
27
- commandProcess.kill('SIGINT') // Send SIGINT to the command process
28
- } else {
29
- logger.debug('no command process to send SIGINT to')
30
- }
31
- }
32
- // handler for SIGTERM
33
- const sigtermHandler = () => {
34
- logger.debug('received SIGTERM')
35
- logger.debug('checking command process')
36
- logger.debug(commandProcess)
37
-
38
- if (commandProcess) {
39
- logger.debug('sending SIGTERM to command process')
40
- commandProcess.kill('SIGTERM') // Send SIGTEM to the command process
41
- } else {
42
- logger.debug('no command process to send SIGTERM to')
43
- }
44
- }
45
-
46
- const handleOtherSignal = (signal) => {
47
- logger.debug(`received ${signal}`)
48
- }
49
-
50
- try {
51
- // ensure the first command is expanded
52
- try {
53
- commandArgs[0] = path.resolve(which.sync(`${commandArgs[0]}`))
54
- logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
55
- } catch (e) {
56
- logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
57
- }
58
-
59
- // expand any other commands that follow a --
60
- let expandNext = false
61
- for (let i = 0; i < commandArgs.length; i++) {
62
- if (commandArgs[i] === '--') {
63
- expandNext = true
64
- } else if (expandNext) {
65
- try {
66
- commandArgs[i] = path.resolve(which.sync(`${commandArgs[i]}`))
67
- logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
68
- } catch (e) {
69
- logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
70
- }
71
- expandNext = false
72
- }
73
- }
74
-
75
- commandProcess = execa(commandArgs[0], commandArgs.slice(1), {
76
- stdio: 'inherit',
77
- env: { ...process.env, ...env }
78
- })
79
-
80
- process.on('SIGINT', sigintHandler)
81
- process.on('SIGTERM', sigtermHandler)
82
-
83
- signals.forEach(signal => {
84
- process.on(signal, () => handleOtherSignal(signal))
85
- })
86
-
87
- // Wait for the command process to finish
88
- const { exitCode } = await commandProcess
89
-
90
- if (exitCode !== 0) {
91
- logger.debug(`received exitCode ${exitCode}`)
92
- throw new Error(`Command exited with exit code ${exitCode}`)
93
- }
94
- } catch (error) {
95
- // no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
96
- if (error.signal !== 'SIGINT' && error.signal !== 'SIGTERM') {
97
- if (error.code === 'ENOENT') {
98
- logger.errornocolor(`Unknown command: ${error.command}`)
99
- } else if (error.message.includes('Command failed with exit code 1')) {
100
- logger.errornocolor(`Command exited with exit code 1: ${error.command}`)
101
- } else {
102
- logger.errornocolor(error.message)
103
- }
104
- }
105
-
106
- // Exit with the error code from the command process, or 1 if unavailable
107
- process.exit(error.exitCode || 1)
108
- } finally {
109
- // Clean up: Remove the SIGINT handler
110
- process.removeListener('SIGINT', sigintHandler)
111
- // Clean up: Remove the SIGTERM handler
112
- process.removeListener('SIGTERM', sigtermHandler)
113
- }
114
- }
115
-
116
9
  async function run () {
117
10
  const commandArgs = this.args
118
11
  logger.debug(`process command [${commandArgs.join(' ')}]`)
@@ -160,11 +53,11 @@ async function run () {
160
53
  if (processedEnv.error.code === 'MISSING_ENV_FILE') {
161
54
  // do not warn for conventions (too noisy)
162
55
  if (!options.convention) {
163
- logger.warnv(processedEnv.error)
56
+ logger.warnv(processedEnv.error.message)
164
57
  logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.filepath}] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
165
58
  }
166
59
  } else {
167
- logger.warnv(processedEnv.error)
60
+ logger.warnv(processedEnv.error.message)
168
61
  }
169
62
  } else {
170
63
  // debug parsed
@@ -38,10 +38,10 @@ function set (key, value) {
38
38
 
39
39
  if (processedEnvFile.error) {
40
40
  if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
41
- logger.warn(processedEnvFile.error)
41
+ logger.warn(processedEnvFile.error.message)
42
42
  logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx set]`)
43
43
  } else {
44
- logger.warn(processedEnvFile.error)
44
+ logger.warn(processedEnvFile.error.message)
45
45
  }
46
46
  } else {
47
47
  fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
@@ -1,11 +1,23 @@
1
1
  const { Command } = require('commander')
2
2
 
3
3
  const examples = require('./../examples')
4
+ const executeExtension = require('../../lib/helpers/executeExtension')
4
5
 
5
6
  const ext = new Command('ext')
6
7
 
7
8
  ext
8
9
  .description('🔌 extensions')
10
+ .allowUnknownOption()
11
+
12
+ ext.addHelpText('after', ' hub 🚫 DEPRECATED: to be replaced by [dotenvx pro]')
13
+
14
+ ext
15
+ .argument('[command]', 'dynamic ext command')
16
+ .argument('[args...]', 'dynamic ext command arguments')
17
+ .action((command, args, cmdObj) => {
18
+ const rawArgs = process.argv.slice(3) // adjust the index based on where actual args start
19
+ executeExtension(ext, command, rawArgs)
20
+ })
9
21
 
10
22
  // dotenvx ext ls
11
23
  ext.command('ls')
@@ -53,6 +65,5 @@ ext.command('settings')
53
65
  .action(require('./../actions/ext/settings'))
54
66
 
55
67
  ext.addCommand(require('./../commands/ext/vault'))
56
- ext.addCommand(require('./../commands/ext/hub'))
57
68
 
58
69
  module.exports = ext
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /* c8 ignore start */
3
4
  const fs = require('fs')
4
5
  const path = require('path')
5
6
  const { execSync } = require('child_process')
@@ -72,14 +73,17 @@ program.command('get')
72
73
  })
73
74
 
74
75
  // dotenvx set
76
+ const setAction = require('./actions/set')
75
77
  program.command('set')
76
78
  .description('set a single environment variable')
79
+ .addHelpText('after', examples.set)
80
+ .allowUnknownOption()
77
81
  .argument('KEY', 'KEY')
78
82
  .argument('value', 'value')
79
83
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
80
84
  .option('-c, --encrypt', 'encrypt value (default: true)', true)
81
85
  .option('-p, --plain', 'store value as plain text', false)
82
- .action(require('./actions/set'))
86
+ .action(setAction)
83
87
 
84
88
  // dotenvx encrypt
85
89
  const encryptAction = require('./actions/encrypt')
@@ -87,8 +91,18 @@ program.command('encrypt')
87
91
  .description('convert .env file(s) to encrypted .env file(s)')
88
92
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
89
93
  .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
94
+ .option('--stdout', 'send to stdout')
90
95
  .action(encryptAction)
91
96
 
97
+ // dotenvx decrypt
98
+ const decryptAction = require('./actions/decrypt')
99
+ program.command('decrypt')
100
+ .description('convert encrypted .env file(s) to plain .env file(s)')
101
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
102
+ .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
103
+ .option('--stdout', 'send to stdout')
104
+ .action(decryptAction)
105
+
92
106
  // dotenvx pro
93
107
  program.command('pro')
94
108
  .description('🏆 pro')
@@ -113,20 +127,6 @@ program.command('pro')
113
127
  // dotenvx ext
114
128
  program.addCommand(require('./commands/ext'))
115
129
 
116
- //
117
- // DEPRECATED AND hidden
118
- //
119
- program.addCommand(require('./commands/vault'))
120
-
121
- program.command('convert')
122
- .description('DEPRECATED: moved to [dotenvx encrypt]')
123
- .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
124
- .action(function (...args) {
125
- logger.warn('DEPRECATION NOTICE: [dotenvx convert] has moved to [dotenvx encrypt]')
126
-
127
- encryptAction.apply(this, args)
128
- })
129
-
130
130
  const lsAction = require('./actions/ext/ls')
131
131
  program.command('ls')
132
132
  .description('DEPRECATED: moved to [dotenvx ext ls]')
@@ -203,5 +203,6 @@ program.helpInformation = function () {
203
203
 
204
204
  return filteredLines.join('\n')
205
205
  }
206
+ /* c8 ignore stop */
206
207
 
207
208
  program.parse(process.argv)
@@ -100,10 +100,26 @@ Try it:
100
100
  `
101
101
  }
102
102
 
103
+ const set = function () {
104
+ return `
105
+ Examples:
106
+
107
+ \`\`\`
108
+ $ dotenvx set KEY value
109
+ $ dotenvx set KEY "value with spaces"
110
+ $ dotenvx set KEY -- "---value with a dash---"
111
+ $ dotenvx set KEY -- "-----BEGIN OPENSSH PRIVATE KEY-----
112
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
113
+ -----END OPENSSH PRIVATE KEY-----"
114
+ \`\`\`
115
+ `
116
+ }
117
+
103
118
  module.exports = {
104
119
  run,
105
120
  vaultEncrypt,
106
121
  precommit,
107
122
  prebuild,
108
- gitignore
123
+ gitignore,
124
+ set
109
125
  }
@@ -0,0 +1,9 @@
1
+ const execa = require('execa')
2
+
3
+ const execute = {
4
+ execa (command, args, options) {
5
+ return execa(command, args, options)
6
+ }
7
+ }
8
+
9
+ module.exports = execute
@@ -0,0 +1,117 @@
1
+ const path = require('path')
2
+ const which = require('which')
3
+ const execute = require('./../../lib/helpers/execute')
4
+ const { logger } = require('./../../shared/logger')
5
+
6
+ async function executeCommand (commandArgs, env) {
7
+ const signals = [
8
+ 'SIGHUP', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
9
+ 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2'
10
+ ]
11
+
12
+ logger.debug(`executing process command [${commandArgs.join(' ')}]`)
13
+
14
+ // handler for SIGINT
15
+ let commandProcess
16
+ const sigintHandler = () => {
17
+ logger.debug('received SIGINT')
18
+ logger.debug('checking command process')
19
+ logger.debug(commandProcess)
20
+
21
+ if (commandProcess) {
22
+ logger.debug('sending SIGINT to command process')
23
+ commandProcess.kill('SIGINT') // Send SIGINT to the command process
24
+ /* c8 ignore start */
25
+ } else {
26
+ logger.debug('no command process to send SIGINT to')
27
+ }
28
+ /* c8 ignore stop */
29
+ }
30
+ // handler for SIGTERM
31
+
32
+ /* c8 ignore start */
33
+ const sigtermHandler = () => {
34
+ logger.debug('received SIGTERM')
35
+ logger.debug('checking command process')
36
+ logger.debug(commandProcess)
37
+
38
+ if (commandProcess) {
39
+ logger.debug('sending SIGTERM to command process')
40
+ commandProcess.kill('SIGTERM') // Send SIGTEM to the command process
41
+ } else {
42
+ logger.debug('no command process to send SIGTERM to')
43
+ }
44
+ }
45
+
46
+ const handleOtherSignal = (signal) => {
47
+ logger.debug(`received ${signal}`)
48
+ }
49
+ /* c8 ignore stop */
50
+
51
+ try {
52
+ // ensure the first command is expanded
53
+ try {
54
+ commandArgs[0] = path.resolve(which.sync(`${commandArgs[0]}`))
55
+ logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
56
+ } catch (e) {
57
+ logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
58
+ }
59
+
60
+ // expand any other commands that follow a --
61
+ let expandNext = false
62
+ for (let i = 0; i < commandArgs.length; i++) {
63
+ if (commandArgs[i] === '--') {
64
+ expandNext = true
65
+ } else if (expandNext) {
66
+ try {
67
+ commandArgs[i] = path.resolve(which.sync(`${commandArgs[i]}`))
68
+ logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
69
+ } catch (e) {
70
+ logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
71
+ }
72
+ expandNext = false
73
+ }
74
+ }
75
+
76
+ commandProcess = execute.execa(commandArgs[0], commandArgs.slice(1), {
77
+ stdio: 'inherit',
78
+ env: { ...process.env, ...env }
79
+ })
80
+
81
+ process.on('SIGINT', sigintHandler)
82
+ process.on('SIGTERM', sigtermHandler)
83
+
84
+ signals.forEach(signal => {
85
+ process.on(signal, () => handleOtherSignal(signal))
86
+ })
87
+
88
+ // Wait for the command process to finish
89
+ const { exitCode } = await commandProcess
90
+
91
+ if (exitCode !== 0) {
92
+ logger.debug(`received exitCode ${exitCode}`)
93
+ throw new Error(`Command exited with exit code ${exitCode}`)
94
+ }
95
+ } catch (error) {
96
+ // no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
97
+ if (error.signal !== 'SIGINT' && error.signal !== 'SIGTERM') {
98
+ if (error.code === 'ENOENT') {
99
+ logger.errornocolor(`Unknown command: ${error.command}`)
100
+ } else if (error.message.includes('Command failed with exit code 1')) {
101
+ logger.errornocolor(`Command exited with exit code 1: ${error.command}`)
102
+ } else {
103
+ logger.errornocolor(error.message)
104
+ }
105
+ }
106
+
107
+ // Exit with the error code from the command process, or 1 if unavailable
108
+ process.exit(error.exitCode || 1)
109
+ } finally {
110
+ // Clean up: Remove the SIGINT handler
111
+ process.removeListener('SIGINT', sigintHandler)
112
+ // Clean up: Remove the SIGTERM handler
113
+ process.removeListener('SIGTERM', sigtermHandler)
114
+ }
115
+ }
116
+
117
+ module.exports = executeCommand
@@ -0,0 +1,38 @@
1
+ const path = require('path')
2
+ const childProcess = require('child_process')
3
+ const { logger } = require('../../shared/logger')
4
+
5
+ function executeExtension (ext, command, rawArgs) {
6
+ if (!command) {
7
+ ext.outputHelp()
8
+ process.exit(1)
9
+ return
10
+ }
11
+
12
+ // construct the full command line manually including flags
13
+ const commandIndex = rawArgs.indexOf(command)
14
+ const forwardedArgs = rawArgs.slice(commandIndex + 1)
15
+
16
+ logger.debug(`command: ${command}`)
17
+ logger.debug(`args: ${JSON.stringify(forwardedArgs)}`)
18
+
19
+ const binPath = path.join(process.cwd(), 'node_modules', '.bin')
20
+ const newPath = `${binPath}:${process.env.PATH}`
21
+ const env = { ...process.env, PATH: newPath }
22
+
23
+ const result = childProcess.spawnSync(`dotenvx-ext-${command}`, forwardedArgs, { stdio: 'inherit', env })
24
+ if (result.error) {
25
+ if (command === 'hub') {
26
+ logger.warn(`[INSTALLATION_NEEDED] install dotenvx-ext-${command} to use [dotenvx ext ${command}] commands`)
27
+ logger.help('? see installation instructions [https://github.com/dotenvx/dotenvx-ext-hub]')
28
+ } else {
29
+ logger.info(`error: unknown command '${command}'`)
30
+ }
31
+ }
32
+
33
+ if (result.status !== 0) {
34
+ process.exit(result.status)
35
+ }
36
+ }
37
+
38
+ module.exports = executeExtension
@@ -24,20 +24,23 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
24
24
  // parsed
25
25
  const envParsed = dotenv.parse(envSrc)
26
26
  const keysParsed = dotenv.parse(keysSrc)
27
+ const existingPublicKey = envParsed[publicKeyName]
28
+ const existingPrivateKey = keysParsed[privateKeyName]
27
29
 
28
30
  // if DOTENV_PUBLIC_KEY_${environment} already present then go no further
29
- if (envParsed[publicKeyName] && envParsed[publicKeyName].length > 0) {
31
+ if (existingPublicKey && existingPublicKey.length > 0) {
30
32
  return {
31
33
  envSrc,
32
34
  keysSrc,
33
- publicKey: envParsed[publicKeyName],
34
- privateKey: keysParsed[privateKeyName],
35
+ publicKey: existingPublicKey,
36
+ privateKey: existingPrivateKey,
37
+ publicKeyAdded: false,
35
38
  privateKeyAdded: false
36
39
  }
37
40
  }
38
41
 
39
42
  // generate key pair
40
- const { publicKey, privateKey } = keyPair()
43
+ const { publicKey, privateKey } = keyPair(existingPrivateKey)
41
44
 
42
45
  // publicKey
43
46
  const prependPublicKey = [
@@ -65,17 +68,20 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
65
68
 
66
69
  envSrc = `${prependPublicKey}\n${envSrc}`
67
70
  keysSrc = keysSrc.length > 1 ? keysSrc : `${firstTimeKeysSrc}\n`
68
- keysSrc = `${keysSrc}\n${appendPrivateKey}`
69
71
 
70
- fs.writeFileSync(envFilepath, envSrc)
71
- fs.writeFileSync(envKeysFilepath, keysSrc)
72
+ let privateKeyAdded = false
73
+ if (!existingPrivateKey) {
74
+ keysSrc = `${keysSrc}\n${appendPrivateKey}`
75
+ privateKeyAdded = true
76
+ }
72
77
 
73
78
  return {
74
79
  envSrc,
75
80
  keysSrc,
76
81
  publicKey,
77
82
  privateKey,
78
- privateKeyAdded: true
83
+ publicKeyAdded: true,
84
+ privateKeyAdded
79
85
  }
80
86
  }
81
87
 
@@ -1,8 +1,7 @@
1
1
  const ENCRYPTION_PATTERN = /^encrypted:.+/
2
- const PUBLIC_KEY_PATTERN = /^DOTENV_PUBLIC_KEY/
3
2
 
4
3
  function isEncrypted (key, value) {
5
- return PUBLIC_KEY_PATTERN.test(key) || ENCRYPTION_PATTERN.test(value)
4
+ return ENCRYPTION_PATTERN.test(value)
6
5
  }
7
6
 
8
7
  module.exports = isEncrypted
@@ -1,12 +1,13 @@
1
1
  const dotenv = require('dotenv')
2
2
 
3
3
  const isEncrypted = require('./isEncrypted')
4
+ const isPublicKey = require('./isPublicKey')
4
5
 
5
6
  function isFullyEncrypted (src) {
6
7
  const parsed = dotenv.parse(src)
7
8
 
8
9
  for (const [key, value] of Object.entries(parsed)) {
9
- const result = isEncrypted(key, value)
10
+ const result = isEncrypted(key, value) || isPublicKey(key, value)
10
11
  if (!result) {
11
12
  return false
12
13
  }
@@ -13,7 +13,7 @@ function isIgnoringDotenvKeys () {
13
13
  return false
14
14
  }
15
15
 
16
- return false
16
+ return true
17
17
  }
18
18
 
19
19
  module.exports = isIgnoringDotenvKeys
@@ -0,0 +1,7 @@
1
+ const PUBLIC_KEY_PATTERN = /^DOTENV_PUBLIC_KEY/
2
+
3
+ function isPublicKey (key, value) {
4
+ return PUBLIC_KEY_PATTERN.test(key)
5
+ }
6
+
7
+ module.exports = isPublicKey
@@ -1,7 +1,13 @@
1
1
  const { PrivateKey } = require('eciesjs')
2
2
 
3
- function keyPair () {
4
- const kp = new PrivateKey()
3
+ function keyPair (existingPrivateKey) {
4
+ let kp
5
+
6
+ if (existingPrivateKey) {
7
+ kp = new PrivateKey(Buffer.from(existingPrivateKey, 'hex'))
8
+ } else {
9
+ kp = new PrivateKey()
10
+ }
5
11
 
6
12
  const publicKey = kp.publicKey.toHex()
7
13
  const privateKey = kp.secret.toString('hex')