@dotenvx/dotenvx 1.59.1 → 1.60.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 +18 -1
- package/README.md +14 -4
- package/package.json +7 -6
- package/src/cli/actions/decrypt.js +15 -8
- package/src/cli/actions/encrypt.js +22 -17
- package/src/cli/actions/ext/genexample.js +2 -2
- package/src/cli/actions/ext/gitignore.js +3 -3
- package/src/cli/actions/get.js +12 -7
- package/src/cli/actions/keypair.js +13 -5
- package/src/cli/actions/rotate.js +26 -14
- package/src/cli/actions/run.js +13 -6
- package/src/cli/actions/set.js +31 -17
- package/src/cli/dotenvx.js +19 -21
- package/src/cli/examples.js +1 -1
- package/src/db/session.js +10 -6
- package/src/lib/extensions/ops.js +112 -63
- package/src/lib/helpers/catchAndLog.js +1 -1
- package/src/lib/helpers/createSpinner.js +24 -0
- package/src/lib/helpers/cryptography/index.js +4 -0
- package/src/lib/helpers/cryptography/mutateKeysSrc.js +4 -4
- package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +38 -0
- package/src/lib/helpers/cryptography/opsKeypair.js +2 -2
- package/src/lib/helpers/cryptography/opsKeypairSync.js +14 -0
- package/src/lib/helpers/cryptography/provision.js +15 -11
- package/src/lib/helpers/cryptography/provisionSync.js +51 -0
- package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +1 -1
- package/src/lib/helpers/detectEncoding.js +2 -2
- package/src/lib/helpers/detectEncodingSync.js +22 -0
- package/src/lib/helpers/errors.js +15 -0
- package/src/lib/helpers/fsx.js +27 -3
- package/src/lib/helpers/installPrecommitHook.js +2 -2
- package/src/lib/helpers/isIgnoringDotenvKeys.js +1 -1
- package/src/lib/helpers/keyResolution/index.js +3 -1
- package/src/lib/helpers/keyResolution/keyValues.js +12 -12
- package/src/lib/helpers/keyResolution/keyValuesSync.js +85 -0
- package/src/lib/helpers/keyResolution/readFileKey.js +10 -8
- package/src/lib/helpers/keyResolution/readFileKeySync.js +15 -0
- package/src/lib/helpers/kits/sample.js +9 -21
- package/src/lib/main.d.ts +20 -4
- package/src/lib/main.js +30 -22
- package/src/lib/services/decrypt.js +8 -8
- package/src/lib/services/encrypt.js +19 -12
- package/src/lib/services/genexample.js +2 -2
- package/src/lib/services/get.js +30 -21
- package/src/lib/services/keypair.js +21 -5
- package/src/lib/services/prebuild.js +7 -12
- package/src/lib/services/precommit.js +7 -11
- package/src/lib/services/rotate.js +26 -20
- package/src/lib/services/run.js +82 -9
- package/src/lib/services/sets.js +142 -13
- package/src/shared/logger.js +3 -3
- package/src/lib/helpers/sleep.js +0 -5
package/src/lib/services/sets.js
CHANGED
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
13
|
keyNames,
|
|
14
|
-
keyValues
|
|
14
|
+
keyValues,
|
|
15
|
+
keyValuesSync
|
|
15
16
|
} = require('./../helpers/keyResolution')
|
|
16
17
|
|
|
17
18
|
const {
|
|
@@ -19,21 +20,23 @@ const {
|
|
|
19
20
|
decryptKeyValue,
|
|
20
21
|
isEncrypted,
|
|
21
22
|
provision,
|
|
23
|
+
provisionSync,
|
|
22
24
|
provisionWithPrivateKey
|
|
23
25
|
} = require('./../helpers/cryptography')
|
|
24
26
|
|
|
25
27
|
const replace = require('./../helpers/replace')
|
|
26
28
|
const dotenvParse = require('./../helpers/dotenvParse')
|
|
27
29
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
30
|
+
const detectEncodingSync = require('./../helpers/detectEncodingSync')
|
|
28
31
|
|
|
29
32
|
class Sets {
|
|
30
|
-
constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null,
|
|
33
|
+
constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null, noOps = false, noCreate = false) {
|
|
31
34
|
this.envs = determine(envs, process.env)
|
|
32
35
|
this.key = key
|
|
33
36
|
this.value = value
|
|
34
37
|
this.encrypt = encrypt
|
|
35
38
|
this.envKeysFilepath = envKeysFilepath
|
|
36
|
-
this.
|
|
39
|
+
this.noOps = noOps
|
|
37
40
|
this.noCreate = noCreate
|
|
38
41
|
|
|
39
42
|
this.processedEnvs = []
|
|
@@ -42,7 +45,7 @@ class Sets {
|
|
|
42
45
|
this.readableFilepaths = new Set()
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
runSync () {
|
|
46
49
|
// example
|
|
47
50
|
// envs [
|
|
48
51
|
// { type: 'envFile', value: '.env' }
|
|
@@ -50,7 +53,7 @@ class Sets {
|
|
|
50
53
|
|
|
51
54
|
for (const env of this.envs) {
|
|
52
55
|
if (env.type === TYPE_ENV_FILE) {
|
|
53
|
-
this.
|
|
56
|
+
this._setEnvFileSync(env.value)
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
@@ -61,7 +64,26 @@ class Sets {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
async run () {
|
|
68
|
+
// example
|
|
69
|
+
// envs [
|
|
70
|
+
// { type: 'envFile', value: '.env' }
|
|
71
|
+
// ]
|
|
72
|
+
|
|
73
|
+
for (const env of this.envs) {
|
|
74
|
+
if (env.type === TYPE_ENV_FILE) {
|
|
75
|
+
await this._setEnvFile(env.value)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
processedEnvs: this.processedEnvs,
|
|
81
|
+
changedFilepaths: [...this.changedFilepaths],
|
|
82
|
+
unchangedFilepaths: [...this.unchangedFilepaths]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_setEnvFileSync (envFilepath) {
|
|
65
87
|
const row = {}
|
|
66
88
|
row.key = this.key || null
|
|
67
89
|
row.value = this.value || null
|
|
@@ -77,14 +99,120 @@ class Sets {
|
|
|
77
99
|
|
|
78
100
|
if (!fsx.existsSync(filepath)) {
|
|
79
101
|
if (this.noCreate) {
|
|
80
|
-
|
|
102
|
+
detectEncodingSync(filepath) // throws ENOENT
|
|
103
|
+
} else {
|
|
104
|
+
fsx.writeFileXSync(filepath, '')
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const encoding = detectEncodingSync(filepath)
|
|
109
|
+
let envSrc = fsx.readFileXSync(filepath, { encoding })
|
|
110
|
+
|
|
111
|
+
// blank files seeded by `set` should contain only the key being set
|
|
112
|
+
if (row.key && envSrc.trim().length === 0) {
|
|
113
|
+
envSrc = `${row.key}="${this.value}"\n`
|
|
114
|
+
seededWithInitialKey = true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const envParsed = dotenvParse(envSrc)
|
|
118
|
+
row.originalValue = envParsed[row.key] || null
|
|
119
|
+
if (seededWithInitialKey) {
|
|
120
|
+
row.originalValue = null
|
|
121
|
+
}
|
|
122
|
+
const wasPlainText = !isEncrypted(row.originalValue)
|
|
123
|
+
this.readableFilepaths.add(envFilepath)
|
|
124
|
+
|
|
125
|
+
if (this.encrypt) {
|
|
126
|
+
let publicKey
|
|
127
|
+
let privateKey
|
|
128
|
+
|
|
129
|
+
const { publicKeyName, privateKeyName } = keyNames(filepath)
|
|
130
|
+
const { publicKeyValue, privateKeyValue } = keyValuesSync(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
131
|
+
|
|
132
|
+
// first pass - provisionSync
|
|
133
|
+
if (!privateKeyValue && !publicKeyValue) {
|
|
134
|
+
const prov = provisionSync({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
135
|
+
envSrc = prov.envSrc
|
|
136
|
+
publicKey = prov.publicKey
|
|
137
|
+
privateKey = prov.privateKey
|
|
138
|
+
row.envKeysFilepath = prov.envKeysFilepath
|
|
139
|
+
row.localPrivateKeyAdded = prov.localPrivateKeyAdded
|
|
140
|
+
row.remotePrivateKeyAdded = prov.remotePrivateKeyAdded
|
|
141
|
+
} else if (privateKeyValue) {
|
|
142
|
+
const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
|
|
143
|
+
publicKey = prov.publicKey
|
|
144
|
+
privateKey = prov.privateKey
|
|
145
|
+
envSrc = prov.envSrc
|
|
146
|
+
|
|
147
|
+
if (row.originalValue) {
|
|
148
|
+
row.originalValue = decryptKeyValue(row.key, row.originalValue, privateKeyName, privateKey)
|
|
149
|
+
}
|
|
150
|
+
} else if (publicKeyValue) {
|
|
151
|
+
publicKey = publicKeyValue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
row.publicKey = publicKey
|
|
155
|
+
row.privateKey = privateKey
|
|
156
|
+
try {
|
|
157
|
+
row.encryptedValue = encryptValue(this.value, publicKey)
|
|
158
|
+
} catch {
|
|
159
|
+
throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
|
|
160
|
+
}
|
|
161
|
+
row.privateKeyName = privateKeyName
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const goingFromPlainTextToEncrypted = wasPlainText && this.encrypt
|
|
165
|
+
const valueChanged = this.value !== row.originalValue
|
|
166
|
+
const shouldPersistSeededPlainValue = seededWithInitialKey && !this.encrypt
|
|
167
|
+
|
|
168
|
+
if (shouldPersistSeededPlainValue) {
|
|
169
|
+
row.envSrc = envSrc
|
|
170
|
+
this.changedFilepaths.add(envFilepath)
|
|
171
|
+
row.changed = true
|
|
172
|
+
} else if (goingFromPlainTextToEncrypted || valueChanged) {
|
|
173
|
+
row.envSrc = replace(envSrc, this.key, row.encryptedValue || this.value)
|
|
174
|
+
this.changedFilepaths.add(envFilepath)
|
|
175
|
+
row.changed = true
|
|
176
|
+
} else {
|
|
177
|
+
row.envSrc = envSrc
|
|
178
|
+
this.unchangedFilepaths.add(envFilepath)
|
|
179
|
+
row.changed = false
|
|
180
|
+
}
|
|
181
|
+
} catch (e) {
|
|
182
|
+
if (e.code === 'ENOENT') {
|
|
183
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
184
|
+
} else {
|
|
185
|
+
row.error = e
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.processedEnvs.push(row)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async _setEnvFile (envFilepath) {
|
|
193
|
+
const row = {}
|
|
194
|
+
row.key = this.key || null
|
|
195
|
+
row.value = this.value || null
|
|
196
|
+
row.type = TYPE_ENV_FILE
|
|
197
|
+
|
|
198
|
+
const filepath = path.resolve(envFilepath)
|
|
199
|
+
row.filepath = filepath
|
|
200
|
+
row.envFilepath = envFilepath
|
|
201
|
+
row.changed = false
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
let seededWithInitialKey = false
|
|
205
|
+
|
|
206
|
+
if (!(await fsx.exists(filepath))) {
|
|
207
|
+
if (this.noCreate) {
|
|
208
|
+
await detectEncoding(filepath) // throws ENOENT
|
|
81
209
|
} else {
|
|
82
|
-
fsx.writeFileX(filepath, '')
|
|
210
|
+
await fsx.writeFileX(filepath, '')
|
|
83
211
|
}
|
|
84
212
|
}
|
|
85
213
|
|
|
86
|
-
const encoding = detectEncoding(filepath)
|
|
87
|
-
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
214
|
+
const encoding = await detectEncoding(filepath)
|
|
215
|
+
let envSrc = await fsx.readFileX(filepath, { encoding })
|
|
88
216
|
|
|
89
217
|
// blank files seeded by `set` should contain only the key being set
|
|
90
218
|
if (row.key && envSrc.trim().length === 0) {
|
|
@@ -105,16 +233,17 @@ class Sets {
|
|
|
105
233
|
let privateKey
|
|
106
234
|
|
|
107
235
|
const { publicKeyName, privateKeyName } = keyNames(filepath)
|
|
108
|
-
const { publicKeyValue, privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath,
|
|
236
|
+
const { publicKeyValue, privateKeyValue } = await keyValues(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
109
237
|
|
|
110
238
|
// first pass - provision
|
|
111
239
|
if (!privateKeyValue && !publicKeyValue) {
|
|
112
|
-
const prov = provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath,
|
|
240
|
+
const prov = await provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
113
241
|
envSrc = prov.envSrc
|
|
114
242
|
publicKey = prov.publicKey
|
|
115
243
|
privateKey = prov.privateKey
|
|
116
|
-
row.privateKeyAdded = prov.privateKeyAdded
|
|
117
244
|
row.envKeysFilepath = prov.envKeysFilepath
|
|
245
|
+
row.localPrivateKeyAdded = prov.localPrivateKeyAdded
|
|
246
|
+
row.remotePrivateKeyAdded = prov.remotePrivateKeyAdded
|
|
118
247
|
} else if (privateKeyValue) {
|
|
119
248
|
const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
|
|
120
249
|
publicKey = prov.publicKey
|
package/src/shared/logger.js
CHANGED
|
@@ -20,8 +20,8 @@ const success = getColor('amber')
|
|
|
20
20
|
const successv = (m) => getColor('amber')(`⟐ ${m}`)
|
|
21
21
|
const info = getColor('gray')
|
|
22
22
|
const help = getColor('dodgerblue')
|
|
23
|
-
const verbose = getColor('plum')
|
|
24
|
-
const debug = getColor('plum')
|
|
23
|
+
const verbose = (m) => getColor('plum')(`┆ ${m}`)
|
|
24
|
+
const debug = (m) => getColor('plum')(`┆ ${m}`)
|
|
25
25
|
|
|
26
26
|
let currentLevel = levels.info // default log level
|
|
27
27
|
let currentName = 'dotenvx' // default logger name
|
|
@@ -121,7 +121,7 @@ function setLogLevel (options) {
|
|
|
121
121
|
logger.setLevel(logLevel)
|
|
122
122
|
// Only log which level it's setting if it's not set to quiet mode
|
|
123
123
|
if (!options.quiet || (options.quiet && logLevel !== 'error')) {
|
|
124
|
-
logger.debug(`
|
|
124
|
+
logger.debug(`setting log level to: ${logLevel}`)
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|