@dotenvx/dotenvx 1.75.1 → 2.1.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 +39 -1
- package/README.md +4 -114
- package/package.json +8 -7
- package/src/cli/actions/decrypt.js +35 -17
- package/src/cli/actions/doctor.js +2 -2
- package/src/cli/actions/encrypt.js +64 -85
- package/src/cli/actions/ext/genexample.js +2 -2
- package/src/cli/actions/get.js +22 -7
- package/src/cli/actions/keypair.js +55 -42
- package/src/cli/actions/login.js +4 -3
- package/src/cli/actions/normalizeArmorAliases.js +9 -0
- package/src/cli/actions/run.js +65 -22
- package/src/cli/actions/set.js +87 -37
- package/src/cli/dotenvx.js +8 -23
- package/src/lib/api/{postKeypair.js → postArmorKeyring.js} +4 -14
- package/src/lib/{helpers/envResolution → conventions}/environment.js +1 -1
- package/src/lib/conventions/filepaths.js +9 -0
- package/src/lib/conventions/keynames.js +38 -0
- package/src/lib/helpers/catchAndLog.js +4 -1
- package/src/lib/helpers/cryptography/index.js +2 -9
- package/src/lib/helpers/cryptography/{isPublicKey.js → isDotenvPublicKey.js} +2 -2
- package/src/lib/helpers/cryptography/isPlainKey.js +7 -0
- package/src/lib/helpers/cryptography/mutateKeysSrc.js +5 -21
- package/src/lib/helpers/envResolution/index.js +1 -1
- package/src/lib/helpers/errors.js +26 -0
- package/src/lib/helpers/keypairMetadata.js +1 -1
- package/src/lib/helpers/prompts.js +46 -0
- package/src/lib/main.d.ts +2 -39
- package/src/lib/main.js +83 -46
- package/src/lib/providers/armor/index.js +29 -0
- package/src/lib/providers/index.js +46 -0
- package/src/lib/providers/worker.js +6 -0
- package/src/lib/resolvers/envs.js +302 -0
- package/src/lib/resolvers/get.js +69 -0
- package/src/lib/resolvers/keypair.js +81 -0
- package/src/lib/resolvers/ls.js +41 -0
- package/src/lib/services/armorDown.js +2 -2
- package/src/lib/services/{armorKeypair.js → armorKeyring.js} +14 -51
- package/src/lib/services/armorMove.js +2 -2
- package/src/lib/services/armorPull.js +2 -2
- package/src/lib/services/armorPush.js +2 -2
- package/src/lib/services/armorUp.js +2 -2
- package/src/lib/services/genexample.js +5 -6
- package/src/lib/services/login.js +25 -0
- package/src/lib/services/prebuild.js +9 -3
- package/src/lib/services/precommit.js +9 -3
- package/src/lib/transforms/decrypt.js +83 -0
- package/src/lib/transforms/encrypt.js +178 -0
- package/src/lib/transforms/set.js +179 -0
- package/src/shared/logger.js +5 -0
- package/src/cli/actions/normalizeArmorOptions.js +0 -10
- package/src/cli/actions/rotate.js +0 -87
- package/src/lib/helpers/append.js +0 -62
- package/src/lib/helpers/cryptography/armorKeypair.js +0 -42
- package/src/lib/helpers/cryptography/armorKeypairSync.js +0 -55
- package/src/lib/helpers/cryptography/encryptValue.js +0 -7
- package/src/lib/helpers/cryptography/localKeypair.js +0 -14
- package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +0 -38
- package/src/lib/helpers/cryptography/provision.js +0 -56
- package/src/lib/helpers/cryptography/provisionSync.js +0 -51
- package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +0 -26
- package/src/lib/helpers/escapeDollarSigns.js +0 -5
- package/src/lib/helpers/escapeForRegex.js +0 -5
- package/src/lib/helpers/evalKeyValue.js +0 -23
- package/src/lib/helpers/isIgnoringDotenvKeys.js +0 -19
- package/src/lib/helpers/keyResolution/index.js +0 -16
- package/src/lib/helpers/keyResolution/keyNamesForEnvFile.js +0 -24
- package/src/lib/helpers/keyResolution/keyValues.js +0 -99
- package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +0 -85
- package/src/lib/helpers/keyResolution/keyValuesSync.js +0 -103
- package/src/lib/helpers/keyResolution/readFileKey.js +0 -19
- package/src/lib/helpers/keyResolution/readFileKeySync.js +0 -17
- package/src/lib/helpers/keyResolution/readProcessKey.js +0 -7
- package/src/lib/helpers/localDisplayPath.js +0 -11
- package/src/lib/helpers/parse.js +0 -214
- package/src/lib/helpers/pluralize.js +0 -10
- package/src/lib/helpers/quotes.js +0 -36
- package/src/lib/helpers/replace.js +0 -82
- package/src/lib/helpers/resolveEscapeSequences.js +0 -5
- package/src/lib/services/decrypt.js +0 -156
- package/src/lib/services/encrypt.js +0 -213
- package/src/lib/services/get.js +0 -76
- package/src/lib/services/keypair.js +0 -60
- package/src/lib/services/loginPoll.js +0 -36
- package/src/lib/services/ls.js +0 -57
- package/src/lib/services/rotate.js +0 -197
- package/src/lib/services/run.js +0 -255
- package/src/lib/services/sets.js +0 -328
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const { logger } = require('./../../shared/logger')
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const keypairResolver = require('./../../lib/resolvers/keypair')
|
|
4
|
+
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
4
5
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
5
|
-
const
|
|
6
|
-
const normalizeArmorOptions = require('./normalizeArmorOptions')
|
|
6
|
+
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
7
7
|
|
|
8
8
|
async function keypair (key) {
|
|
9
|
-
const options =
|
|
9
|
+
const options = normalizeArmorAliases(this.opts())
|
|
10
10
|
const spinner = await createSpinner({ ...options, text: 'retrieving' })
|
|
11
11
|
|
|
12
12
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
@@ -16,50 +16,63 @@ async function keypair (key) {
|
|
|
16
16
|
|
|
17
17
|
const prettyPrint = options.prettyPrint || options.pp
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
let inline = ''
|
|
31
|
-
for (const [keyName, value] of Object.entries(results)) {
|
|
32
|
-
inline += `${keyName}=${value || ''} `
|
|
19
|
+
try {
|
|
20
|
+
const keypairs = await keypairResolver({
|
|
21
|
+
envFile: options.envFile,
|
|
22
|
+
envKeysFile: options.envKeysFile,
|
|
23
|
+
armor: options.armor,
|
|
24
|
+
token: options.token,
|
|
25
|
+
command: process.argv.slice(2),
|
|
26
|
+
onStatus: (text) => {
|
|
27
|
+
if (spinner && text) {
|
|
28
|
+
spinner.text = text
|
|
29
|
+
}
|
|
33
30
|
}
|
|
34
|
-
|
|
31
|
+
})
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
} else if (options.format === 'colon') {
|
|
38
|
-
let inline = ''
|
|
39
|
-
for (const [keyName, value] of Object.entries(results)) {
|
|
40
|
-
inline += `${keyName}:${value || ''} `
|
|
41
|
-
}
|
|
42
|
-
inline = inline.trim()
|
|
33
|
+
const results = key ? keypairs[key] : keypairs
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
if (spinner) spinner.stop()
|
|
36
|
+
if (typeof results === 'object' && results !== null) {
|
|
37
|
+
if (options.format === 'shell') {
|
|
38
|
+
let inline = ''
|
|
39
|
+
for (const [keyName, value] of Object.entries(results)) {
|
|
40
|
+
inline += `${keyName}=${value || ''} `
|
|
41
|
+
}
|
|
42
|
+
inline = inline.trim()
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
console.log(inline)
|
|
45
|
+
} else if (options.format === 'colon') {
|
|
46
|
+
let inline = ''
|
|
47
|
+
for (const [keyName, value] of Object.entries(results)) {
|
|
48
|
+
inline += `${keyName}:${value || ''} `
|
|
49
|
+
}
|
|
50
|
+
inline = inline.trim()
|
|
51
|
+
|
|
52
|
+
console.log(inline)
|
|
53
|
+
// json format
|
|
54
|
+
} else {
|
|
55
|
+
let space = 0
|
|
56
|
+
if (prettyPrint) {
|
|
57
|
+
space = 2
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(JSON.stringify(results, null, space))
|
|
61
|
+
}
|
|
60
62
|
} else {
|
|
61
|
-
|
|
63
|
+
if (results === undefined) {
|
|
64
|
+
console.log('')
|
|
65
|
+
process.exit(1)
|
|
66
|
+
} else if (options.format === 'colon' && key) {
|
|
67
|
+
console.log(`${key}:${results}`)
|
|
68
|
+
} else {
|
|
69
|
+
console.log(results)
|
|
70
|
+
}
|
|
62
71
|
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (spinner) spinner.stop()
|
|
74
|
+
catchAndLog(error)
|
|
75
|
+
process.exit(1)
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
|
package/src/cli/actions/login.js
CHANGED
|
@@ -2,7 +2,6 @@ const { logger } = require('../../shared/logger')
|
|
|
2
2
|
|
|
3
3
|
const Session = require('../../db/session')
|
|
4
4
|
const Login = require('../../lib/services/login')
|
|
5
|
-
const LoginPoll = require('../../lib/services/loginPoll')
|
|
6
5
|
|
|
7
6
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
7
|
const formatCode = require('../../lib/helpers/formatCode')
|
|
@@ -20,15 +19,17 @@ async function login () {
|
|
|
20
19
|
const sesh = new Session()
|
|
21
20
|
const hostname = options.hostname || sesh.hostname()
|
|
22
21
|
let cleanupOpenKeyListener = () => {}
|
|
22
|
+
let loginService
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
|
+
loginService = new Login(hostname)
|
|
25
26
|
const {
|
|
26
27
|
deviceCode,
|
|
27
28
|
userCode,
|
|
28
29
|
verificationUri,
|
|
29
30
|
verificationUriComplete,
|
|
30
31
|
interval
|
|
31
|
-
} = await
|
|
32
|
+
} = await loginService.run()
|
|
32
33
|
|
|
33
34
|
const promptMessage = `◌ press Enter to open [${verificationUri}] and enter code [${formatCode(userCode)}]...`
|
|
34
35
|
|
|
@@ -36,7 +37,7 @@ async function login () {
|
|
|
36
37
|
logger.info(promptMessage)
|
|
37
38
|
|
|
38
39
|
// begin polling
|
|
39
|
-
const pollPromise =
|
|
40
|
+
const pollPromise = loginService.poll(deviceCode, interval)
|
|
40
41
|
cleanupOpenKeyListener = listenForOpenKey(() => openUrl(verificationUriComplete))
|
|
41
42
|
const data = await pollPromise
|
|
42
43
|
|
package/src/cli/actions/run.js
CHANGED
|
@@ -2,18 +2,56 @@ const path = require('path')
|
|
|
2
2
|
const { logger } = require('./../../shared/logger')
|
|
3
3
|
|
|
4
4
|
const executeCommand = require('./../../lib/helpers/executeCommand')
|
|
5
|
-
const
|
|
5
|
+
const envsResolver = require('./../../lib/resolvers/envs')
|
|
6
6
|
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
7
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
8
|
const Session = require('../../db/session')
|
|
9
|
-
const
|
|
9
|
+
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
10
|
|
|
11
11
|
const conventions = require('./../../lib/helpers/conventions')
|
|
12
12
|
const { determine } = require('./../../lib/helpers/envResolution')
|
|
13
13
|
|
|
14
|
+
function inferCommandArgsFromProcessArgv (argv) {
|
|
15
|
+
const runIndex = argv.indexOf('run')
|
|
16
|
+
if (runIndex === -1) return []
|
|
17
|
+
|
|
18
|
+
const args = argv.slice(runIndex + 1)
|
|
19
|
+
|
|
20
|
+
const separatorIndex = args.indexOf('--')
|
|
21
|
+
if (separatorIndex !== -1) return args.slice(separatorIndex + 1)
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
if (args[i] === '-f' || args[i] === '--env-file') {
|
|
25
|
+
i++
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (args[i].startsWith('-')) continue
|
|
30
|
+
|
|
31
|
+
return args.slice(i)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function uniqueInjectedKeys (processedEnvs) {
|
|
38
|
+
const result = new Set()
|
|
39
|
+
for (const processedEnv of processedEnvs) {
|
|
40
|
+
for (const key of Object.keys(processedEnv.injected || {})) {
|
|
41
|
+
result.add(key)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result
|
|
45
|
+
}
|
|
46
|
+
|
|
14
47
|
async function run () {
|
|
15
|
-
const options =
|
|
16
|
-
|
|
48
|
+
const options = normalizeArmorAliases(this.opts())
|
|
49
|
+
|
|
50
|
+
let commandArgs = this.args
|
|
51
|
+
if (commandArgs.length < 1) {
|
|
52
|
+
commandArgs = inferCommandArgsFromProcessArgv(process.argv)
|
|
53
|
+
}
|
|
54
|
+
|
|
17
55
|
const spinner = await createSpinner({ ...options, text: 'injecting' })
|
|
18
56
|
|
|
19
57
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
@@ -48,17 +86,24 @@ async function run () {
|
|
|
48
86
|
envs = this.envs
|
|
49
87
|
}
|
|
50
88
|
envs = determine(envs, process.env)
|
|
51
|
-
if (spinner) spinner.stop()
|
|
52
89
|
|
|
53
90
|
const {
|
|
54
91
|
processedEnvs,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
92
|
+
readableFilepaths
|
|
93
|
+
} = await envsResolver({
|
|
94
|
+
envs,
|
|
95
|
+
overload: options.overload,
|
|
96
|
+
processEnv: process.env,
|
|
97
|
+
envKeysFile: options.envKeysFile,
|
|
98
|
+
noArmor,
|
|
59
99
|
token: options.token,
|
|
60
|
-
command: commandArgs
|
|
61
|
-
|
|
100
|
+
command: commandArgs,
|
|
101
|
+
onStatus: (text) => {
|
|
102
|
+
if (spinner && text) {
|
|
103
|
+
spinner.text = text
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
62
107
|
|
|
63
108
|
for (const processedEnv of processedEnvs) {
|
|
64
109
|
if (processedEnv.type === 'envFile') {
|
|
@@ -93,27 +138,25 @@ async function run () {
|
|
|
93
138
|
logger.debug(`${key} set to ${value}`)
|
|
94
139
|
}
|
|
95
140
|
|
|
96
|
-
// verbose/debug
|
|
97
|
-
for (const [key, value] of Object.entries(processedEnv.
|
|
141
|
+
// verbose/debug existed key/value
|
|
142
|
+
for (const [key, value] of Object.entries(processedEnv.existed || {})) {
|
|
98
143
|
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
99
144
|
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
|
|
100
145
|
}
|
|
101
146
|
}
|
|
102
147
|
|
|
103
|
-
let msg = `injected env (${uniqueInjectedKeys.
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
let msg = `injected env (${uniqueInjectedKeys(processedEnvs).size})`
|
|
149
|
+
const envStringCount = processedEnvs.filter((processedEnv) => processedEnv.type === 'env' && processedEnv.parsed).length
|
|
150
|
+
if (readableFilepaths.length > 0 && envStringCount > 0) {
|
|
151
|
+
msg += ` from ${readableFilepaths.join(', ')}, and --env flag${envStringCount > 1 ? 's' : ''}`
|
|
106
152
|
} else if (readableFilepaths.length > 0) {
|
|
107
153
|
msg += ` from ${readableFilepaths.join(', ')}`
|
|
108
|
-
} else if (
|
|
109
|
-
msg += ` from --env flag${
|
|
154
|
+
} else if (envStringCount > 0) {
|
|
155
|
+
msg += ` from --env flag${envStringCount > 1 ? 's' : ''}`
|
|
110
156
|
}
|
|
111
157
|
|
|
112
|
-
const armoredPrivateKeyUsed = processedEnvs.some((processedEnv) => processedEnv.armoredPrivateKeyUsed)
|
|
113
|
-
const keyUsedSuffix = armoredPrivateKeyUsed ? ' · armored ⛨' : ''
|
|
114
|
-
|
|
115
158
|
if (spinner) spinner.stop()
|
|
116
|
-
logger.success(`⟐ ${msg}
|
|
159
|
+
logger.success(`⟐ ${msg}`)
|
|
117
160
|
} catch (error) {
|
|
118
161
|
if (spinner) spinner.stop()
|
|
119
162
|
catchAndLog(error)
|
package/src/cli/actions/set.js
CHANGED
|
@@ -1,87 +1,137 @@
|
|
|
1
1
|
const fsx = require('./../../lib/helpers/fsx')
|
|
2
2
|
const { logger } = require('./../../shared/logger')
|
|
3
3
|
|
|
4
|
+
const setTransform = require('./../../lib/transforms/set')
|
|
5
|
+
|
|
4
6
|
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
5
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
|
+
const Errors = require('../../lib/helpers/errors')
|
|
9
|
+
const prompts = require('../../lib/helpers/prompts')
|
|
6
10
|
const Session = require('../../db/session')
|
|
7
|
-
const
|
|
8
|
-
const normalizeArmorOptions = require('./normalizeArmorOptions')
|
|
11
|
+
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
9
12
|
|
|
10
13
|
async function set (key, value) {
|
|
11
|
-
const options =
|
|
14
|
+
const options = normalizeArmorAliases(this.opts())
|
|
12
15
|
|
|
13
16
|
let encrypt = true
|
|
14
17
|
let settingMessage = 'encrypting'
|
|
18
|
+
let settingSymbol = '◈'
|
|
15
19
|
if (options.plain) {
|
|
16
20
|
encrypt = false
|
|
17
21
|
settingMessage = 'setting'
|
|
22
|
+
settingSymbol = '◇'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof value === 'undefined') {
|
|
26
|
+
if (!process.stdin.isTTY) {
|
|
27
|
+
catchAndLog(new Errors({ key }).missingValue())
|
|
28
|
+
process.exit(1)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
value = await prompts.password({
|
|
34
|
+
message: key,
|
|
35
|
+
prefix: settingSymbol,
|
|
36
|
+
separator: '='
|
|
37
|
+
}, {
|
|
38
|
+
input: process.stdin,
|
|
39
|
+
output: process.stderr
|
|
40
|
+
})
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.code === 'PROMPT_CANCELLED') {
|
|
43
|
+
process.exit(130)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (value === '') {
|
|
52
|
+
catchAndLog(new Errors({ key }).missingValue())
|
|
53
|
+
process.exit(1)
|
|
54
|
+
return
|
|
18
55
|
}
|
|
19
56
|
|
|
20
57
|
const spinner = await createSpinner({ ...options, text: settingMessage })
|
|
58
|
+
const sesh = new Session()
|
|
21
59
|
|
|
22
60
|
logger.debug(`key: ${key}`)
|
|
23
61
|
logger.debug(`value: ${value}`)
|
|
24
62
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
25
63
|
|
|
64
|
+
const envs = this.envs || []
|
|
65
|
+
const fk = options.envKeysFile || '.env.keys'
|
|
66
|
+
const noCreate = options.create === false
|
|
67
|
+
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
68
|
+
|
|
69
|
+
let errorCount = 0
|
|
70
|
+
|
|
26
71
|
try {
|
|
27
|
-
const
|
|
28
|
-
const envKeysFilepath = options.envKeysFile
|
|
29
|
-
const sesh = new Session()
|
|
30
|
-
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
31
|
-
const noCreate = options.create === false
|
|
72
|
+
const { keysSrc, processedEnvs, changedFilepaths, unchangedFilepaths } = await setTransform({ envs, key, value, fk, noArmor, noCreate, encrypt })
|
|
32
73
|
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
changedFilepaths,
|
|
37
|
-
unchangedFilepaths
|
|
38
|
-
} = await new Sets(key, value, envs, encrypt, envKeysFilepath, noArmor, noCreate, {
|
|
39
|
-
command: process.argv.slice(2)
|
|
40
|
-
}).run()
|
|
74
|
+
if (keysSrc) {
|
|
75
|
+
await fsx.writeFileX(fk, keysSrc)
|
|
76
|
+
}
|
|
41
77
|
|
|
42
78
|
let withEncryption = ''
|
|
43
|
-
|
|
44
79
|
if (encrypt) {
|
|
45
80
|
withEncryption = ' with encryption'
|
|
46
81
|
}
|
|
47
82
|
|
|
83
|
+
if (spinner) spinner.stop()
|
|
48
84
|
for (const processedEnv of processedEnvs) {
|
|
49
85
|
logger.verbose(`setting for ${processedEnv.envFilepath}`)
|
|
50
|
-
|
|
51
86
|
if (processedEnv.error) {
|
|
52
|
-
|
|
53
|
-
|
|
87
|
+
errorCount += 1
|
|
88
|
+
logger.error(processedEnv.error.messageWithHelp || processedEnv.error.message)
|
|
89
|
+
} else if (processedEnv.changed) {
|
|
54
90
|
await fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
55
|
-
|
|
56
91
|
logger.verbose(`${processedEnv.key} set${withEncryption} (${processedEnv.envFilepath})`)
|
|
57
|
-
|
|
92
|
+
} else {
|
|
93
|
+
logger.verbose(`no change ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
58
94
|
}
|
|
59
95
|
}
|
|
60
96
|
|
|
61
|
-
const localKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.localPrivateKeyAdded)
|
|
62
|
-
const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
|
|
63
|
-
let keyAddedSuffix = ''
|
|
64
|
-
if (remoteKeyAddedEnv) {
|
|
65
|
-
|
|
66
|
-
}
|
|
97
|
+
// const localKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.localPrivateKeyAdded)
|
|
98
|
+
// const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
|
|
99
|
+
// let keyAddedSuffix = ''
|
|
100
|
+
// if (remoteKeyAddedEnv) {
|
|
101
|
+
// keyAddedSuffix = ' · armored ⛨'
|
|
102
|
+
// }
|
|
67
103
|
|
|
68
|
-
if (spinner) spinner.stop()
|
|
69
104
|
if (changedFilepaths.length > 0) {
|
|
105
|
+
let msg = `◇ set ${key} (${changedFilepaths.join(',')})`
|
|
70
106
|
if (encrypt) {
|
|
71
|
-
|
|
72
|
-
} else {
|
|
73
|
-
logger.success(`◇ set ${key} (${changedFilepaths.join(',')})`)
|
|
107
|
+
msg = `◈ encrypted ${key} (${changedFilepaths.join(',')})`
|
|
74
108
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
|
|
110
|
+
logger.success(msg)
|
|
111
|
+
|
|
112
|
+
// if (encrypt) {
|
|
113
|
+
// logger.success(`◈ encrypted ${key} (${changedFilepaths.join(',')})${keyAddedSuffix}`)
|
|
114
|
+
// } else {
|
|
115
|
+
// logger.success(`◇ set ${key} (${changedFilepaths.join(',')})`)
|
|
116
|
+
// }
|
|
78
117
|
} else if (unchangedFilepaths.length > 0) {
|
|
79
118
|
logger.info(`○ no change (${unchangedFilepaths})`)
|
|
80
119
|
} else {
|
|
81
|
-
// do nothing
|
|
120
|
+
// do nothing - scenario when no .env files found
|
|
82
121
|
}
|
|
83
122
|
|
|
84
|
-
//
|
|
123
|
+
// } else if (encrypt && localKeyAddedEnv) {
|
|
124
|
+
// const localKeyAddedEnvFilepath = localKeyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
|
|
125
|
+
// logger.success(`◈ encrypted ${key} (${localKeyAddedEnvFilepath})${keyAddedSuffix}`)
|
|
126
|
+
// } else if (unchangedFilepaths.length > 0) {
|
|
127
|
+
// logger.info(`○ no change (${unchangedFilepaths})`)
|
|
128
|
+
// } else {
|
|
129
|
+
// // do nothing
|
|
130
|
+
// }
|
|
131
|
+
|
|
132
|
+
if (errorCount > 0) {
|
|
133
|
+
process.exit(1)
|
|
134
|
+
}
|
|
85
135
|
} catch (error) {
|
|
86
136
|
if (spinner) spinner.stop()
|
|
87
137
|
catchAndLog(error)
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -64,7 +64,7 @@ program.command('run')
|
|
|
64
64
|
.description('inject env at runtime [dotenvx run -- yourcommand]')
|
|
65
65
|
.addHelpText('after', examples.run)
|
|
66
66
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
67
|
-
.option('-f, --env-file <
|
|
67
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
68
68
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
69
69
|
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
70
70
|
.option('--strict', 'process.exit(1) on any errors', false)
|
|
@@ -84,7 +84,7 @@ program.command('get')
|
|
|
84
84
|
.description('return a single environment variable')
|
|
85
85
|
.argument('[KEY]', 'environment variable name')
|
|
86
86
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
87
|
-
.option('-f, --env-file <
|
|
87
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
88
88
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
89
89
|
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
90
90
|
.option('--strict', 'process.exit(1) on any errors', false)
|
|
@@ -103,13 +103,13 @@ program.command('get')
|
|
|
103
103
|
|
|
104
104
|
// dotenvx set
|
|
105
105
|
program.command('set')
|
|
106
|
-
.usage('<KEY>
|
|
106
|
+
.usage('<KEY> [value] [options]')
|
|
107
107
|
.description('encrypt a single environment variable')
|
|
108
108
|
.addHelpText('after', examples.set)
|
|
109
109
|
.allowUnknownOption()
|
|
110
110
|
.argument('KEY', 'KEY')
|
|
111
|
-
.argument('value', 'value')
|
|
112
|
-
.option('-f, --env-file <
|
|
111
|
+
.argument('[value]', 'value')
|
|
112
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
113
113
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
114
114
|
.option('-c, --encrypt', 'encrypt value', true)
|
|
115
115
|
.option('-p, --plain', 'store value as plain text', false)
|
|
@@ -124,7 +124,7 @@ program.command('set')
|
|
|
124
124
|
// dotenvx encrypt
|
|
125
125
|
program.command('encrypt')
|
|
126
126
|
.description('encrypt .env file(s)')
|
|
127
|
-
.option('-f, --env-file <
|
|
127
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
128
128
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
129
129
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
130
130
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
@@ -141,7 +141,7 @@ program.command('encrypt')
|
|
|
141
141
|
// dotenvx decrypt
|
|
142
142
|
program.command('decrypt')
|
|
143
143
|
.description('decrypt .env file(s)')
|
|
144
|
-
.option('-f, --env-file <
|
|
144
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
145
145
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
146
146
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
147
147
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
@@ -153,27 +153,12 @@ program.command('decrypt')
|
|
|
153
153
|
return require('./actions/decrypt').apply(this, args)
|
|
154
154
|
})
|
|
155
155
|
|
|
156
|
-
// dotenvx rotate
|
|
157
|
-
program.command('rotate')
|
|
158
|
-
.description('rotate keypair(s) and re-encrypt .env file(s)')
|
|
159
|
-
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
160
|
-
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
161
|
-
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
162
|
-
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
163
|
-
.option('--no-armor', 'disable dotenvx-armor features')
|
|
164
|
-
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
165
|
-
.option('--stdout', 'send to stdout')
|
|
166
|
-
.action(function (...args) {
|
|
167
|
-
this.envs = envs
|
|
168
|
-
return require('./actions/rotate').apply(this, args)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
156
|
// dotenvx keypair
|
|
172
157
|
program.command('keypair')
|
|
173
158
|
.usage('[KEY] [options]')
|
|
174
159
|
.description('print public/private keys for .env file(s)')
|
|
175
160
|
.argument('[KEY]', 'environment variable key name')
|
|
176
|
-
.option('-f, --env-file <
|
|
161
|
+
.option('-f, --env-file <path>', 'path(s) to your env file(s)')
|
|
177
162
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
178
163
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
179
164
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
@@ -3,20 +3,18 @@ const buildApiError = require('../helpers/buildApiError')
|
|
|
3
3
|
const packageJson = require('../helpers/packageJson')
|
|
4
4
|
const normalizeToken = require('../helpers/normalizeToken')
|
|
5
5
|
|
|
6
|
-
class
|
|
7
|
-
constructor (hostname, token, devicePublicKey, publicKey,
|
|
6
|
+
class PostArmorKeyring {
|
|
7
|
+
constructor (hostname, token, devicePublicKey, publicKey, grantToken) {
|
|
8
8
|
this.hostname = hostname || 'https://armor.dotenvx.com'
|
|
9
9
|
this.token = token
|
|
10
10
|
this.devicePublicKey = devicePublicKey
|
|
11
11
|
this.publicKey = publicKey
|
|
12
|
-
this.team = team
|
|
13
|
-
this.metadata = metadata
|
|
14
12
|
this.grantToken = grantToken
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
async run () {
|
|
18
16
|
const token = normalizeToken(this.token)
|
|
19
|
-
const url = `${this.hostname}/api/
|
|
17
|
+
const url = `${this.hostname}/api/armor/keyring`
|
|
20
18
|
const body = {
|
|
21
19
|
device_public_key: this.devicePublicKey,
|
|
22
20
|
cli_version: packageJson.version
|
|
@@ -26,14 +24,6 @@ class PostKeypair {
|
|
|
26
24
|
body.public_key = this.publicKey
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
if (this.team) {
|
|
30
|
-
body.team = this.team
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (this.metadata && Object.keys(this.metadata).length > 0) {
|
|
34
|
-
body.metadata = this.metadata
|
|
35
|
-
}
|
|
36
|
-
|
|
37
27
|
if (this.grantToken) {
|
|
38
28
|
body.grant_token = this.grantToken
|
|
39
29
|
}
|
|
@@ -57,4 +47,4 @@ class PostKeypair {
|
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
49
|
|
|
60
|
-
module.exports =
|
|
50
|
+
module.exports = PostArmorKeyring
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { scan } = require('@dotenvx/primitives')
|
|
2
|
+
const canonicalEnvFilename = require('../helpers/canonicalEnvFilename')
|
|
3
|
+
const environment = require('./environment')
|
|
4
|
+
|
|
5
|
+
function keynames (filepath = '.env', src = '') {
|
|
6
|
+
const { parsed } = scan(src)
|
|
7
|
+
|
|
8
|
+
// src public key name wins
|
|
9
|
+
for (const publicKeyName in parsed) {
|
|
10
|
+
if (publicKeyName.startsWith('DOTENV_PUBLIC_KEY')) {
|
|
11
|
+
return {
|
|
12
|
+
publicKeyName,
|
|
13
|
+
privateKeyName: publicKeyName.replace('DOTENV_PUBLIC_KEY', 'DOTENV_PRIVATE_KEY')
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const filename = canonicalEnvFilename(filepath)
|
|
19
|
+
|
|
20
|
+
// .env
|
|
21
|
+
if (filename === '.env') {
|
|
22
|
+
return {
|
|
23
|
+
publicKeyName: 'DOTENV_PUBLIC_KEY',
|
|
24
|
+
privateKeyName: 'DOTENV_PRIVATE_KEY'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// .env.ENVIRONMENT
|
|
29
|
+
const resolvedEnvironment = environment(filename).toUpperCase()
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
publicKeyName: `DOTENV_PUBLIC_KEY_${resolvedEnvironment}`,
|
|
33
|
+
privateKeyName: `DOTENV_PRIVATE_KEY_${resolvedEnvironment}`
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = keynames
|
|
38
|
+
module.exports.keynames = keynames
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const { logger } = require('./../../shared/logger')
|
|
2
2
|
|
|
3
3
|
function catchAndLog (error) {
|
|
4
|
-
|
|
4
|
+
const msg = error.messageWithHelp || error.message
|
|
5
|
+
if (msg) {
|
|
6
|
+
logger.error(msg)
|
|
7
|
+
}
|
|
5
8
|
if (error.debug) {
|
|
6
9
|
logger.debug(error.debug)
|
|
7
10
|
}
|