@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.
@@ -3,7 +3,9 @@ const fs = require('fs')
3
3
  const ignore = require('ignore')
4
4
 
5
5
  const pluralize = require('./../helpers/pluralize')
6
+ const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
6
7
  const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
8
+ const MISSING_GITIGNORE = '.env.keys' // by default only ignore .env.keys. all other .env* files COULD be included - as long as they are encrypted
7
9
 
8
10
  class Precommit {
9
11
  constructor (options = {}) {
@@ -22,16 +24,20 @@ class Precommit {
22
24
  }
23
25
  } else {
24
26
  const warnings = []
27
+ let successMessage = 'success'
28
+ let gitignore = MISSING_GITIGNORE
25
29
 
26
30
  // 1. check for .gitignore file
27
31
  if (!fs.existsSync('.gitignore')) {
28
- const error = new Error('.gitignore missing')
29
- error.help = '? add it with [touch .gitignore]'
30
- throw error
32
+ const warning = new Error('.gitignore missing')
33
+ warning.help = '? add it with [touch .gitignore]'
34
+ warnings.push(warning)
35
+ } else {
36
+ gitignore = fs.readFileSync('.gitignore').toString()
31
37
  }
32
38
 
33
39
  // 2. check .env* files against .gitignore file
34
- const ig = ignore().add(fs.readFileSync('.gitignore').toString())
40
+ const ig = ignore().add(gitignore)
35
41
  const files = fs.readdirSync(process.cwd())
36
42
  const dotenvFiles = files.filter(file => file.match(/^\.env(\..+)?$/))
37
43
  dotenvFiles.forEach(file => {
@@ -44,14 +50,19 @@ class Precommit {
44
50
  }
45
51
  } else {
46
52
  if (file !== '.env.example' && file !== '.env.vault') {
47
- const error = new Error(`${file} not properly gitignored`)
48
- error.help = `? add ${file} to .gitignore with [echo ".env*" >> .gitignore]`
49
- throw error
53
+ const src = fs.readFileSync(file).toString()
54
+ const encrypted = isFullyEncrypted(src)
55
+
56
+ // if contents are encrypted don't raise an error
57
+ if (!encrypted) {
58
+ const error = new Error(`${file} not encrypted (or not gitignored)`)
59
+ error.help = `? encrypt it with [dotenvx protect -f ${file}] or add ${file} to .gitignore with [echo ".env*" >> .gitignore]`
60
+ throw error
61
+ }
50
62
  }
51
63
  }
52
64
  })
53
65
 
54
- let successMessage = 'success'
55
66
  if (warnings.length > 0) {
56
67
  successMessage = `success (with ${pluralize('warning', warnings.length)})`
57
68
  }
@@ -1,7 +1,10 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
+ const dotenv = require('dotenv')
5
+
4
6
  const findOrCreatePublicKey = require('./../helpers/findOrCreatePublicKey')
7
+ const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
5
8
  const encryptValue = require('./../helpers/encryptValue')
6
9
  const replace = require('./../helpers/replace')
7
10
 
@@ -14,39 +17,57 @@ class Sets {
14
17
  this.envFile = envFile
15
18
  this.encrypt = encrypt
16
19
 
17
- this.publicKey = null
18
20
  this.processedEnvFiles = []
19
- this.settableFilepaths = new Set()
21
+ this.changedFilepaths = new Set()
22
+ this.unchangedFilepaths = new Set()
20
23
  }
21
24
 
22
25
  run () {
23
26
  const envFilepaths = this._envFilepaths()
24
27
  for (const envFilepath of envFilepaths) {
28
+ const filepath = path.resolve(envFilepath)
29
+
25
30
  const row = {}
26
31
  row.key = this.key
27
- row.filepath = envFilepath
28
32
  row.value = this.value
33
+ row.filepath = filepath
34
+ row.envFilepath = envFilepath
35
+ row.changed = false
29
36
 
30
- const filepath = path.resolve(envFilepath)
31
37
  try {
32
38
  let value = this.value
33
39
  let src = fs.readFileSync(filepath, { encoding: ENCODING })
40
+ row.originalValue = dotenv.parse(src)[row.key] || null
41
+
34
42
  if (this.encrypt) {
35
43
  const envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
36
44
  const {
45
+ envSrc,
37
46
  publicKey,
38
- envSrc
47
+ privateKey,
48
+ privateKeyAdded
39
49
  } = findOrCreatePublicKey(filepath, envKeysFilepath)
40
50
  src = envSrc // overwrite the original read (because findOrCreatePublicKey) rewrite to it
41
51
  value = encryptValue(value, publicKey)
42
- row.encryptedValue = value // useful
52
+
53
+ row.changed = true // track change
54
+ row.encryptedValue = value
43
55
  row.publicKey = publicKey
56
+ row.privateKey = privateKey
57
+ row.privateKeyAdded = privateKeyAdded
58
+ row.privateKeyName = guessPrivateKeyName(filepath)
44
59
  }
45
60
 
46
- const newSrc = replace(src, this.key, value)
47
- fs.writeFileSync(filepath, newSrc)
61
+ if (value !== row.originalValue) {
62
+ row.envSrc = replace(src, this.key, value)
63
+
64
+ this.changedFilepaths.add(envFilepath)
65
+ row.changed = true
66
+ } else {
67
+ row.envSrc = src
48
68
 
49
- this.settableFilepaths.add(envFilepath)
69
+ this.unchangedFilepaths.add(envFilepath)
70
+ }
50
71
  } catch (e) {
51
72
  if (e.code === 'ENOENT') {
52
73
  const error = new Error(`missing ${envFilepath} file (${filepath})`)
@@ -63,7 +84,8 @@ class Sets {
63
84
 
64
85
  return {
65
86
  processedEnvFiles: this.processedEnvFiles,
66
- settableFilepaths: [...this.settableFilepaths]
87
+ changedFilepaths: [...this.changedFilepaths],
88
+ unchangedFilepaths: [...this.unchangedFilepaths]
67
89
  }
68
90
  }
69
91
 
@@ -0,0 +1,118 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const dotenv = require('dotenv')
4
+
5
+ const DotenvKeys = require('./../helpers/dotenvKeys')
6
+ const DotenvVault = require('./../helpers/dotenvVault')
7
+
8
+ const ENCODING = 'utf8'
9
+
10
+ const findEnvFiles = require('../helpers/findEnvFiles')
11
+
12
+ class VaultEncrypt {
13
+ constructor (directory = '.', envFile) {
14
+ this.directory = directory
15
+ this.envFile = envFile || findEnvFiles(directory)
16
+ // calculated
17
+ this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
18
+ this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
19
+ }
20
+
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 vault 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
+ const envFilepaths = this._envFilepaths()
36
+
37
+ // build filepaths to be passed to DotenvKeys
38
+ const uniqueEnvFilepaths = new Set()
39
+ for (const envFilepath of envFilepaths) {
40
+ const filepath = path.resolve(this.directory, envFilepath)
41
+ if (!fs.existsSync(filepath)) {
42
+ const code = 'MISSING_ENV_FILE'
43
+ const message = `file does not exist at [${filepath}]`
44
+ const help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx vault encrypt]`
45
+
46
+ const error = new Error(message)
47
+ error.code = code
48
+ error.help = help
49
+ throw error
50
+ }
51
+
52
+ uniqueEnvFilepaths.add(filepath)
53
+ }
54
+
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
+ return {
79
+ // from DotenvKeys
80
+ dotenvKeys,
81
+ dotenvKeysFile,
82
+ addedKeys,
83
+ existingKeys,
84
+ // from DotenvVault
85
+ dotenvVaultFile,
86
+ addedVaults,
87
+ existingVaults,
88
+ addedDotenvFilenames,
89
+ envFile: this.envFile
90
+ }
91
+ }
92
+
93
+ _envFilepaths () {
94
+ if (!Array.isArray(this.envFile)) {
95
+ return [this.envFile]
96
+ }
97
+
98
+ return this.envFile
99
+ }
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
+ }
117
+
118
+ module.exports = VaultEncrypt