@dotenvx/dotenvx 1.5.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.
- package/CHANGELOG.md +18 -1
- package/README.md +88 -2
- package/package.json +9 -9
- package/src/cli/actions/decrypt.js +71 -0
- package/src/cli/actions/encrypt.js +54 -42
- package/src/cli/actions/ext/genexample.js +4 -12
- package/src/cli/actions/ext/gitignore.js +5 -0
- package/src/cli/actions/ext/prebuild.js +1 -0
- package/src/cli/actions/ext/scan.js +9 -6
- package/src/cli/actions/ext/settings.js +5 -4
- package/src/cli/actions/ext/vault/decrypt.js +6 -13
- package/src/cli/actions/ext/vault/encrypt.js +5 -12
- package/src/cli/actions/ext/vault/migrate.js +22 -39
- package/src/cli/actions/get.js +6 -5
- package/src/cli/actions/run.js +3 -110
- package/src/cli/actions/set.js +2 -2
- package/src/cli/commands/ext.js +2 -32
- package/src/cli/dotenvx.js +12 -14
- package/src/lib/helpers/execute.js +9 -0
- package/src/lib/helpers/executeCommand.js +117 -0
- package/src/lib/helpers/executeExtension.js +38 -0
- package/src/lib/helpers/findOrCreatePublicKey.js +14 -8
- package/src/lib/helpers/isEncrypted.js +1 -2
- package/src/lib/helpers/isFullyEncrypted.js +2 -1
- package/src/lib/helpers/isIgnoringDotenvKeys.js +1 -1
- package/src/lib/helpers/isPublicKey.js +7 -0
- package/src/lib/helpers/keyPair.js +8 -2
- package/src/lib/helpers/smartDotenvPrivateKey.js +64 -6
- package/src/lib/main.js +16 -9
- package/src/lib/services/decrypt.js +79 -98
- package/src/lib/services/encrypt.js +12 -7
- package/src/lib/services/sets.js +9 -2
- package/src/lib/services/status.js +2 -2
- package/src/lib/services/vaultDecrypt.js +126 -0
- package/src/shared/logger.js +0 -3
- package/src/cli/commands/vault.js +0 -57
- package/src/lib/helpers/clipboardy/fallbacks/linux/xsel +0 -0
- package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_i686.exe +0 -0
- package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_x86_64.exe +0 -0
- package/src/lib/helpers/clipboardy/linux.js +0 -57
- package/src/lib/helpers/clipboardy/macos.js +0 -14
- package/src/lib/helpers/clipboardy/termux.js +0 -41
- package/src/lib/helpers/clipboardy/windows.js +0 -16
- package/src/lib/helpers/clipboardy.js +0 -51
- package/src/lib/helpers/extractUsernameName.js +0 -10
- package/src/lib/helpers/forgivingDirectory.js +0 -11
- package/src/lib/helpers/gitRoot.js +0 -14
- package/src/lib/helpers/gitUrl.js +0 -13
- package/src/lib/helpers/isGitRepo.js +0 -13
- package/src/lib/helpers/isGithub.js +0 -5
- package/src/lib/helpers/resolvePath.js +0 -8
- package/src/shared/confirm.js +0 -12
- package/src/shared/createSpinner.js +0 -97
package/src/cli/actions/get.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
32
|
+
process.stdout.write('')
|
|
32
33
|
process.exit(1)
|
|
33
34
|
} else {
|
|
34
|
-
|
|
35
|
+
process.stdout.write(value)
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
}
|
package/src/cli/actions/run.js
CHANGED
|
@@ -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
|
package/src/cli/actions/set.js
CHANGED
|
@@ -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)
|
package/src/cli/commands/ext.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const { spawnSync } = require('child_process')
|
|
3
1
|
const { Command } = require('commander')
|
|
4
|
-
const { logger } = require('../../shared/logger')
|
|
5
2
|
|
|
6
3
|
const examples = require('./../examples')
|
|
4
|
+
const executeExtension = require('../../lib/helpers/executeExtension')
|
|
7
5
|
|
|
8
6
|
const ext = new Command('ext')
|
|
9
7
|
|
|
@@ -17,36 +15,8 @@ ext
|
|
|
17
15
|
.argument('[command]', 'dynamic ext command')
|
|
18
16
|
.argument('[args...]', 'dynamic ext command arguments')
|
|
19
17
|
.action((command, args, cmdObj) => {
|
|
20
|
-
if (!command) {
|
|
21
|
-
ext.outputHelp()
|
|
22
|
-
process.exit(1)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// construct the full command line manually including flags
|
|
26
18
|
const rawArgs = process.argv.slice(3) // adjust the index based on where actual args start
|
|
27
|
-
|
|
28
|
-
const forwardedArgs = rawArgs.slice(commandIndex + 1)
|
|
29
|
-
|
|
30
|
-
logger.debug(`command: ${command}`)
|
|
31
|
-
logger.debug(`args: ${JSON.stringify(forwardedArgs)}`)
|
|
32
|
-
|
|
33
|
-
const binPath = path.join(process.cwd(), 'node_modules', '.bin')
|
|
34
|
-
const newPath = `${binPath}:${process.env.PATH}`
|
|
35
|
-
const env = { ...process.env, PATH: newPath }
|
|
36
|
-
|
|
37
|
-
const result = spawnSync(`dotenvx-ext-${command}`, forwardedArgs, { stdio: 'inherit', env })
|
|
38
|
-
if (result.error) {
|
|
39
|
-
if (command === 'hub') {
|
|
40
|
-
logger.warn(`[INSTALLATION_NEEDED] install dotenvx-ext-${command} to use [dotenvx ext ${command}] commands`)
|
|
41
|
-
logger.help('? see installation instructions [https://github.com/dotenvx/dotenvx-ext-hub]')
|
|
42
|
-
} else {
|
|
43
|
-
logger.info(`error: unknown command '${command}'`)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (result.status !== 0) {
|
|
48
|
-
process.exit(result.status)
|
|
49
|
-
}
|
|
19
|
+
executeExtension(ext, command, rawArgs)
|
|
50
20
|
})
|
|
51
21
|
|
|
52
22
|
// dotenvx ext ls
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -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')
|
|
@@ -90,8 +91,18 @@ program.command('encrypt')
|
|
|
90
91
|
.description('convert .env file(s) to encrypted .env file(s)')
|
|
91
92
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
92
93
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
94
|
+
.option('--stdout', 'send to stdout')
|
|
93
95
|
.action(encryptAction)
|
|
94
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
|
+
|
|
95
106
|
// dotenvx pro
|
|
96
107
|
program.command('pro')
|
|
97
108
|
.description('🏆 pro')
|
|
@@ -116,20 +127,6 @@ program.command('pro')
|
|
|
116
127
|
// dotenvx ext
|
|
117
128
|
program.addCommand(require('./commands/ext'))
|
|
118
129
|
|
|
119
|
-
//
|
|
120
|
-
// DEPRECATED AND hidden
|
|
121
|
-
//
|
|
122
|
-
program.addCommand(require('./commands/vault'))
|
|
123
|
-
|
|
124
|
-
program.command('convert')
|
|
125
|
-
.description('DEPRECATED: moved to [dotenvx encrypt]')
|
|
126
|
-
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
127
|
-
.action(function (...args) {
|
|
128
|
-
logger.warn('DEPRECATION NOTICE: [dotenvx convert] has moved to [dotenvx encrypt]')
|
|
129
|
-
|
|
130
|
-
encryptAction.apply(this, args)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
130
|
const lsAction = require('./actions/ext/ls')
|
|
134
131
|
program.command('ls')
|
|
135
132
|
.description('DEPRECATED: moved to [dotenvx ext ls]')
|
|
@@ -206,5 +203,6 @@ program.helpInformation = function () {
|
|
|
206
203
|
|
|
207
204
|
return filteredLines.join('\n')
|
|
208
205
|
}
|
|
206
|
+
/* c8 ignore stop */
|
|
209
207
|
|
|
210
208
|
program.parse(process.argv)
|
|
@@ -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 (
|
|
31
|
+
if (existingPublicKey && existingPublicKey.length > 0) {
|
|
30
32
|
return {
|
|
31
33
|
envSrc,
|
|
32
34
|
keysSrc,
|
|
33
|
-
publicKey:
|
|
34
|
-
privateKey:
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
|
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
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
const { PrivateKey } = require('eciesjs')
|
|
2
2
|
|
|
3
|
-
function keyPair () {
|
|
4
|
-
|
|
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')
|
|
@@ -2,27 +2,85 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const dotenv = require('dotenv')
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const ENCODING = 'utf8'
|
|
6
|
+
const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
|
|
7
|
+
const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
const privateKeyName = guessPrivateKeyName(envFilepath) // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
|
|
9
|
+
const guessPrivateKeyName = require('./guessPrivateKeyName')
|
|
9
10
|
|
|
10
|
-
|
|
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
|
-
|
|
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
|
}
|