@dotenvx/dotenvx 2.1.5 → 2.2.1
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 +13 -1
- package/README.md +264 -99
- package/package.json +1 -1
- package/src/cli/actions/decrypt.js +3 -0
- package/src/cli/actions/encrypt.js +3 -2
- package/src/cli/actions/get.js +2 -0
- package/src/cli/actions/keychain/down.js +29 -0
- package/src/cli/actions/keychain/pull.js +29 -0
- package/src/cli/actions/keychain/push.js +29 -0
- package/src/cli/actions/keychain/up.js +29 -0
- package/src/cli/actions/keypair.js +1 -0
- package/src/cli/actions/run.js +2 -0
- package/src/cli/actions/set.js +2 -1
- package/src/cli/commands/keychain.js +43 -0
- package/src/cli/dotenvx.js +26 -7
- package/src/lib/main.d.ts +24 -0
- package/src/lib/main.js +11 -1
- package/src/lib/providers/index.js +76 -15
- package/src/lib/providers/keychain/index.js +20 -0
- package/src/lib/resolvers/envs.js +2 -2
- package/src/lib/resolvers/get.js +1 -0
- package/src/lib/services/genexample.js +1 -1
- package/src/lib/services/keychainDown.js +57 -0
- package/src/lib/services/keychainPull.js +46 -0
- package/src/lib/services/keychainPush.js +63 -0
- package/src/lib/services/keychainUp.js +81 -0
- package/src/lib/transforms/decrypt.js +1 -1
- package/src/lib/transforms/set.js +3 -1
package/src/cli/actions/run.js
CHANGED
|
@@ -63,6 +63,7 @@ async function run () {
|
|
|
63
63
|
|
|
64
64
|
const sesh = new Session()
|
|
65
65
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
66
|
+
const noKeychain = options.keychain === false || options.noKeychain === true
|
|
66
67
|
|
|
67
68
|
if (commandArgs.length < 1) {
|
|
68
69
|
if (spinner) spinner.stop()
|
|
@@ -98,6 +99,7 @@ async function run () {
|
|
|
98
99
|
processEnv: process.env,
|
|
99
100
|
envKeysFile: options.envKeysFile,
|
|
100
101
|
noArmor,
|
|
102
|
+
noKeychain,
|
|
101
103
|
token: options.token,
|
|
102
104
|
command: commandArgs,
|
|
103
105
|
onStatus: (text) => {
|
package/src/cli/actions/set.js
CHANGED
|
@@ -65,11 +65,12 @@ async function set (key, value) {
|
|
|
65
65
|
const fk = options.envKeysFile || '.env.keys'
|
|
66
66
|
const noCreate = options.create === false
|
|
67
67
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
68
|
+
const noKeychain = options.keychain === false || options.noKeychain === true
|
|
68
69
|
|
|
69
70
|
let errorCount = 0
|
|
70
71
|
|
|
71
72
|
try {
|
|
72
|
-
const { keysSrc, processedEnvs, changedFilepaths, unchangedFilepaths } = await setTransform({ envs, key, value, fk, noArmor, noCreate, encrypt })
|
|
73
|
+
const { keysSrc, processedEnvs, changedFilepaths, unchangedFilepaths } = await setTransform({ envs, key, value, fk, noArmor, noCreate, encrypt, noKeychain })
|
|
73
74
|
|
|
74
75
|
if (keysSrc) {
|
|
75
76
|
await fsx.writeFileX(fk, keysSrc)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function configureKeychainCommand (keychain) {
|
|
2
|
+
keychain
|
|
3
|
+
.description('store private keys in macOS Keychain')
|
|
4
|
+
.action(function () {
|
|
5
|
+
this.help()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const upAction = require('./../actions/keychain/up')
|
|
9
|
+
keychain
|
|
10
|
+
.command('up')
|
|
11
|
+
.description('store key in macOS Keychain')
|
|
12
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
13
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
14
|
+
.action(upAction)
|
|
15
|
+
|
|
16
|
+
const downAction = require('./../actions/keychain/down')
|
|
17
|
+
keychain
|
|
18
|
+
.command('down')
|
|
19
|
+
.description('move key from macOS Keychain to .env.keys')
|
|
20
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
21
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
22
|
+
.action(downAction)
|
|
23
|
+
|
|
24
|
+
const pushAction = require('./../actions/keychain/push')
|
|
25
|
+
keychain
|
|
26
|
+
.command('push')
|
|
27
|
+
.description('push key to macOS Keychain from .env.keys')
|
|
28
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
29
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
30
|
+
.action(pushAction)
|
|
31
|
+
|
|
32
|
+
const pullAction = require('./../actions/keychain/pull')
|
|
33
|
+
keychain
|
|
34
|
+
.command('pull')
|
|
35
|
+
.description('pull key from macOS Keychain into .env.keys')
|
|
36
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
37
|
+
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file', '.env.keys')
|
|
38
|
+
.action(pullAction)
|
|
39
|
+
|
|
40
|
+
return keychain
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = configureKeychainCommand
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -72,7 +72,8 @@ program.command('run')
|
|
|
72
72
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
|
|
73
73
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
74
74
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
75
|
-
.option('--no-armor', 'disable
|
|
75
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
76
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
76
77
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
77
78
|
.action(function (...args) {
|
|
78
79
|
this.envs = envs
|
|
@@ -95,7 +96,8 @@ program.command('get')
|
|
|
95
96
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
96
97
|
.option('--pp', 'pretty print output (alias)')
|
|
97
98
|
.option('--format <type>', 'format of the output (json, shell, colon, eval)', 'json')
|
|
98
|
-
.option('--no-armor', 'disable
|
|
99
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
100
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
99
101
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
100
102
|
.action(function (...args) {
|
|
101
103
|
this.envs = envs
|
|
@@ -115,7 +117,8 @@ program.command('set')
|
|
|
115
117
|
.option('-c, --encrypt', 'encrypt value', true)
|
|
116
118
|
.option('-p, --plain', 'store value as plain text', false)
|
|
117
119
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
118
|
-
.option('--no-armor', 'disable
|
|
120
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
121
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
119
122
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
120
123
|
.action(function (...args) {
|
|
121
124
|
this.envs = envs
|
|
@@ -132,7 +135,8 @@ program.command('encrypt')
|
|
|
132
135
|
.option('--stdout', 'send to stdout')
|
|
133
136
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
134
137
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
135
|
-
.option('--no-armor', 'disable
|
|
138
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
139
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
136
140
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
137
141
|
.action(function (...args) {
|
|
138
142
|
this.envs = envs
|
|
@@ -146,7 +150,8 @@ program.command('decrypt')
|
|
|
146
150
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
147
151
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
148
152
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
149
|
-
.option('--no-armor', 'disable
|
|
153
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
154
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
150
155
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
151
156
|
.option('--stdout', 'send to stdout')
|
|
152
157
|
.action(function (...args) {
|
|
@@ -161,7 +166,8 @@ program.command('keypair')
|
|
|
161
166
|
.argument('[KEY]', 'environment variable key name')
|
|
162
167
|
.option('-f, --env-file <path>', 'path(s) to your env file(s)')
|
|
163
168
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
164
|
-
.option('--no-armor', 'disable
|
|
169
|
+
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
170
|
+
.option('--no-keychain', 'disable macOS Keychain features')
|
|
165
171
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
166
172
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
167
173
|
.option('--pp', 'pretty print output (alias)')
|
|
@@ -180,6 +186,15 @@ program.command('ls')
|
|
|
180
186
|
return require('./actions/ls').apply(this, args)
|
|
181
187
|
})
|
|
182
188
|
|
|
189
|
+
// dotenvx genexample
|
|
190
|
+
program.command('genexample')
|
|
191
|
+
.description('generate .env.example')
|
|
192
|
+
.argument('[directory]', 'directory to generate from', '.')
|
|
193
|
+
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
194
|
+
.action(function (...args) {
|
|
195
|
+
return require('./actions/ext/genexample').apply(this, args)
|
|
196
|
+
})
|
|
197
|
+
|
|
183
198
|
// dotenvx gitignore
|
|
184
199
|
program.command('gitignore')
|
|
185
200
|
.description('append to .gitignore')
|
|
@@ -255,7 +270,11 @@ program.addHelpText('after', ' ')
|
|
|
255
270
|
program.addHelpText('after', 'Professional Security: ')
|
|
256
271
|
program.addHelpText('after', ' login log in to move keys off-device, share with your team, and audit access')
|
|
257
272
|
program.addHelpText('after', ' logout log out of connected security features')
|
|
258
|
-
program.addHelpText('after', '
|
|
273
|
+
program.addHelpText('after', ' keychain ⌥ move private keys into macOS Keychain')
|
|
274
|
+
program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
|
|
275
|
+
|
|
276
|
+
// dotenvx keychain
|
|
277
|
+
require('./commands/keychain')(program.command('keychain', { hidden: true }))
|
|
259
278
|
|
|
260
279
|
// dotenvx armor
|
|
261
280
|
require('./commands/armor')(program.command('armor', { hidden: true }))
|
package/src/lib/main.d.ts
CHANGED
|
@@ -179,6 +179,14 @@ export interface DotenvConfigOptions {
|
|
|
179
179
|
*/
|
|
180
180
|
noArmor?: boolean;
|
|
181
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Turn off macOS Keychain features.
|
|
184
|
+
*
|
|
185
|
+
* @default false
|
|
186
|
+
* @example require('@dotenvx/dotenvx').config({ noKeychain: true })
|
|
187
|
+
*/
|
|
188
|
+
noKeychain?: boolean;
|
|
189
|
+
|
|
182
190
|
/**
|
|
183
191
|
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
184
192
|
*
|
|
@@ -262,6 +270,14 @@ export interface SetOptions {
|
|
|
262
270
|
*/
|
|
263
271
|
noArmor?: boolean;
|
|
264
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Turn off macOS Keychain features.
|
|
275
|
+
*
|
|
276
|
+
* @default false
|
|
277
|
+
* @example require('@dotenvx/dotenvx').set(key, value, { noKeychain: true })
|
|
278
|
+
*/
|
|
279
|
+
noKeychain?: boolean;
|
|
280
|
+
|
|
265
281
|
/**
|
|
266
282
|
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
267
283
|
*
|
|
@@ -347,6 +363,14 @@ export interface GetOptions {
|
|
|
347
363
|
*/
|
|
348
364
|
noArmor?: boolean;
|
|
349
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Turn off macOS Keychain features.
|
|
368
|
+
*
|
|
369
|
+
* @default false
|
|
370
|
+
* @example require('@dotenvx/dotenvx').get('KEY', { noKeychain: true })
|
|
371
|
+
*/
|
|
372
|
+
noKeychain?: boolean;
|
|
373
|
+
|
|
350
374
|
/**
|
|
351
375
|
* Turn off Dotenvx Armor features. Alias for `noArmor`.
|
|
352
376
|
*
|
package/src/lib/main.js
CHANGED
|
@@ -66,6 +66,7 @@ const config = function (options = {}) {
|
|
|
66
66
|
|
|
67
67
|
// dotenvx-armor related
|
|
68
68
|
const noArmor = resolveNoArmor(options)
|
|
69
|
+
const noKeychain = resolveNoKeychain(options)
|
|
69
70
|
|
|
70
71
|
try {
|
|
71
72
|
let envs = buildEnvs(options)
|
|
@@ -81,6 +82,7 @@ const config = function (options = {}) {
|
|
|
81
82
|
processEnv,
|
|
82
83
|
envKeysFile,
|
|
83
84
|
noArmor,
|
|
85
|
+
noKeychain,
|
|
84
86
|
noSpinner: options.noSpinner,
|
|
85
87
|
token: options.token
|
|
86
88
|
})
|
|
@@ -227,6 +229,7 @@ const set = async function (key, value, options = {}) {
|
|
|
227
229
|
const envKeysFilepath = options.envKeysFile
|
|
228
230
|
const noCreate = options.create === false
|
|
229
231
|
const noArmor = resolveNoArmor(options)
|
|
232
|
+
const noKeychain = resolveNoKeychain(options)
|
|
230
233
|
|
|
231
234
|
const {
|
|
232
235
|
keysSrc,
|
|
@@ -239,6 +242,7 @@ const set = async function (key, value, options = {}) {
|
|
|
239
242
|
value,
|
|
240
243
|
fk: envKeysFilepath,
|
|
241
244
|
noArmor,
|
|
245
|
+
noKeychain,
|
|
242
246
|
noCreate,
|
|
243
247
|
encrypt
|
|
244
248
|
})
|
|
@@ -306,6 +310,7 @@ const get = async function (key, options = {}) {
|
|
|
306
310
|
|
|
307
311
|
const envs = buildEnvs(options)
|
|
308
312
|
const noArmor = resolveNoArmor(options)
|
|
313
|
+
const noKeychain = resolveNoKeychain(options)
|
|
309
314
|
|
|
310
315
|
// ignore
|
|
311
316
|
const ignore = options.ignore || []
|
|
@@ -316,7 +321,8 @@ const get = async function (key, options = {}) {
|
|
|
316
321
|
overload: options.overload,
|
|
317
322
|
all: options.all,
|
|
318
323
|
envKeysFile: options.envKeysFile,
|
|
319
|
-
noArmor
|
|
324
|
+
noArmor,
|
|
325
|
+
noKeychain
|
|
320
326
|
})
|
|
321
327
|
|
|
322
328
|
for (const error of errors || []) {
|
|
@@ -377,6 +383,10 @@ function resolveNoArmor (options = {}) {
|
|
|
377
383
|
return options.noArmor === true || options.noOps === true || (!options.token && sesh.noArmorSync())
|
|
378
384
|
}
|
|
379
385
|
|
|
386
|
+
function resolveNoKeychain (options = {}) {
|
|
387
|
+
return options.noKeychain === true || options.keychain === false
|
|
388
|
+
}
|
|
389
|
+
|
|
380
390
|
module.exports = {
|
|
381
391
|
// dotenv proxies
|
|
382
392
|
config,
|
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
const Session = require('./../../db/session')
|
|
2
2
|
|
|
3
3
|
const armorProvider = require('./armor/index')
|
|
4
|
+
const keychainProvider = require('./keychain/index')
|
|
5
|
+
|
|
4
6
|
function syncArmorProvider (publicKeyHex) {
|
|
5
7
|
const { createSyncFn } = require('synckit')
|
|
6
8
|
const runProviderSync = createSyncFn(require.resolve('./worker.js'))
|
|
7
9
|
return runProviderSync(require.resolve('./armor/index'), publicKeyHex)
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
12
|
+
function hasKey (keyring, publicKeyHex) {
|
|
13
|
+
return keyring && keyring[publicKeyHex]
|
|
14
|
+
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
function composeProviders (providerFns) {
|
|
17
|
+
return async function provider (publicKeyHex) {
|
|
18
|
+
for (const providerFn of providerFns) {
|
|
19
|
+
const keyring = await providerFn(publicKeyHex)
|
|
20
|
+
if (hasKey(keyring, publicKeyHex)) return keyring
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {}
|
|
17
24
|
}
|
|
25
|
+
}
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
function composeProvidersSync (providerFns) {
|
|
28
|
+
return function providerSync (publicKeyHex) {
|
|
29
|
+
for (const providerFn of providerFns) {
|
|
30
|
+
const keyring = providerFn(publicKeyHex)
|
|
31
|
+
if (hasKey(keyring, publicKeyHex)) return keyring
|
|
32
|
+
}
|
|
22
33
|
|
|
34
|
+
return {}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function armorProviderForOptions (options) {
|
|
23
39
|
return (publicKeyHex) => armorProvider(publicKeyHex, {
|
|
24
40
|
onStatus: options.onStatus,
|
|
25
41
|
token: options.token,
|
|
@@ -27,20 +43,65 @@ async function providers (options = {}) {
|
|
|
27
43
|
})
|
|
28
44
|
}
|
|
29
45
|
|
|
46
|
+
function useKeychain (options) {
|
|
47
|
+
if (process.platform !== 'darwin') return false
|
|
48
|
+
if (process.env.CI) return false
|
|
49
|
+
return options.noKeychain !== true && options.keychain !== false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function useArmor (options, noArmor) {
|
|
53
|
+
return options.noArmor !== true && options.armor !== false && !noArmor
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function providerFrom (providerFns, compose) {
|
|
57
|
+
if (providerFns.length === 0) return null
|
|
58
|
+
if (providerFns.length === 1) return providerFns[0]
|
|
59
|
+
|
|
60
|
+
return compose(providerFns)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function providers (options = {}) {
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(options, 'provider')) {
|
|
65
|
+
return options.provider
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const providerFns = []
|
|
69
|
+
|
|
70
|
+
if (useKeychain(options)) {
|
|
71
|
+
providerFns.push(keychainProvider)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (options.noArmor !== true && options.armor !== false) {
|
|
75
|
+
const sesh = new Session()
|
|
76
|
+
const noArmor = !options.token && await sesh.noArmor()
|
|
77
|
+
if (useArmor(options, noArmor)) {
|
|
78
|
+
providerFns.push(armorProviderForOptions(options))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return providerFrom(providerFns, composeProviders)
|
|
83
|
+
}
|
|
84
|
+
|
|
30
85
|
providers.sync = function providersSync (options = {}) {
|
|
31
86
|
if (Object.prototype.hasOwnProperty.call(options, 'provider')) {
|
|
32
87
|
return options.provider
|
|
33
88
|
}
|
|
34
89
|
|
|
35
|
-
|
|
36
|
-
|
|
90
|
+
const providerFns = []
|
|
91
|
+
|
|
92
|
+
if (useKeychain(options)) {
|
|
93
|
+
providerFns.push(keychainProvider)
|
|
37
94
|
}
|
|
38
95
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
96
|
+
if (options.noArmor !== true && options.armor !== false) {
|
|
97
|
+
const sesh = new Session()
|
|
98
|
+
const noArmor = !options.token && sesh.noArmorSync()
|
|
99
|
+
if (useArmor(options, noArmor)) {
|
|
100
|
+
providerFns.push(syncArmorProvider)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
42
103
|
|
|
43
|
-
return
|
|
104
|
+
return providerFrom(providerFns, composeProvidersSync)
|
|
44
105
|
}
|
|
45
106
|
|
|
46
107
|
module.exports = providers
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const SECURITY_BIN = '/usr/bin/security'
|
|
4
|
+
const SERVICE = 'dotenvx'
|
|
5
|
+
|
|
6
|
+
function index (publicKeyHex) {
|
|
7
|
+
if (process.platform !== 'darwin') return {}
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const stdout = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKeyHex, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
|
|
11
|
+
const privateKeyHex = stdout.trim()
|
|
12
|
+
if (!privateKeyHex) return {}
|
|
13
|
+
|
|
14
|
+
return { [publicKeyHex]: privateKeyHex }
|
|
15
|
+
} catch {
|
|
16
|
+
return {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = index
|
|
@@ -235,7 +235,7 @@ async function envs (options = {}) {
|
|
|
235
235
|
const readableFilepaths = new Set()
|
|
236
236
|
const processEnv = options.processEnv || process.env
|
|
237
237
|
const envKeysFilepath = options.envKeysFilepath || options.envKeysFile || null
|
|
238
|
-
const provider =
|
|
238
|
+
const provider = await providers(options)
|
|
239
239
|
|
|
240
240
|
for (const env of options.envs || []) {
|
|
241
241
|
if (env.type === TYPE_ENV_FILE) {
|
|
@@ -269,7 +269,7 @@ function envsSync (options = {}) {
|
|
|
269
269
|
const readableFilepaths = new Set()
|
|
270
270
|
const processEnv = options.processEnv || process.env
|
|
271
271
|
const envKeysFilepath = options.envKeysFilepath || options.envKeysFile || null
|
|
272
|
-
const provider =
|
|
272
|
+
const provider = providers.sync(options)
|
|
273
273
|
|
|
274
274
|
for (const env of options.envs || []) {
|
|
275
275
|
if (env.type === TYPE_ENV_FILE) {
|
package/src/lib/resolvers/get.js
CHANGED
|
@@ -45,7 +45,7 @@ class Genexample {
|
|
|
45
45
|
const filepath = path.resolve(this.directory, envFilepath)
|
|
46
46
|
if (!fsx.existsSync(filepath)) {
|
|
47
47
|
const error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
48
|
-
error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx
|
|
48
|
+
error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
|
|
49
49
|
throw error
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const keynames = require('../conventions/keynames')
|
|
4
|
+
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
|
+
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
|
+
|
|
7
|
+
const SECURITY_BIN = '/usr/bin/security'
|
|
8
|
+
const SERVICE = 'dotenvx'
|
|
9
|
+
|
|
10
|
+
class KeychainDown {
|
|
11
|
+
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
12
|
+
this.envFile = envFile
|
|
13
|
+
this.envKeysFile = envKeysFile
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
run () {
|
|
17
|
+
const envFile = this.envFile
|
|
18
|
+
const envKeysFile = this.envKeysFile
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
publicKeyName,
|
|
22
|
+
privateKeyName
|
|
23
|
+
} = keynames(envFile)
|
|
24
|
+
|
|
25
|
+
const publicKey = readEnvKey(publicKeyName, envFile, { strict: true, ignore: ['MISSING_PRIVATE_KEY'] })
|
|
26
|
+
let privateKey
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
privateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
30
|
+
} catch (error) {
|
|
31
|
+
privateKey = readEnvKey(privateKeyName, envKeysFile, { strict: false })
|
|
32
|
+
|
|
33
|
+
if (privateKey) {
|
|
34
|
+
return {
|
|
35
|
+
changed: false,
|
|
36
|
+
privateKeyName,
|
|
37
|
+
privateKeyValue: privateKey,
|
|
38
|
+
publicKeyValue: publicKey
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
upsertEnvKey(privateKeyName, privateKey, envKeysFile)
|
|
46
|
+
execFileSync(SECURITY_BIN, ['delete-generic-password', '-s', SERVICE, '-a', publicKey], { stdio: 'ignore' })
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
changed: true,
|
|
50
|
+
privateKeyName,
|
|
51
|
+
privateKeyValue: privateKey,
|
|
52
|
+
publicKeyValue: publicKey
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = KeychainDown
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const keynames = require('../conventions/keynames')
|
|
4
|
+
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
|
+
const upsertEnvKey = require('../helpers/upsertEnvKey')
|
|
6
|
+
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
7
|
+
|
|
8
|
+
const SECURITY_BIN = '/usr/bin/security'
|
|
9
|
+
const SERVICE = 'dotenvx'
|
|
10
|
+
|
|
11
|
+
class KeychainPull {
|
|
12
|
+
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
13
|
+
this.envFile = envFile
|
|
14
|
+
this.envKeysFile = envKeysFile
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
run () {
|
|
18
|
+
const envFile = this.envFile
|
|
19
|
+
const envKeysFile = this.envKeysFile
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
publicKeyName,
|
|
23
|
+
privateKeyName
|
|
24
|
+
} = keynames(envFile)
|
|
25
|
+
|
|
26
|
+
const publicKey = readEnvKey(publicKeyName, envFile, { strict: true, ignore: ['MISSING_PRIVATE_KEY'] })
|
|
27
|
+
let privateKey
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
privateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error(`[NOT_FOUND] private key not found in macOS Keychain (${armoredKeyDisplay(publicKey)}). fix: [dotenvx keychain up]`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const result = upsertEnvKey(privateKeyName, privateKey, envKeysFile)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
changed: result.changed,
|
|
39
|
+
privateKeyName,
|
|
40
|
+
privateKeyValue: privateKey,
|
|
41
|
+
publicKeyValue: publicKey
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = KeychainPull
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process')
|
|
2
|
+
|
|
3
|
+
const keynames = require('../conventions/keynames')
|
|
4
|
+
const readEnvKey = require('../helpers/readEnvKey')
|
|
5
|
+
const armoredKeyDisplay = require('../helpers/armoredKeyDisplay')
|
|
6
|
+
|
|
7
|
+
const SECURITY_BIN = '/usr/bin/security'
|
|
8
|
+
const SERVICE = 'dotenvx'
|
|
9
|
+
|
|
10
|
+
function addGenericPassword (publicKey, label, privateKey) {
|
|
11
|
+
try {
|
|
12
|
+
execFileSync(SECURITY_BIN, ['add-generic-password', '-U', '-s', SERVICE, '-a', publicKey, '-l', label, '-w', privateKey], { stdio: 'ignore' })
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('failed to save private key to macOS Keychain')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class KeychainPush {
|
|
19
|
+
constructor (envFile = '.env', envKeysFile = '.env.keys') {
|
|
20
|
+
this.envFile = envFile
|
|
21
|
+
this.envKeysFile = envKeysFile
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
run () {
|
|
25
|
+
const envFile = this.envFile
|
|
26
|
+
const envKeysFile = this.envKeysFile
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
publicKeyName,
|
|
30
|
+
privateKeyName
|
|
31
|
+
} = keynames(envFile)
|
|
32
|
+
|
|
33
|
+
const publicKey = readEnvKey(publicKeyName, envFile, { strict: true, ignore: ['MISSING_PRIVATE_KEY'] })
|
|
34
|
+
const privateKey = readEnvKey(privateKeyName, envKeysFile, { strict: true })
|
|
35
|
+
const label = `dotenvx (${armoredKeyDisplay(publicKey)})`
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const existingPrivateKey = execFileSync(SECURITY_BIN, ['find-generic-password', '-s', SERVICE, '-a', publicKey, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim()
|
|
39
|
+
|
|
40
|
+
if (existingPrivateKey === privateKey) {
|
|
41
|
+
return {
|
|
42
|
+
changed: false,
|
|
43
|
+
label,
|
|
44
|
+
privateKeyName,
|
|
45
|
+
privateKeyValue: privateKey,
|
|
46
|
+
publicKeyValue: publicKey
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {}
|
|
50
|
+
|
|
51
|
+
addGenericPassword(publicKey, label, privateKey)
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
changed: true,
|
|
55
|
+
label,
|
|
56
|
+
privateKeyName,
|
|
57
|
+
privateKeyValue: privateKey,
|
|
58
|
+
publicKeyValue: publicKey
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = KeychainPush
|