@dotenvx/dotenvx 1.75.1 → 2.1.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +4 -114
  3. package/package.json +8 -7
  4. package/src/cli/actions/decrypt.js +35 -17
  5. package/src/cli/actions/doctor.js +2 -2
  6. package/src/cli/actions/encrypt.js +64 -85
  7. package/src/cli/actions/ext/genexample.js +2 -2
  8. package/src/cli/actions/get.js +22 -7
  9. package/src/cli/actions/keypair.js +55 -42
  10. package/src/cli/actions/login.js +4 -3
  11. package/src/cli/actions/normalizeArmorAliases.js +9 -0
  12. package/src/cli/actions/run.js +65 -22
  13. package/src/cli/actions/set.js +87 -37
  14. package/src/cli/dotenvx.js +8 -23
  15. package/src/lib/api/{postKeypair.js → postArmorKeyring.js} +4 -14
  16. package/src/lib/{helpers/envResolution → conventions}/environment.js +1 -1
  17. package/src/lib/conventions/filepaths.js +9 -0
  18. package/src/lib/conventions/keynames.js +38 -0
  19. package/src/lib/helpers/catchAndLog.js +4 -1
  20. package/src/lib/helpers/cryptography/index.js +2 -9
  21. package/src/lib/helpers/cryptography/{isPublicKey.js → isDotenvPublicKey.js} +2 -2
  22. package/src/lib/helpers/cryptography/isPlainKey.js +7 -0
  23. package/src/lib/helpers/cryptography/mutateKeysSrc.js +5 -21
  24. package/src/lib/helpers/envResolution/index.js +1 -1
  25. package/src/lib/helpers/errors.js +26 -0
  26. package/src/lib/helpers/keypairMetadata.js +1 -1
  27. package/src/lib/helpers/prompts.js +46 -0
  28. package/src/lib/main.d.ts +2 -39
  29. package/src/lib/main.js +83 -46
  30. package/src/lib/providers/armor/index.js +29 -0
  31. package/src/lib/providers/index.js +46 -0
  32. package/src/lib/providers/worker.js +6 -0
  33. package/src/lib/resolvers/envs.js +302 -0
  34. package/src/lib/resolvers/get.js +69 -0
  35. package/src/lib/resolvers/keypair.js +81 -0
  36. package/src/lib/resolvers/ls.js +41 -0
  37. package/src/lib/services/armorDown.js +2 -2
  38. package/src/lib/services/{armorKeypair.js → armorKeyring.js} +14 -51
  39. package/src/lib/services/armorMove.js +2 -2
  40. package/src/lib/services/armorPull.js +2 -2
  41. package/src/lib/services/armorPush.js +2 -2
  42. package/src/lib/services/armorUp.js +2 -2
  43. package/src/lib/services/genexample.js +5 -6
  44. package/src/lib/services/login.js +25 -0
  45. package/src/lib/services/prebuild.js +9 -3
  46. package/src/lib/services/precommit.js +9 -3
  47. package/src/lib/transforms/decrypt.js +83 -0
  48. package/src/lib/transforms/encrypt.js +178 -0
  49. package/src/lib/transforms/set.js +179 -0
  50. package/src/shared/logger.js +5 -0
  51. package/src/cli/actions/normalizeArmorOptions.js +0 -10
  52. package/src/cli/actions/rotate.js +0 -87
  53. package/src/lib/helpers/append.js +0 -62
  54. package/src/lib/helpers/cryptography/armorKeypair.js +0 -42
  55. package/src/lib/helpers/cryptography/armorKeypairSync.js +0 -55
  56. package/src/lib/helpers/cryptography/encryptValue.js +0 -7
  57. package/src/lib/helpers/cryptography/localKeypair.js +0 -14
  58. package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +0 -38
  59. package/src/lib/helpers/cryptography/provision.js +0 -56
  60. package/src/lib/helpers/cryptography/provisionSync.js +0 -51
  61. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +0 -26
  62. package/src/lib/helpers/escapeDollarSigns.js +0 -5
  63. package/src/lib/helpers/escapeForRegex.js +0 -5
  64. package/src/lib/helpers/evalKeyValue.js +0 -23
  65. package/src/lib/helpers/isIgnoringDotenvKeys.js +0 -19
  66. package/src/lib/helpers/keyResolution/index.js +0 -16
  67. package/src/lib/helpers/keyResolution/keyNamesForEnvFile.js +0 -24
  68. package/src/lib/helpers/keyResolution/keyValues.js +0 -99
  69. package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +0 -85
  70. package/src/lib/helpers/keyResolution/keyValuesSync.js +0 -103
  71. package/src/lib/helpers/keyResolution/readFileKey.js +0 -19
  72. package/src/lib/helpers/keyResolution/readFileKeySync.js +0 -17
  73. package/src/lib/helpers/keyResolution/readProcessKey.js +0 -7
  74. package/src/lib/helpers/localDisplayPath.js +0 -11
  75. package/src/lib/helpers/parse.js +0 -214
  76. package/src/lib/helpers/pluralize.js +0 -10
  77. package/src/lib/helpers/quotes.js +0 -36
  78. package/src/lib/helpers/replace.js +0 -82
  79. package/src/lib/helpers/resolveEscapeSequences.js +0 -5
  80. package/src/lib/services/decrypt.js +0 -156
  81. package/src/lib/services/encrypt.js +0 -213
  82. package/src/lib/services/get.js +0 -76
  83. package/src/lib/services/keypair.js +0 -60
  84. package/src/lib/services/loginPoll.js +0 -36
  85. package/src/lib/services/ls.js +0 -57
  86. package/src/lib/services/rotate.js +0 -197
  87. package/src/lib/services/run.js +0 -255
  88. package/src/lib/services/sets.js +0 -328
