@dotenvx/dotenvx 1.75.0 → 2.0.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 (89) hide show
  1. package/CHANGELOG.md +34 -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 +16 -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 +51 -37
  14. package/src/cli/dotenvx.js +6 -21
  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 +1 -12
  21. package/src/lib/helpers/cryptography/mutateKeysSrc.js +5 -21
  22. package/src/lib/helpers/envResolution/index.js +1 -1
  23. package/src/lib/helpers/errors.js +13 -0
  24. package/src/lib/helpers/executeDynamic.js +1 -3
  25. package/src/lib/helpers/keypairMetadata.js +3 -3
  26. package/src/lib/helpers/readEnvKey.js +4 -3
  27. package/src/lib/main.d.ts +2 -39
  28. package/src/lib/main.js +82 -45
  29. package/src/lib/providers/armor/index.js +29 -0
  30. package/src/lib/providers/index.js +46 -0
  31. package/src/lib/providers/worker.js +6 -0
  32. package/src/lib/resolvers/envs.js +302 -0
  33. package/src/lib/resolvers/get.js +69 -0
  34. package/src/lib/resolvers/keypair.js +81 -0
  35. package/src/lib/resolvers/ls.js +41 -0
  36. package/src/lib/services/armorDown.js +2 -2
  37. package/src/lib/services/{armorKeypair.js → armorKeyring.js} +14 -51
  38. package/src/lib/services/armorMove.js +2 -2
  39. package/src/lib/services/armorPull.js +2 -2
  40. package/src/lib/services/armorPush.js +2 -2
  41. package/src/lib/services/armorUp.js +2 -2
  42. package/src/lib/services/genexample.js +19 -8
  43. package/src/lib/services/login.js +25 -0
  44. package/src/lib/services/prebuild.js +11 -5
  45. package/src/lib/services/precommit.js +11 -5
  46. package/src/lib/transforms/decrypt.js +83 -0
  47. package/src/lib/transforms/encrypt.js +179 -0
  48. package/src/lib/transforms/set.js +179 -0
  49. package/src/shared/logger.js +5 -0
  50. package/src/cli/actions/normalizeArmorOptions.js +0 -10
  51. package/src/cli/actions/rotate.js +0 -87
  52. package/src/lib/helpers/append.js +0 -61
  53. package/src/lib/helpers/cryptography/armorKeypair.js +0 -42
  54. package/src/lib/helpers/cryptography/armorKeypairSync.js +0 -55
  55. package/src/lib/helpers/cryptography/encryptValue.js +0 -7
  56. package/src/lib/helpers/cryptography/localKeypair.js +0 -14
  57. package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +0 -38
  58. package/src/lib/helpers/cryptography/provision.js +0 -56
  59. package/src/lib/helpers/cryptography/provisionSync.js +0 -51
  60. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +0 -26
  61. package/src/lib/helpers/dotenvParse.js +0 -55
  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/isFullyEncrypted.js +0 -27
  66. package/src/lib/helpers/isIgnoringDotenvKeys.js +0 -19
  67. package/src/lib/helpers/keyResolution/index.js +0 -16
  68. package/src/lib/helpers/keyResolution/keyNamesForEnvFile.js +0 -24
  69. package/src/lib/helpers/keyResolution/keyValues.js +0 -99
  70. package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +0 -80
  71. package/src/lib/helpers/keyResolution/keyValuesSync.js +0 -103
  72. package/src/lib/helpers/keyResolution/readFileKey.js +0 -17
  73. package/src/lib/helpers/keyResolution/readFileKeySync.js +0 -15
  74. package/src/lib/helpers/keyResolution/readProcessKey.js +0 -7
  75. package/src/lib/helpers/localDisplayPath.js +0 -11
  76. package/src/lib/helpers/parse.js +0 -214
  77. package/src/lib/helpers/pluralize.js +0 -10
  78. package/src/lib/helpers/quotes.js +0 -36
  79. package/src/lib/helpers/replace.js +0 -82
  80. package/src/lib/helpers/resolveEscapeSequences.js +0 -5
  81. package/src/lib/services/decrypt.js +0 -157
  82. package/src/lib/services/encrypt.js +0 -214
  83. package/src/lib/services/get.js +0 -76
  84. package/src/lib/services/keypair.js +0 -60
  85. package/src/lib/services/loginPoll.js +0 -36
  86. package/src/lib/services/ls.js +0 -57
  87. package/src/lib/services/rotate.js +0 -198
  88. package/src/lib/services/run.js +0 -255
  89. package/src/lib/services/sets.js +0 -326
