@dotenvx/dotenvx 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +653 -17
- package/package.json +1 -1
- package/src/cli/actions/encryptme.js +73 -0
- package/src/cli/actions/get.js +11 -1
- package/src/cli/actions/run.js +5 -4
- package/src/cli/actions/set.js +43 -9
- package/src/cli/actions/vault/encrypt.js +0 -1
- package/src/cli/commands/hub.js +8 -8
- package/src/cli/dotenvx.js +19 -11
- package/src/lib/helpers/findOrCreatePublicKey.js +4 -2
- package/src/lib/helpers/isEncrypted.js +8 -0
- package/src/lib/helpers/isFullyEncrypted.js +18 -0
- package/src/lib/helpers/isIgnoringDotenvKeys.js +19 -0
- package/src/lib/main.js +14 -3
- package/src/lib/services/encrypt.js +73 -87
- package/src/lib/services/precommit.js +19 -8
- package/src/lib/services/sets.js +32 -10
- package/src/lib/services/vaultEncrypt.js +118 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const logger = require('./../../shared/logger')
|
|
3
|
+
|
|
4
|
+
const main = require('./../../lib/main')
|
|
5
|
+
|
|
6
|
+
const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
|
|
7
|
+
|
|
8
|
+
const ENCODING = 'utf8'
|
|
9
|
+
|
|
10
|
+
async function encryptme () {
|
|
11
|
+
const options = this.opts()
|
|
12
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const {
|
|
16
|
+
processedEnvFiles,
|
|
17
|
+
changedFilepaths,
|
|
18
|
+
unchangedFilepaths
|
|
19
|
+
} = main.encryptme(options.envFile)
|
|
20
|
+
|
|
21
|
+
for (const processedEnvFile of processedEnvFiles) {
|
|
22
|
+
logger.verbose(`encrypting ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
|
|
23
|
+
if (processedEnvFile.error) {
|
|
24
|
+
if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
|
|
25
|
+
logger.warn(processedEnvFile.error)
|
|
26
|
+
logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx encryptme]`)
|
|
27
|
+
} else {
|
|
28
|
+
logger.warn(processedEnvFile.error)
|
|
29
|
+
}
|
|
30
|
+
} else if (processedEnvFile.changed) {
|
|
31
|
+
fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
|
|
32
|
+
|
|
33
|
+
logger.verbose(`encrypted ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
|
|
34
|
+
} else {
|
|
35
|
+
logger.verbose(`no changes ${processedEnvFile.envFilepath} (${processedEnvFile.filepath})`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (changedFilepaths.length > 0) {
|
|
40
|
+
logger.success(`✔ encrypted (${changedFilepaths.join(',')})`)
|
|
41
|
+
} else if (unchangedFilepaths.length > 0) {
|
|
42
|
+
logger.info(`no changes (${unchangedFilepaths})`)
|
|
43
|
+
} else {
|
|
44
|
+
// do nothing - scenario when no .env files found
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const processedEnvFile of processedEnvFiles) {
|
|
48
|
+
if (processedEnvFile.privateKeyAdded) {
|
|
49
|
+
logger.success(`✔ key added to .env.keys (${processedEnvFile.privateKeyName})`)
|
|
50
|
+
|
|
51
|
+
if (!isIgnoringDotenvKeys()) {
|
|
52
|
+
logger.help2('ℹ add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
logger.help2(`ℹ run [${processedEnvFile.privateKeyName}='${processedEnvFile.privateKey}' dotenvx run -- yourcommand] to test decryption locally`)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error(error.message)
|
|
60
|
+
if (error.help) {
|
|
61
|
+
logger.help(error.help)
|
|
62
|
+
}
|
|
63
|
+
if (error.debug) {
|
|
64
|
+
logger.debug(error.debug)
|
|
65
|
+
}
|
|
66
|
+
if (error.code) {
|
|
67
|
+
logger.debug(`ERROR_CODE: ${error.code}`)
|
|
68
|
+
}
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = encryptme
|
package/src/cli/actions/get.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const logger = require('./../../shared/logger')
|
|
2
2
|
|
|
3
|
+
const conventions = require('./../../lib/helpers/conventions')
|
|
4
|
+
|
|
3
5
|
const main = require('./../../lib/main')
|
|
4
6
|
|
|
5
7
|
function get (key) {
|
|
@@ -8,7 +10,15 @@ function get (key) {
|
|
|
8
10
|
const options = this.opts()
|
|
9
11
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
let envs = []
|
|
14
|
+
// handle shorthand conventions - like --convention=nextjs
|
|
15
|
+
if (options.convention) {
|
|
16
|
+
envs = conventions(options.convention).concat(this.envs)
|
|
17
|
+
} else {
|
|
18
|
+
envs = this.envs
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const value = main.get(key, envs, options.overload, process.env.DOTENV_KEY, options.all)
|
|
12
22
|
|
|
13
23
|
if (typeof value === 'object' && value !== null) {
|
|
14
24
|
if (options.prettyPrint) {
|
package/src/cli/actions/run.js
CHANGED
|
@@ -136,10 +136,11 @@ async function run () {
|
|
|
136
136
|
|
|
137
137
|
if (processedEnv.error) {
|
|
138
138
|
if (processedEnv.error.code === 'MISSING_ENV_FILE') {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
// do not warn for conventions (too noisy)
|
|
140
|
+
if (!options.convention) {
|
|
141
|
+
logger.warnv(processedEnv.error)
|
|
142
|
+
logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.filepath}] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
|
|
143
|
+
}
|
|
143
144
|
} else {
|
|
144
145
|
logger.warnv(processedEnv.error)
|
|
145
146
|
}
|
package/src/cli/actions/set.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
1
2
|
const logger = require('./../../shared/logger')
|
|
2
3
|
|
|
3
4
|
const main = require('./../../lib/main')
|
|
4
5
|
|
|
6
|
+
const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
|
|
7
|
+
|
|
8
|
+
const ENCODING = 'utf8'
|
|
9
|
+
|
|
5
10
|
function set (key, value) {
|
|
6
11
|
logger.debug(`key: ${key}`)
|
|
7
12
|
logger.debug(`value: ${value}`)
|
|
@@ -12,36 +17,65 @@ function set (key, value) {
|
|
|
12
17
|
try {
|
|
13
18
|
const {
|
|
14
19
|
processedEnvFiles,
|
|
15
|
-
|
|
20
|
+
changedFilepaths,
|
|
21
|
+
unchangedFilepaths
|
|
16
22
|
} = main.set(key, value, options.envFile, options.encrypt)
|
|
17
23
|
|
|
18
|
-
let
|
|
24
|
+
let withEncryption = ''
|
|
25
|
+
|
|
26
|
+
if (options.encrypt) {
|
|
27
|
+
withEncryption = ' with encryption'
|
|
28
|
+
}
|
|
19
29
|
|
|
20
30
|
for (const processedEnvFile of processedEnvFiles) {
|
|
21
|
-
logger.verbose(`setting for ${processedEnvFile.
|
|
31
|
+
logger.verbose(`setting for ${processedEnvFile.envFilepath}`)
|
|
22
32
|
|
|
23
33
|
if (processedEnvFile.error) {
|
|
24
34
|
if (processedEnvFile.error.code === 'MISSING_ENV_FILE') {
|
|
25
35
|
logger.warn(processedEnvFile.error)
|
|
26
|
-
logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.
|
|
36
|
+
logger.help(`? add one with [echo "HELLO=World" > ${processedEnvFile.envFilepath}] and re-run [dotenvx set]`)
|
|
27
37
|
} else {
|
|
28
38
|
logger.warn(processedEnvFile.error)
|
|
29
39
|
}
|
|
30
40
|
} else {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
logger.
|
|
41
|
+
fs.writeFileSync(processedEnvFile.filepath, processedEnvFile.envSrc, ENCODING)
|
|
42
|
+
|
|
43
|
+
logger.verbose(`${processedEnvFile.key} set${withEncryption} (${processedEnvFile.envFilepath})`)
|
|
44
|
+
logger.debug(`${processedEnvFile.key} set${withEncryption} to ${processedEnvFile.value} (${processedEnvFile.envFilepath})`)
|
|
34
45
|
}
|
|
35
46
|
}
|
|
36
47
|
|
|
37
|
-
if (
|
|
38
|
-
logger.success(
|
|
48
|
+
if (changedFilepaths.length > 0) {
|
|
49
|
+
logger.success(`✔ set ${key}${withEncryption} (${changedFilepaths.join(',')})`)
|
|
50
|
+
} else if (unchangedFilepaths.length > 0) {
|
|
51
|
+
logger.info(`no changes (${unchangedFilepaths})`)
|
|
52
|
+
} else {
|
|
53
|
+
// do nothing
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const processedEnvFile of processedEnvFiles) {
|
|
57
|
+
if (processedEnvFile.privateKeyAdded) {
|
|
58
|
+
logger.success(`✔ key added to .env.keys (${processedEnvFile.privateKeyName})`)
|
|
59
|
+
|
|
60
|
+
if (!isIgnoringDotenvKeys()) {
|
|
61
|
+
logger.help2('ℹ add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.help2(`ℹ run [${processedEnvFile.privateKeyName}='${processedEnvFile.privateKey}' dotenvx get ${key}] to test decryption locally`)
|
|
65
|
+
}
|
|
39
66
|
}
|
|
40
67
|
} catch (error) {
|
|
41
68
|
logger.error(error.message)
|
|
42
69
|
if (error.help) {
|
|
43
70
|
logger.help(error.help)
|
|
44
71
|
}
|
|
72
|
+
if (error.debug) {
|
|
73
|
+
logger.debug(error.debug)
|
|
74
|
+
}
|
|
75
|
+
if (error.code) {
|
|
76
|
+
logger.debug(`ERROR_CODE: ${error.code}`)
|
|
77
|
+
}
|
|
78
|
+
process.exit(1)
|
|
45
79
|
}
|
|
46
80
|
}
|
|
47
81
|
|
|
@@ -58,7 +58,6 @@ async function encrypt (directory) {
|
|
|
58
58
|
|
|
59
59
|
if (addedKeys.length > 0) {
|
|
60
60
|
spinner.succeed(`${pluralize('key', addedKeys.length)} added to .env.keys (${addedKeys})`)
|
|
61
|
-
logger.help2('ℹ push .env.keys up to hub: [dotenvx hub push]')
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
if (addedVaults.length > 0) {
|
package/src/cli/commands/hub.js
CHANGED
|
@@ -6,7 +6,7 @@ const logger = require('./../../shared/logger')
|
|
|
6
6
|
const hub = new Command('hub')
|
|
7
7
|
|
|
8
8
|
hub
|
|
9
|
-
.description('interact with dotenvx hub')
|
|
9
|
+
.description('DEPRECATED: interact with dotenvx hub')
|
|
10
10
|
|
|
11
11
|
const loginAction = require('./../actions/hub/login')
|
|
12
12
|
hub
|
|
@@ -14,7 +14,7 @@ hub
|
|
|
14
14
|
.description('authenticate to dotenvx hub')
|
|
15
15
|
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
16
16
|
.action(function (...args) {
|
|
17
|
-
logger.warn('DEPRECATION
|
|
17
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub login] will be removed in 1.0.0 release soon')
|
|
18
18
|
|
|
19
19
|
loginAction.apply(this, args)
|
|
20
20
|
})
|
|
@@ -26,7 +26,7 @@ hub
|
|
|
26
26
|
.argument('[directory]', 'directory to push', '.')
|
|
27
27
|
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
28
28
|
.action(function (...args) {
|
|
29
|
-
logger.warn('DEPRECATION
|
|
29
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub push] will be removed in 1.0.0 release soon')
|
|
30
30
|
|
|
31
31
|
pushAction.apply(this, args)
|
|
32
32
|
})
|
|
@@ -38,7 +38,7 @@ hub
|
|
|
38
38
|
.argument('[directory]', 'directory to pull', '.')
|
|
39
39
|
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
40
40
|
.action(function (...args) {
|
|
41
|
-
logger.warn('DEPRECATION
|
|
41
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub pull] will be removed in 1.0.0 release soon')
|
|
42
42
|
|
|
43
43
|
pullAction.apply(this, args)
|
|
44
44
|
})
|
|
@@ -49,7 +49,7 @@ hub
|
|
|
49
49
|
.description('view repository on dotenvx hub')
|
|
50
50
|
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
51
51
|
.action(function (...args) {
|
|
52
|
-
logger.warn('DEPRECATION
|
|
52
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub open] will be removed in 1.0.0 release soon')
|
|
53
53
|
|
|
54
54
|
openAction.apply(this, args)
|
|
55
55
|
})
|
|
@@ -60,7 +60,7 @@ hub
|
|
|
60
60
|
.description('print the auth token dotenvx hub is configured to use')
|
|
61
61
|
.option('-h, --hostname <url>', 'set hostname', 'https://hub.dotenvx.com')
|
|
62
62
|
.action(function (...args) {
|
|
63
|
-
logger.warn('DEPRECATION
|
|
63
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub token] will be removed in 1.0.0 release soon')
|
|
64
64
|
|
|
65
65
|
tokenAction.apply(this, args)
|
|
66
66
|
})
|
|
@@ -70,7 +70,7 @@ hub
|
|
|
70
70
|
.command('status')
|
|
71
71
|
.description('display logged in user')
|
|
72
72
|
.action(function (...args) {
|
|
73
|
-
logger.warn('DEPRECATION
|
|
73
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub status] will be removed in 1.0.0 release soon')
|
|
74
74
|
|
|
75
75
|
statusAction.apply(this, args)
|
|
76
76
|
})
|
|
@@ -81,7 +81,7 @@ hub
|
|
|
81
81
|
.description('log out this machine from dotenvx hub')
|
|
82
82
|
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
83
83
|
.action(function (...args) {
|
|
84
|
-
logger.warn('DEPRECATION
|
|
84
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx hub logout] will be removed in 1.0.0 release soon')
|
|
85
85
|
|
|
86
86
|
logoutAction.apply(this, args)
|
|
87
87
|
})
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -85,6 +85,7 @@ program.command('get')
|
|
|
85
85
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
86
86
|
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
87
87
|
.option('-o, --overload', 'override existing env variables')
|
|
88
|
+
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\'])')
|
|
88
89
|
.option('-a, --all', 'include all machine envs as well')
|
|
89
90
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
90
91
|
.action(function (...args) {
|
|
@@ -102,6 +103,19 @@ program.command('set')
|
|
|
102
103
|
.option('-c, --encrypt', 'encrypt value')
|
|
103
104
|
.action(require('./actions/set'))
|
|
104
105
|
|
|
106
|
+
// dotenvx encryptme
|
|
107
|
+
program.command('encryptme')
|
|
108
|
+
.description('encrypt env file(s) environment variables')
|
|
109
|
+
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
110
|
+
.action(require('./actions/encryptme'))
|
|
111
|
+
|
|
112
|
+
// dotenvx ls
|
|
113
|
+
program.command('ls')
|
|
114
|
+
.description('print all .env files in a tree structure')
|
|
115
|
+
.argument('[directory]', 'directory to list .env files from', '.')
|
|
116
|
+
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
117
|
+
.action(require('./actions/ls'))
|
|
118
|
+
|
|
105
119
|
// dotenvx genexample
|
|
106
120
|
program.command('genexample')
|
|
107
121
|
.description('generate .env.example')
|
|
@@ -133,13 +147,6 @@ program.command('scan')
|
|
|
133
147
|
.description('scan for leaked secrets')
|
|
134
148
|
.action(require('./actions/scan'))
|
|
135
149
|
|
|
136
|
-
// dotenvx ls
|
|
137
|
-
program.command('ls')
|
|
138
|
-
.description('print all .env files in a tree structure')
|
|
139
|
-
.argument('[directory]', 'directory to list .env files from', '.')
|
|
140
|
-
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
141
|
-
.action(require('./actions/ls'))
|
|
142
|
-
|
|
143
150
|
// dotenvx settings
|
|
144
151
|
program.command('settings')
|
|
145
152
|
.description('print current dotenvx settings')
|
|
@@ -147,6 +154,9 @@ program.command('settings')
|
|
|
147
154
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
148
155
|
.action(require('./actions/settings'))
|
|
149
156
|
|
|
157
|
+
// dotenvx vault
|
|
158
|
+
program.addCommand(require('./commands/vault'))
|
|
159
|
+
|
|
150
160
|
// dotenvx encrypt
|
|
151
161
|
const encryptAction = require('./actions/vault/encrypt')
|
|
152
162
|
program.command('encrypt')
|
|
@@ -156,6 +166,7 @@ program.command('encrypt')
|
|
|
156
166
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
157
167
|
.action(function (...args) {
|
|
158
168
|
logger.warn('DEPRECATION NOTICE: [dotenvx encrypt] has moved. change your command to [dotenvx vault encrypt]')
|
|
169
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx encryptme] will become [dotenvx encrypt] in a 1.0.0 release scheduled for middle of June 2024.')
|
|
159
170
|
|
|
160
171
|
encryptAction.apply(this, args)
|
|
161
172
|
})
|
|
@@ -167,7 +178,7 @@ program.command('decrypt')
|
|
|
167
178
|
.argument('[directory]', 'directory to decrypt', '.')
|
|
168
179
|
.option('-e, --environment <environments...>', 'environment(s) to decrypt')
|
|
169
180
|
.action(function (...args) {
|
|
170
|
-
logger.warn('DEPRECATION
|
|
181
|
+
logger.warn('DEPRECATION NOTICE: [dotenvx decrypt] has moved. change your command to [dotenvx vault decrypt]')
|
|
171
182
|
|
|
172
183
|
decryptAction.apply(this, args)
|
|
173
184
|
})
|
|
@@ -183,9 +194,6 @@ program.command('status')
|
|
|
183
194
|
statusAction.apply(this, args)
|
|
184
195
|
})
|
|
185
196
|
|
|
186
|
-
// dotenvx vault
|
|
187
|
-
program.addCommand(require('./commands/vault'))
|
|
188
|
-
|
|
189
197
|
// dotenvx hub
|
|
190
198
|
program.addCommand(require('./commands/hub'))
|
|
191
199
|
|
|
@@ -31,7 +31,8 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
|
|
|
31
31
|
envSrc,
|
|
32
32
|
keysSrc,
|
|
33
33
|
publicKey: envParsed[publicKeyName],
|
|
34
|
-
privateKey: keysParsed[privateKeyName]
|
|
34
|
+
privateKey: keysParsed[privateKeyName],
|
|
35
|
+
privateKeyAdded: false
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -73,7 +74,8 @@ function findOrCreatePublicKey (envFilepath, envKeysFilepath) {
|
|
|
73
74
|
envSrc,
|
|
74
75
|
keysSrc,
|
|
75
76
|
publicKey,
|
|
76
|
-
privateKey
|
|
77
|
+
privateKey,
|
|
78
|
+
privateKeyAdded: true
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const dotenv = require('dotenv')
|
|
2
|
+
|
|
3
|
+
const isEncrypted = require('./isEncrypted')
|
|
4
|
+
|
|
5
|
+
function isFullyEncrypted (src) {
|
|
6
|
+
const parsed = dotenv.parse(src)
|
|
7
|
+
|
|
8
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
9
|
+
const result = isEncrypted(key, value)
|
|
10
|
+
if (!result) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = isFullyEncrypted
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const ignore = require('ignore')
|
|
3
|
+
|
|
4
|
+
function isIgnoringDotenvKeys () {
|
|
5
|
+
if (!fs.existsSync('.gitignore')) {
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const gitignore = fs.readFileSync('.gitignore').toString()
|
|
10
|
+
const ig = ignore(gitignore).add(gitignore)
|
|
11
|
+
|
|
12
|
+
if (!ig.ignores('.env.keys')) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = isIgnoringDotenvKeys
|
package/src/lib/main.js
CHANGED
|
@@ -3,13 +3,14 @@ const dotenv = require('dotenv')
|
|
|
3
3
|
const dotenvExpand = require('dotenv-expand')
|
|
4
4
|
|
|
5
5
|
// services
|
|
6
|
-
const Encrypt = require('./services/encrypt')
|
|
7
6
|
const Ls = require('./services/ls')
|
|
8
7
|
const Get = require('./services/get')
|
|
9
8
|
const Sets = require('./services/sets')
|
|
10
9
|
const Status = require('./services/status')
|
|
10
|
+
const Encrypt = require('./services/encrypt')
|
|
11
11
|
const Genexample = require('./services/genexample')
|
|
12
12
|
const Settings = require('./services/settings')
|
|
13
|
+
const VaultEncrypt = require('./services/vaultEncrypt')
|
|
13
14
|
|
|
14
15
|
// helpers
|
|
15
16
|
const dotenvEval = require('./helpers/dotenvEval')
|
|
@@ -41,9 +42,13 @@ const parse = function (src) {
|
|
|
41
42
|
return dotenv.parse(src)
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
//
|
|
45
|
+
// DEPRECATED: will became the same function as encryptme
|
|
45
46
|
const encrypt = function (directory, envFile) {
|
|
46
|
-
return new
|
|
47
|
+
return new VaultEncrypt(directory, envFile).run()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const vaultEncrypt = function (directory, envFile) {
|
|
51
|
+
return new VaultEncrypt(directory, envFile).run()
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
const ls = function (directory, envFile) {
|
|
@@ -62,6 +67,10 @@ const set = function (key, value, envFile, encrypt) {
|
|
|
62
67
|
return new Sets(key, value, envFile, encrypt).run()
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
const encryptme = function (envFile) {
|
|
71
|
+
return new Encrypt(envFile).run()
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
const status = function (directory) {
|
|
66
75
|
return new Status(directory).run()
|
|
67
76
|
}
|
|
@@ -96,9 +105,11 @@ module.exports = {
|
|
|
96
105
|
parse,
|
|
97
106
|
// actions related
|
|
98
107
|
encrypt,
|
|
108
|
+
vaultEncrypt,
|
|
99
109
|
ls,
|
|
100
110
|
get,
|
|
101
111
|
set,
|
|
112
|
+
encryptme,
|
|
102
113
|
status,
|
|
103
114
|
genexample,
|
|
104
115
|
// settings
|
|
@@ -2,91 +2,93 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const dotenv = require('dotenv')
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const findOrCreatePublicKey = require('./../helpers/findOrCreatePublicKey')
|
|
6
|
+
const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
|
|
7
|
+
const encryptValue = require('./../helpers/encryptValue')
|
|
8
|
+
const isEncrypted = require('./../helpers/isEncrypted')
|
|
9
|
+
const replace = require('./../helpers/replace')
|
|
7
10
|
|
|
8
11
|
const ENCODING = 'utf8'
|
|
9
12
|
|
|
10
|
-
const findEnvFiles = require('../helpers/findEnvFiles')
|
|
11
|
-
|
|
12
13
|
class Encrypt {
|
|
13
|
-
constructor (
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
|
|
14
|
+
constructor (envFile = '.env') {
|
|
15
|
+
this.envFile = envFile
|
|
16
|
+
this.processedEnvFiles = []
|
|
17
|
+
this.changedFilepaths = new Set()
|
|
18
|
+
this.unchangedFilepaths = new Set()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
run () {
|
|
22
|
-
if (this.envFile.length < 1) {
|
|
23
|
-
const code = 'MISSING_ENV_FILES'
|
|
24
|
-
const message = 'no .env* files found'
|
|
25
|
-
const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx encrypt]'
|
|
26
|
-
|
|
27
|
-
const error = new Error(message)
|
|
28
|
-
error.code = code
|
|
29
|
-
error.help = help
|
|
30
|
-
throw error
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const parsedDotenvKeys = this._parsedDotenvKeys()
|
|
34
|
-
const parsedDotenvVaults = this._parsedDotenvVault()
|
|
35
22
|
const envFilepaths = this._envFilepaths()
|
|
36
|
-
|
|
37
|
-
// build filepaths to be passed to DotenvKeys
|
|
38
|
-
const uniqueEnvFilepaths = new Set()
|
|
39
23
|
for (const envFilepath of envFilepaths) {
|
|
40
|
-
const filepath = path.resolve(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
24
|
+
const filepath = path.resolve(envFilepath)
|
|
25
|
+
|
|
26
|
+
const row = {}
|
|
27
|
+
row.keys = []
|
|
28
|
+
row.filepath = filepath
|
|
29
|
+
row.envFilepath = envFilepath
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// get the original src
|
|
33
|
+
let src = fs.readFileSync(filepath, { encoding: ENCODING })
|
|
34
|
+
// get/generate the public key
|
|
35
|
+
const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
36
|
+
const {
|
|
37
|
+
envSrc,
|
|
38
|
+
publicKey,
|
|
39
|
+
privateKey,
|
|
40
|
+
privateKeyAdded
|
|
41
|
+
} = findOrCreatePublicKey(filepath, envKeysFilepath)
|
|
42
|
+
row.publicKey = publicKey
|
|
43
|
+
row.privateKey = privateKey
|
|
44
|
+
row.privateKeyName = guessPrivateKeyName(filepath)
|
|
45
|
+
row.privateKeyAdded = privateKeyAdded
|
|
46
|
+
|
|
47
|
+
src = envSrc // src was potentially changed by findOrCreatePublicKey so we set it again here
|
|
48
|
+
|
|
49
|
+
// track possible changes
|
|
50
|
+
row.changed = false
|
|
51
|
+
|
|
52
|
+
// iterate over all non-encrypted values and encrypt them
|
|
53
|
+
const parsed = dotenv.parse(src)
|
|
54
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
55
|
+
const encrypted = isEncrypted(key, value)
|
|
56
|
+
if (!encrypted) {
|
|
57
|
+
row.keys.push(key) // track key(s)
|
|
58
|
+
|
|
59
|
+
const encryptedValue = encryptValue(value, publicKey)
|
|
60
|
+
// once newSrc is built write it out
|
|
61
|
+
src = replace(src, key, encryptedValue)
|
|
62
|
+
|
|
63
|
+
row.changed = true // track change
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (row.changed) {
|
|
68
|
+
row.envSrc = src
|
|
69
|
+
this.changedFilepaths.add(envFilepath)
|
|
70
|
+
} else {
|
|
71
|
+
row.envSrc = src
|
|
72
|
+
this.unchangedFilepaths.add(envFilepath)
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (e.code === 'ENOENT') {
|
|
76
|
+
const error = new Error(`missing ${envFilepath} file (${filepath})`)
|
|
77
|
+
error.code = 'MISSING_ENV_FILE'
|
|
78
|
+
|
|
79
|
+
row.error = error
|
|
80
|
+
} else {
|
|
81
|
+
row.error = e
|
|
82
|
+
}
|
|
50
83
|
}
|
|
51
84
|
|
|
52
|
-
|
|
85
|
+
this.processedEnvFiles.push(row)
|
|
53
86
|
}
|
|
54
87
|
|
|
55
|
-
// generate .env.keys string
|
|
56
|
-
const {
|
|
57
|
-
dotenvKeys,
|
|
58
|
-
dotenvKeysFile,
|
|
59
|
-
addedKeys,
|
|
60
|
-
existingKeys
|
|
61
|
-
} = new DotenvKeys([...uniqueEnvFilepaths], parsedDotenvKeys).run()
|
|
62
|
-
|
|
63
|
-
// build look up of .env filepaths and their raw content
|
|
64
|
-
const dotenvFiles = {}
|
|
65
|
-
for (const filepath of [...uniqueEnvFilepaths]) {
|
|
66
|
-
const raw = fs.readFileSync(filepath, ENCODING)
|
|
67
|
-
dotenvFiles[filepath] = raw
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// generate .env.vault string
|
|
71
|
-
const {
|
|
72
|
-
dotenvVaultFile,
|
|
73
|
-
addedVaults,
|
|
74
|
-
existingVaults,
|
|
75
|
-
addedDotenvFilenames
|
|
76
|
-
} = new DotenvVault(dotenvFiles, dotenvKeys, parsedDotenvVaults).run()
|
|
77
|
-
|
|
78
88
|
return {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
addedKeys,
|
|
83
|
-
existingKeys,
|
|
84
|
-
// from DotenvVault
|
|
85
|
-
dotenvVaultFile,
|
|
86
|
-
addedVaults,
|
|
87
|
-
existingVaults,
|
|
88
|
-
addedDotenvFilenames,
|
|
89
|
-
envFile: this.envFile
|
|
89
|
+
processedEnvFiles: this.processedEnvFiles,
|
|
90
|
+
changedFilepaths: [...this.changedFilepaths],
|
|
91
|
+
unchangedFilepaths: [...this.unchangedFilepaths]
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
@@ -97,22 +99,6 @@ class Encrypt {
|
|
|
97
99
|
|
|
98
100
|
return this.envFile
|
|
99
101
|
}
|
|
100
|
-
|
|
101
|
-
_parsedDotenvKeys () {
|
|
102
|
-
const options = {
|
|
103
|
-
path: this.envKeysFilepath
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return dotenv.configDotenv(options).parsed
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
_parsedDotenvVault () {
|
|
110
|
-
const options = {
|
|
111
|
-
path: this.envVaultFilepath
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return dotenv.configDotenv(options).parsed
|
|
115
|
-
}
|
|
116
102
|
}
|
|
117
103
|
|
|
118
104
|
module.exports = Encrypt
|