@@ -1,197 +0,0 @@
1
- const fsx = require('./../helpers/fsx')
2
- const path = require('path')
3
- const picomatch = require('picomatch')
4
- const { encrypted, scan } = require('@dotenvx/primitives')
5
-
6
- const TYPE_ENV_FILE = 'envFile'
7
-
8
- const Errors = require('./../helpers/errors')
9
-
10
- const {
11
- determine
12
- } = require('./../helpers/envResolution')
13
-
14
- const {
15
- keyNamesForEnvFile,
16
- keyValues
17
- } = require('./../helpers/keyResolution')
18
-
19
- const {
20
- armorKeypair,
21
- localKeypair,
22
- encryptValue,
23
- decryptKeyValue
24
- } = require('./../helpers/cryptography')
25
-
26
- const append = require('./../helpers/append')
27
- const replace = require('./../helpers/replace')
28
- const detectEncoding = require('./../helpers/detectEncoding')
29
-
30
- class Rotate {
31
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noArmor = false, options = {}) {
32
- this.envs = determine(envs, process.env)
33
- this.key = key
34
- this.excludeKey = excludeKey
35
- this.envKeysFilepath = envKeysFilepath
36
- this.noArmor = noArmor
37
- this.command = options.command
38
-
39
- this.processedEnvs = []
40
- this.changedFilepaths = new Set()
41
- this.unchangedFilepaths = new Set()
42
-
43
- this.envKeysSources = {}
44
- }
45
-
46
- async run () {
47
- // example
48
- // envs [
49
- // { type: 'envFile', value: '.env' }
50
- // ]
51
-
52
- this.keys = this._keys()
53
- const excludeKeys = this._excludeKeys()
54
-
55
- this.exclude = picomatch(excludeKeys)
56
- this.include = picomatch(this.keys, { ignore: excludeKeys })
57
-
58
- for (const env of this.envs) {
59
- if (env.type === TYPE_ENV_FILE) {
60
- await this._rotateEnvFile(env.value)
61
- }
62
- }
63
-
64
- return {
65
- processedEnvs: this.processedEnvs,
66
- changedFilepaths: [...this.changedFilepaths],
67
- unchangedFilepaths: [...this.unchangedFilepaths]
68
- }
69
- }
70
-
71
- async _rotateEnvFile (envFilepath) {
72
- const row = {}
73
- row.keys = []
74
- row.type = TYPE_ENV_FILE
75
-
76
- const filepath = path.resolve(envFilepath)
77
- row.filepath = filepath
78
- row.envFilepath = envFilepath
79
-
80
- try {
81
- const encoding = await detectEncoding(filepath)
82
- let envSrc = await fsx.readFileX(filepath, { encoding })
83
- const envParsed = scan(envSrc).parsed
84
-
85
- const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
86
- const { privateKeyValue } = await keyValues(envFilepath, {
87
- keysFilepath: this.envKeysFilepath,
88
- noArmor: this.noArmor,
89
- command: this.command
90
- })
91
-
92
- let newPublicKey
93
- let newPrivateKey
94
- let envKeysFilepath
95
- let envKeysSrc
96
-
97
- if (!this.noArmor) {
98
- const kp = await armorKeypair(undefined, {
99
- command: this.command
100
- })
101
- newPublicKey = kp.publicKey
102
- newPrivateKey = kp.privateKey
103
-
104
- row.localPrivateKeyAdded = false
105
- row.remotePrivateKeyAdded = true
106
- } else {
107
- row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), '.env.keys')
108
- envKeysFilepath = path.resolve(row.envKeysFilepath)
109
- const encodingForKeys = await detectEncoding(envKeysFilepath)
110
- this.envKeysSources[envKeysFilepath] ||= await fsx.readFileX(envKeysFilepath, { encoding: encodingForKeys })
111
- envKeysSrc = this.envKeysSources[envKeysFilepath]
112
-
113
- const kp = localKeypair()
114
- newPublicKey = kp.publicKey
115
- newPrivateKey = kp.privateKey
116
-
117
- row.localPrivateKeyAdded = true
118
- row.remotePrivateKeyAdded = false
119
- }
120
-
121
- // .env
122
- envSrc = replace(envSrc, publicKeyName, newPublicKey) // replace publicKey
123
- row.changed = true // track change
124
-
125
- for (const [key, values] of Object.entries(envParsed)) { // re-encrypt each individual key
126
- // key excluded - don't re-encrypt it
127
- if (this.exclude(key)) {
128
- continue
129
- }
130
-
131
- // key effectively excluded (by not being in the list of includes) - don't re-encrypt it
132
- if (this.keys.length > 0 && !this.include(key)) {
133
- continue
134
- }
135
-
136
- const value = [...values].reverse().find(value => encrypted(value))
137
- if (value) { // only re-encrypt those already encrypted
138
- row.keys.push(key) // track key(s)
139
-
140
- const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKeyValue) // get decrypted value
141
- let encryptedValue
142
- try {
143
- encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
144
- } catch {
145
- throw new Errors({ publicKeyName, publicKey: newPublicKey }).invalidPublicKey()
146
- }
147
-
148
- envSrc = replace(envSrc, key, encryptedValue)
149
- }
150
- }
151
- row.envSrc = envSrc
152
- row.privateKeyName = privateKeyName
153
- row.privateKey = newPrivateKey
154
-
155
- if (this.noArmor) {
156
- // keys src only for local key storage
157
- envKeysSrc = append(envKeysSrc, privateKeyName, newPrivateKey) // append privateKey
158
- this.envKeysSources[envKeysFilepath] = envKeysSrc
159
- row.envKeysSrc = envKeysSrc
160
- }
161
-
162
- this.changedFilepaths.add(envFilepath)
163
- } catch (e) {
164
- if (e.code === 'ENOENT') {
165
- const missingPath = e.path ? path.resolve(e.path) : null
166
- const expectedEnvKeysPath = row.envKeysFilepath ? path.resolve(row.envKeysFilepath) : null
167
- if (this.noArmor && expectedEnvKeysPath && missingPath === expectedEnvKeysPath) {
168
- row.error = new Errors({ envKeysFilepath: row.envKeysFilepath }).missingEnvKeysFile()
169
- } else {
170
- row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
171
- }
172
- } else {
173
- row.error = e
174
- }
175
- }
176
-
177
- this.processedEnvs.push(row)
178
- }
179
-
180
- _keys () {
181
- if (!Array.isArray(this.key)) {
182
- return [this.key]
183
- }
184
-
185
- return this.key
186
- }
187
-
188
- _excludeKeys () {
189
- if (!Array.isArray(this.excludeKey)) {
190
- return [this.excludeKey]
191
- }
192
-
193
- return this.excludeKey
194
- }
195
- }
196
-
197
- module.exports = Rotate
@@ -1,255 +0,0 @@
1
- const fsx = require('./../helpers/fsx')
2
- const path = require('path')
3
-
4
- const TYPE_ENV = 'env'
5
- const TYPE_ENV_FILE = 'envFile'
6
-
7
- const Parse = require('./../helpers/parse')
8
- const Errors = require('./../helpers/errors')
9
- const detectEncoding = require('./../helpers/detectEncoding')
10
- const detectEncodingSync = require('./../helpers/detectEncodingSync')
11
-
12
- const {
13
- keyNamesForEnvFile,
14
- keyValues,
15
- keyValuesSync,
16
- keyValuesFromEnvSrc
17
- } = require('./../helpers/keyResolution')
18
-
19
- class Run {
20
- constructor (envs = [], overload = false, processEnv = process.env, envKeysFilepath = null, noArmor = false, options = {}) {
21
- this.envs = envs
22
- this.overload = overload
23
- this.processEnv = processEnv
24
- this.envKeysFilepath = envKeysFilepath
25
- this.noArmor = noArmor
26
- this.noSpinner = options.noSpinner
27
- this.token = options.token
28
- this.command = options.command
29
-
30
- this.processedEnvs = []
31
- this.readableFilepaths = new Set()
32
- this.readableStrings = new Set()
33
- this.uniqueInjectedKeys = new Set()
34
- this.beforeEnv = { ...this.processEnv }
35
- }
36
-
37
- runSync () {
38
- // example
39
- // envs [
40
- // { type: 'env', value: 'HELLO=one' },
41
- // { type: 'envFile', value: '.env' },
42
- // { type: 'env', value: 'HELLO=three' }
43
- // ]
44
-
45
- for (const env of this.envs) {
46
- if (env.type === TYPE_ENV_FILE) {
47
- this._injectEnvFileSync(env.value)
48
- } else if (env.type === TYPE_ENV) {
49
- this._injectEnv(env.value, env.privateKeyName)
50
- }
51
- }
52
-
53
- return {
54
- processedEnvs: this.processedEnvs,
55
- readableStrings: [...this.readableStrings],
56
- readableFilepaths: [...this.readableFilepaths],
57
- uniqueInjectedKeys: [...this.uniqueInjectedKeys],
58
- beforeEnv: this.beforeEnv,
59
- afterEnv: { ...this.processEnv }
60
- }
61
- }
62
-
63
- async run () {
64
- // example
65
- // envs [
66
- // { type: 'env', value: 'HELLO=one' },
67
- // { type: 'envFile', value: '.env' },
68
- // { type: 'env', value: 'HELLO=three' }
69
- // ]
70
-
71
- for (const env of this.envs) {
72
- if (env.type === TYPE_ENV_FILE) {
73
- await this._injectEnvFile(env.value)
74
- } else if (env.type === TYPE_ENV) {
75
- this._injectEnv(env.value, env.privateKeyName)
76
- }
77
- }
78
-
79
- return {
80
- processedEnvs: this.processedEnvs,
81
- readableStrings: [...this.readableStrings],
82
- readableFilepaths: [...this.readableFilepaths],
83
- uniqueInjectedKeys: [...this.uniqueInjectedKeys],
84
- beforeEnv: this.beforeEnv,
85
- afterEnv: { ...this.processEnv }
86
- }
87
- }
88
-
89
- _injectEnv (env, privateKeyName = null) {
90
- const row = {}
91
- row.type = TYPE_ENV
92
- row.string = env
93
-
94
- try {
95
- const {
96
- privateKeyName: resolvedPrivateKeyName,
97
- privateKeyValue,
98
- privateKeySource
99
- } = keyValuesFromEnvSrc(env, privateKeyName, {
100
- keysFilepath: this.envKeysFilepath,
101
- noArmor: this.noArmor,
102
- processEnv: this.processEnv,
103
- token: this.token,
104
- command: this.command
105
- })
106
-
107
- const {
108
- parsed,
109
- errors,
110
- injected,
111
- preExisted
112
- } = new Parse(env, privateKeyValue, this.processEnv, this.overload, resolvedPrivateKeyName).run()
113
-
114
- row.privateKeyName = resolvedPrivateKeyName
115
- row.privateKey = privateKeyValue
116
- if (privateKeySource) {
117
- row.privateKeySource = privateKeySource
118
- row.armoredPrivateKeyUsed = privateKeySource === 'armor'
119
- }
120
- row.parsed = parsed
121
- row.errors = errors
122
- row.injected = injected
123
- row.preExisted = preExisted
124
-
125
- this.inject(row.parsed) // inject
126
-
127
- this.readableStrings.add(env)
128
-
129
- for (const key of Object.keys(injected)) {
130
- this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
131
- }
132
- } catch (e) {
133
- row.errors = [e]
134
- }
135
-
136
- this.processedEnvs.push(row)
137
- }
138
-
139
- _injectEnvFileSync (envFilepath) {
140
- const row = {}
141
- row.type = TYPE_ENV_FILE
142
- row.filepath = envFilepath
143
-
144
- const filepath = path.resolve(envFilepath)
145
- try {
146
- const encoding = detectEncodingSync(filepath)
147
- const src = fsx.readFileXSync(filepath, { encoding })
148
- this.readableFilepaths.add(envFilepath)
149
-
150
- const { privateKeyName } = keyNamesForEnvFile(filepath)
151
- const { privateKeyValue, privateKeySource } = keyValuesSync(filepath, {
152
- keysFilepath: this.envKeysFilepath,
153
- noArmor: this.noArmor,
154
- noSpinner: this.noSpinner,
155
- token: this.token,
156
- command: this.command
157
- })
158
-
159
- const {
160
- parsed,
161
- errors,
162
- injected,
163
- preExisted
164
- } = new Parse(src, privateKeyValue, this.processEnv, this.overload, privateKeyName).run()
165
-
166
- row.privateKeyName = privateKeyName
167
- row.privateKey = privateKeyValue
168
- if (privateKeySource) {
169
- row.privateKeySource = privateKeySource
170
- row.armoredPrivateKeyUsed = privateKeySource === 'armor'
171
- }
172
- row.src = src
173
- row.parsed = parsed
174
- row.errors = errors
175
- row.injected = injected
176
- row.preExisted = preExisted
177
-
178
- this.inject(row.parsed) // inject
179
-
180
- for (const key of Object.keys(injected)) {
181
- this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
182
- }
183
- } catch (e) {
184
- if (e.code === 'ENOENT' || e.code === 'EISDIR') {
185
- row.errors = [new Errors({ envFilepath, filepath }).missingEnvFile()]
186
- } else {
187
- row.errors = [e]
188
- }
189
- }
190
-
191
- this.processedEnvs.push(row)
192
- }
193
-
194
- async _injectEnvFile (envFilepath) {
195
- const row = {}
196
- row.type = TYPE_ENV_FILE
197
- row.filepath = envFilepath
198
-
199
- const filepath = path.resolve(envFilepath)
200
- try {
201
- const encoding = await detectEncoding(filepath)
202
- const src = await fsx.readFileX(filepath, { encoding })
203
- this.readableFilepaths.add(envFilepath)
204
-
205
- const { privateKeyName } = keyNamesForEnvFile(filepath)
206
- const { privateKeyValue, privateKeySource } = await keyValues(filepath, {
207
- keysFilepath: this.envKeysFilepath,
208
- noArmor: this.noArmor,
209
- token: this.token,
210
- command: this.command
211
- })
212
-
213
- const {
214
- parsed,
215
- errors,
216
- injected,
217
- preExisted
218
- } = new Parse(src, privateKeyValue, this.processEnv, this.overload, privateKeyName).run()
219
-
220
- row.privateKeyName = privateKeyName
221
- row.privateKey = privateKeyValue
222
- if (privateKeySource) {
223
- row.privateKeySource = privateKeySource
224
- row.armoredPrivateKeyUsed = privateKeySource === 'armor'
225
- }
226
- row.src = src
227
- row.parsed = parsed
228
- row.errors = errors
229
- row.injected = injected
230
- row.preExisted = preExisted
231
-
232
- this.inject(row.parsed) // inject
233
-
234
- for (const key of Object.keys(injected)) {
235
- this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
236
- }
237
- } catch (e) {
238
- if (e.code === 'ENOENT' || e.code === 'EISDIR') {
239
- row.errors = [new Errors({ envFilepath, filepath }).missingEnvFile()]
240
- } else {
241
- row.errors = [e]
242
- }
243
- }
244
-
245
- this.processedEnvs.push(row)
246
- }
247
-
248
- inject (parsed) {
249
- for (const key of Object.keys(parsed)) {
250
- this.processEnv[key] = parsed[key] // inject to process.env
251
- }
252
- }
253
- }
254
-
255
- module.exports = Run