@@ -1,198 +0,0 @@
1
- const fsx = require('./../helpers/fsx')
2
- const path = require('path')
3
- const picomatch = require('picomatch')
4
- const { encrypted } = 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 dotenvParse = require('./../helpers/dotenvParse')
29
- const detectEncoding = require('./../helpers/detectEncoding')
30
-
31
- class Rotate {
32
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noArmor = false, options = {}) {
33
- this.envs = determine(envs, process.env)
34
- this.key = key
35
- this.excludeKey = excludeKey
36
- this.envKeysFilepath = envKeysFilepath
37
- this.noArmor = noArmor
38
- this.command = options.command
39
-
40
- this.processedEnvs = []
41
- this.changedFilepaths = new Set()
42
- this.unchangedFilepaths = new Set()
43
-
44
- this.envKeysSources = {}
45
- }
46
-
47
- async run () {
48
- // example
49
- // envs [
50
- // { type: 'envFile', value: '.env' }
51
- // ]
52
-
53
- this.keys = this._keys()
54
- const excludeKeys = this._excludeKeys()
55
-
56
- this.exclude = picomatch(excludeKeys)
57
- this.include = picomatch(this.keys, { ignore: excludeKeys })
58
-
59
- for (const env of this.envs) {
60
- if (env.type === TYPE_ENV_FILE) {
61
- await this._rotateEnvFile(env.value)
62
- }
63
- }
64
-
65
- return {
66
- processedEnvs: this.processedEnvs,
67
- changedFilepaths: [...this.changedFilepaths],
68
- unchangedFilepaths: [...this.unchangedFilepaths]
69
- }
70
- }
71
-
72
- async _rotateEnvFile (envFilepath) {
73
- const row = {}
74
- row.keys = []
75
- row.type = TYPE_ENV_FILE
76
-
77
- const filepath = path.resolve(envFilepath)
78
- row.filepath = filepath
79
- row.envFilepath = envFilepath
80
-
81
- try {
82
- const encoding = await detectEncoding(filepath)
83
- let envSrc = await fsx.readFileX(filepath, { encoding })
84
- const envParsed = dotenvParse(envSrc, false, false, true)
85
-
86
- const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
87
- const { privateKeyValue } = await keyValues(envFilepath, {
88
- keysFilepath: this.envKeysFilepath,
89
- noArmor: this.noArmor,
90
- command: this.command
91
- })
92
-
93
- let newPublicKey
94
- let newPrivateKey
95
- let envKeysFilepath
96
- let envKeysSrc
97
-
98
- if (!this.noArmor) {
99
- const kp = await armorKeypair(undefined, {
100
- command: this.command
101
- })
102
- newPublicKey = kp.publicKey
103
- newPrivateKey = kp.privateKey
104
-
105
- row.localPrivateKeyAdded = false
106
- row.remotePrivateKeyAdded = true
107
- } else {
108
- row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), '.env.keys')
109
- envKeysFilepath = path.resolve(row.envKeysFilepath)
110
- const encodingForKeys = await detectEncoding(envKeysFilepath)
111
- this.envKeysSources[envKeysFilepath] ||= await fsx.readFileX(envKeysFilepath, { encoding: encodingForKeys })
112
- envKeysSrc = this.envKeysSources[envKeysFilepath]
113
-
114
- const kp = localKeypair()
115
- newPublicKey = kp.publicKey
116
- newPrivateKey = kp.privateKey
117
-
118
- row.localPrivateKeyAdded = true
119
- row.remotePrivateKeyAdded = false
120
- }
121
-
122
- // .env
123
- envSrc = replace(envSrc, publicKeyName, newPublicKey) // replace publicKey
124
- row.changed = true // track change
125
-
126
- for (const [key, values] of Object.entries(envParsed)) { // re-encrypt each individual key
127
- // key excluded - don't re-encrypt it
128
- if (this.exclude(key)) {
129
- continue
130
- }
131
-
132
- // key effectively excluded (by not being in the list of includes) - don't re-encrypt it
133
- if (this.keys.length > 0 && !this.include(key)) {
134
- continue
135
- }
136
-
137
- const value = [...values].reverse().find(value => encrypted(value))
138
- if (value) { // only re-encrypt those already encrypted
139
- row.keys.push(key) // track key(s)
140
-
141
- const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKeyValue) // get decrypted value
142
- let encryptedValue
143
- try {
144
- encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
145
- } catch {
146
- throw new Errors({ publicKeyName, publicKey: newPublicKey }).invalidPublicKey()
147
- }
148
-
149
- envSrc = replace(envSrc, key, encryptedValue)
150
- }
151
- }
152
- row.envSrc = envSrc
153
- row.privateKeyName = privateKeyName
154
- row.privateKey = newPrivateKey
155
-
156
- if (this.noArmor) {
157
- // keys src only for local key storage
158
- envKeysSrc = append(envKeysSrc, privateKeyName, newPrivateKey) // append privateKey
159
- this.envKeysSources[envKeysFilepath] = envKeysSrc
160
- row.envKeysSrc = envKeysSrc
161
- }
162
-
163
- this.changedFilepaths.add(envFilepath)
164
- } catch (e) {
165
- if (e.code === 'ENOENT') {
166
- const missingPath = e.path ? path.resolve(e.path) : null
167
- const expectedEnvKeysPath = row.envKeysFilepath ? path.resolve(row.envKeysFilepath) : null
168
- if (this.noArmor && expectedEnvKeysPath && missingPath === expectedEnvKeysPath) {
169
- row.error = new Errors({ envKeysFilepath: row.envKeysFilepath }).missingEnvKeysFile()
170
- } else {
171
- row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
172
- }
173
- } else {
174
- row.error = e
175
- }
176
- }
177
-
178
- this.processedEnvs.push(row)
179
- }
180
-
181
- _keys () {
182
- if (!Array.isArray(this.key)) {
183
- return [this.key]
184
- }
185
-
186
- return this.key
187
- }
188
-
189
- _excludeKeys () {
190
- if (!Array.isArray(this.excludeKey)) {
191
- return [this.excludeKey]
192
- }
193
-
194
- return this.excludeKey
195
- }
196
- }
197
-
198
- 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