@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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.11.0",
2
+ "version": "0.13.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -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
- class AppendToIgnores {
59
- run () {
60
- new Docker().run()
61
- new Git().run()
62
- new Npm().run()
63
- new Vercel().run()
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 = { AppendToIgnores, Generic }
82
+ module.exports = gitignore
@@ -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(`? in development: add one with [echo "HELLO=World" > .env] and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
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
@@ -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
 
@@ -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
  }