@dotenvx/dotenvx 0.11.0 → 0.13.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/package.json
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
const main = require('./../../lib/main')
|
|
4
|
+
const logger = require('./../../shared/logger')
|
|
5
|
+
const helpers = require('./../helpers')
|
|
6
|
+
const createSpinner = require('./../../shared/createSpinner')
|
|
7
|
+
|
|
8
|
+
const spinner = createSpinner('decrypting')
|
|
9
|
+
|
|
10
|
+
// constants
|
|
11
|
+
const ENCODING = 'utf8'
|
|
12
|
+
|
|
13
|
+
async function decrypt () {
|
|
14
|
+
spinner.start()
|
|
15
|
+
await helpers.sleep(500) // better dx
|
|
16
|
+
|
|
17
|
+
const options = this.opts()
|
|
18
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
19
|
+
|
|
20
|
+
const vaultFilepath = helpers.resolvePath('.env.vault')
|
|
21
|
+
const keysFilepath = helpers.resolvePath('.env.keys')
|
|
22
|
+
const changedEnvFilenames = new Set()
|
|
23
|
+
const unchangedEnvFilenames = new Set()
|
|
24
|
+
|
|
25
|
+
// logger.verbose(`checking for .env.vault`)
|
|
26
|
+
if (!fs.existsSync(vaultFilepath)) {
|
|
27
|
+
spinner.fail(`.env.vault file missing: ${vaultFilepath}`)
|
|
28
|
+
logger.help('? generate one with [dotenvx encrypt]')
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// logger.verbose(`checking for .env.keys`)
|
|
33
|
+
if (!fs.existsSync(keysFilepath)) {
|
|
34
|
+
spinner.fail(`.env.keys file missing: ${keysFilepath}`)
|
|
35
|
+
logger.help('? a .env.keys file must be present in order to decrypt your .env.vault contents to .env file(s)')
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const dotenvKeys = (main.configDotenv({ path: keysFilepath }).parsed || {})
|
|
40
|
+
const dotenvVault = (main.configDotenv({ path: vaultFilepath }).parsed || {})
|
|
41
|
+
|
|
42
|
+
Object.entries(dotenvKeys).forEach(([dotenvKey, value]) => {
|
|
43
|
+
// determine environment
|
|
44
|
+
const environment = dotenvKey.replace('DOTENV_KEY_', '').toLowerCase()
|
|
45
|
+
// determine corresponding vault key
|
|
46
|
+
const vaultKey = `DOTENV_VAULT_${environment.toUpperCase()}`
|
|
47
|
+
|
|
48
|
+
// attempt to find ciphertext
|
|
49
|
+
const ciphertext = dotenvVault[vaultKey]
|
|
50
|
+
|
|
51
|
+
// give warning if not found
|
|
52
|
+
if (ciphertext && ciphertext.length >= 1) {
|
|
53
|
+
const key = helpers._parseEncryptionKeyFromDotenvKey(value.trim())
|
|
54
|
+
|
|
55
|
+
// Decrypt
|
|
56
|
+
const decrypted = main.decrypt(ciphertext, key)
|
|
57
|
+
|
|
58
|
+
// envFilename
|
|
59
|
+
let envFilename = `.env.${environment}`
|
|
60
|
+
if (environment === 'development') {
|
|
61
|
+
envFilename = '.env'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// check if exists
|
|
65
|
+
if (fs.existsSync(envFilename) && (fs.readFileSync(envFilename, { encoding: ENCODING }).toString() === decrypted)) {
|
|
66
|
+
unchangedEnvFilenames.add(envFilename)
|
|
67
|
+
} else {
|
|
68
|
+
changedEnvFilenames.add(envFilename)
|
|
69
|
+
fs.writeFileSync(envFilename, decrypted)
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
logger.warn(`${vaultKey} missing in .env.vault: ${vaultFilepath}`)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
let changedMsg = ''
|
|
77
|
+
if (changedEnvFilenames.size > 0) {
|
|
78
|
+
changedMsg = `decrypted (${Array.from(changedEnvFilenames).join(',')})`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let unchangedMsg = ''
|
|
82
|
+
if (unchangedEnvFilenames.size > 0) {
|
|
83
|
+
unchangedMsg = `no changes (${Array.from(unchangedEnvFilenames).join(',')})`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (changedMsg.length > 0) {
|
|
87
|
+
spinner.succeed(`${changedMsg} ${unchangedMsg}`)
|
|
88
|
+
} else {
|
|
89
|
+
spinner.done(`${unchangedMsg}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = decrypt
|
|
@@ -150,19 +150,6 @@ async function encrypt () {
|
|
|
150
150
|
|
|
151
151
|
logger.help2(`ℹ run [DOTENV_KEY='${tryKey}' dotenvx run -- yourcommand] to test decryption locally`)
|
|
152
152
|
}
|
|
153
|
-
|
|
154
|
-
// logger.verbose('')
|
|
155
|
-
// logger.verbose('next:')
|
|
156
|
-
// logger.verbose('')
|
|
157
|
-
// logger.verbose(' 1. commit .env.vault safely to code')
|
|
158
|
-
// logger.verbose(' 2. set DOTENV_KEY on server (or ci)')
|
|
159
|
-
// logger.verbose(' 3. push your code')
|
|
160
|
-
// logger.verbose('')
|
|
161
|
-
// logger.verbose('protips:')
|
|
162
|
-
// logger.verbose('')
|
|
163
|
-
// logger.verbose(' * .env.keys file holds your decryption DOTENV_KEYs')
|
|
164
|
-
// logger.verbose(' * DO NOT commit .env.keys to code')
|
|
165
|
-
// logger.verbose(' * share .env.keys file over secure channels only')
|
|
166
153
|
}
|
|
167
154
|
|
|
168
155
|
module.exports = encrypt
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
|
|
3
3
|
const FORMATS = ['.env*', '!.env.vault']
|
|
4
|
+
const logger = require('./../../shared/logger')
|
|
4
5
|
|
|
5
6
|
class Generic {
|
|
6
7
|
constructor (filename, touchFile = false) {
|
|
@@ -16,6 +17,8 @@ class Generic {
|
|
|
16
17
|
run () {
|
|
17
18
|
if (!fs.existsSync(this.filename)) {
|
|
18
19
|
if (this.touchFile === true) {
|
|
20
|
+
logger.info(`creating ${this.filename}`)
|
|
21
|
+
|
|
19
22
|
fs.writeFileSync(this.filename, '')
|
|
20
23
|
} else {
|
|
21
24
|
return
|
|
@@ -25,6 +28,8 @@ class Generic {
|
|
|
25
28
|
const lines = fs.readFileSync(this.filename, 'utf8').split(/\r?\n/)
|
|
26
29
|
this.formats.forEach(format => {
|
|
27
30
|
if (!lines.includes(format.trim())) {
|
|
31
|
+
logger.info(`appending ${format} to ${this.filename}`)
|
|
32
|
+
|
|
28
33
|
this.append(format)
|
|
29
34
|
}
|
|
30
35
|
})
|
|
@@ -55,13 +60,23 @@ class Vercel {
|
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
function gitignore () {
|
|
64
|
+
const options = this.opts()
|
|
65
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
66
|
+
|
|
67
|
+
logger.verbose('appending to .gitignore')
|
|
68
|
+
new Git().run()
|
|
69
|
+
|
|
70
|
+
logger.verbose('appending to .dockerignore (if existing)')
|
|
71
|
+
new Docker().run()
|
|
72
|
+
|
|
73
|
+
logger.verbose('appending to .npmignore (if existing)')
|
|
74
|
+
new Npm().run()
|
|
75
|
+
|
|
76
|
+
logger.verbose('appending to .vercelignore (if existing)')
|
|
77
|
+
new Vercel().run()
|
|
78
|
+
|
|
79
|
+
logger.success('done')
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
module.exports =
|
|
82
|
+
module.exports = gitignore
|
package/src/cli/actions/run.js
CHANGED
|
@@ -84,11 +84,18 @@ async function run () {
|
|
|
84
84
|
readableFilepaths.add(envFilepath)
|
|
85
85
|
result.injected.forEach(key => injected.add(key))
|
|
86
86
|
} catch (e) {
|
|
87
|
+
// calculate development help message depending on state of repo
|
|
88
|
+
const vaultFilepath = helpers.resolvePath('.env.vault')
|
|
89
|
+
let developmentHelp = `? in development: add one with [echo "HELLO=World" > .env] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`
|
|
90
|
+
if (fs.existsSync(vaultFilepath)) {
|
|
91
|
+
developmentHelp = `? in development: use [dotenvx decrypt] to decrypt .env.vault to .env and then re-run [dotenvx run -- ${commandArgs.join(' ')}]`
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
switch (e.code) {
|
|
88
95
|
// missing .env
|
|
89
96
|
case 'ENOENT':
|
|
90
97
|
logger.warnv(`missing ${envFilepath} file (${filepath})`)
|
|
91
|
-
logger.help(
|
|
98
|
+
logger.help(developmentHelp)
|
|
92
99
|
logger.help('? for production: set [DOTENV_KEY] on your server and re-deploy')
|
|
93
100
|
logger.help('? for ci: set [DOTENV_KEY] on your ci and re-build')
|
|
94
101
|
break
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -66,6 +66,12 @@ program.command('encrypt')
|
|
|
66
66
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', helpers.findEnvFiles('./'))
|
|
67
67
|
.action(require('./actions/encrypt'))
|
|
68
68
|
|
|
69
|
+
// dotenvx decrypt
|
|
70
|
+
program.command('decrypt')
|
|
71
|
+
.description('decrypt .env.vault to .env*')
|
|
72
|
+
.addHelpText('after', examples.encrypt)
|
|
73
|
+
.action(require('./actions/decrypt'))
|
|
74
|
+
|
|
69
75
|
// dotenvx precommit
|
|
70
76
|
program.command('precommit')
|
|
71
77
|
.description('prevent committing .env files to code')
|
|
@@ -79,6 +85,12 @@ program.command('prebuild')
|
|
|
79
85
|
.addHelpText('after', examples.prebuild)
|
|
80
86
|
.action(require('./actions/prebuild'))
|
|
81
87
|
|
|
88
|
+
// dotenvx gitignore
|
|
89
|
+
program.command('gitignore')
|
|
90
|
+
.description('append to .gitignore file (and if existing, .dockerignore, .npmignore, and .vercelignore)')
|
|
91
|
+
.addHelpText('after', examples.gitignore)
|
|
92
|
+
.action(require('./actions/gitignore'))
|
|
93
|
+
|
|
82
94
|
// dotenvx hub
|
|
83
95
|
program.addCommand(require('./commands/hub'))
|
|
84
96
|
|
package/src/cli/examples.js
CHANGED
|
@@ -83,9 +83,27 @@ Try it:
|
|
|
83
83
|
`
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
const gitignore = function () {
|
|
87
|
+
return `
|
|
88
|
+
Examples:
|
|
89
|
+
|
|
90
|
+
\`\`\`
|
|
91
|
+
$ dotenvx gitignore
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
Try it:
|
|
95
|
+
|
|
96
|
+
\`\`\`
|
|
97
|
+
$ dotenvx gitignore
|
|
98
|
+
done
|
|
99
|
+
\`\`\`
|
|
100
|
+
`
|
|
101
|
+
}
|
|
102
|
+
|
|
86
103
|
module.exports = {
|
|
87
104
|
run,
|
|
88
105
|
encrypt,
|
|
89
106
|
precommit,
|
|
90
|
-
prebuild
|
|
107
|
+
prebuild,
|
|
108
|
+
gitignore
|
|
91
109
|
}
|