@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
|
@@ -29,12 +29,12 @@ const detectEncoding = require('./../helpers/detectEncoding')
|
|
|
29
29
|
const SAMPLE_ENV_KIT = require('./../helpers/kits/sample')
|
|
30
30
|
|
|
31
31
|
class Encrypt {
|
|
32
|
-
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null,
|
|
32
|
+
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noOps = false, noCreate = false) {
|
|
33
33
|
this.envs = determine(envs, process.env)
|
|
34
34
|
this.key = key
|
|
35
35
|
this.excludeKey = excludeKey
|
|
36
36
|
this.envKeysFilepath = envKeysFilepath
|
|
37
|
-
this.
|
|
37
|
+
this.noOps = noOps
|
|
38
38
|
this.noCreate = noCreate
|
|
39
39
|
|
|
40
40
|
this.processedEnvs = []
|
|
@@ -42,7 +42,7 @@ class Encrypt {
|
|
|
42
42
|
this.unchangedFilepaths = new Set()
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
run () {
|
|
45
|
+
async run () {
|
|
46
46
|
// example
|
|
47
47
|
// envs [
|
|
48
48
|
// { type: 'envFile', value: '.env' }
|
|
@@ -56,7 +56,7 @@ class Encrypt {
|
|
|
56
56
|
|
|
57
57
|
for (const env of this.envs) {
|
|
58
58
|
if (env.type === TYPE_ENV_FILE) {
|
|
59
|
-
this._encryptEnvFile(env.value)
|
|
59
|
+
await this._encryptEnvFile(env.value)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -67,10 +67,11 @@ class Encrypt {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
_encryptEnvFile (envFilepath) {
|
|
70
|
+
async _encryptEnvFile (envFilepath) {
|
|
71
71
|
const row = {}
|
|
72
72
|
row.keys = []
|
|
73
73
|
row.type = TYPE_ENV_FILE
|
|
74
|
+
let fileCreated = false
|
|
74
75
|
|
|
75
76
|
const filepath = path.resolve(envFilepath)
|
|
76
77
|
row.filepath = filepath
|
|
@@ -79,14 +80,16 @@ class Encrypt {
|
|
|
79
80
|
try {
|
|
80
81
|
// if noCreate is on then detectEncoding will throw and we'll halt the calls
|
|
81
82
|
// but if noCreate is false then create the file if it doesn't exist
|
|
82
|
-
if (!fsx.
|
|
83
|
-
fsx.writeFileX(filepath, SAMPLE_ENV_KIT)
|
|
83
|
+
if (!(await fsx.exists(filepath)) && !this.noCreate) {
|
|
84
|
+
await fsx.writeFileX(filepath, SAMPLE_ENV_KIT)
|
|
85
|
+
fileCreated = true
|
|
84
86
|
}
|
|
85
|
-
const encoding = detectEncoding(filepath)
|
|
86
|
-
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
87
|
+
const encoding = await detectEncoding(filepath)
|
|
88
|
+
let envSrc = await fsx.readFileX(filepath, { encoding })
|
|
87
89
|
if (envSrc.trim().length === 0) {
|
|
88
90
|
envSrc = SAMPLE_ENV_KIT
|
|
89
91
|
row.kitCreated = 'sample'
|
|
92
|
+
row.changed = true
|
|
90
93
|
}
|
|
91
94
|
const envParsed = dotenvParse(envSrc)
|
|
92
95
|
|
|
@@ -94,15 +97,16 @@ class Encrypt {
|
|
|
94
97
|
let privateKey
|
|
95
98
|
|
|
96
99
|
const { publicKeyName, privateKeyName } = keyNames(envFilepath)
|
|
97
|
-
const { publicKeyValue, privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath,
|
|
100
|
+
const { publicKeyValue, privateKeyValue } = await keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
98
101
|
|
|
99
102
|
// first pass - provision
|
|
100
103
|
if (!privateKeyValue && !publicKeyValue) {
|
|
101
|
-
const prov = provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath,
|
|
104
|
+
const prov = await provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
102
105
|
envSrc = prov.envSrc
|
|
103
106
|
publicKey = prov.publicKey
|
|
104
107
|
privateKey = prov.privateKey
|
|
105
|
-
row.
|
|
108
|
+
row.localPrivateKeyAdded = prov.localPrivateKeyAdded
|
|
109
|
+
row.remotePrivateKeyAdded = prov.remotePrivateKeyAdded
|
|
106
110
|
row.envKeysFilepath = prov.envKeysFilepath
|
|
107
111
|
} else if (privateKeyValue) {
|
|
108
112
|
const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
|
|
@@ -148,6 +152,9 @@ class Encrypt {
|
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
row.envSrc = envSrc
|
|
155
|
+
if (fileCreated) {
|
|
156
|
+
row.changed = true
|
|
157
|
+
}
|
|
151
158
|
if (row.changed) {
|
|
152
159
|
this.changedFilepaths.add(envFilepath)
|
|
153
160
|
} else {
|
|
@@ -39,7 +39,7 @@ class Genexample {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// get the original src
|
|
42
|
-
let src = fsx.
|
|
42
|
+
let src = fsx.readFileXSync(filepath)
|
|
43
43
|
const parsed = dotenvParse(src)
|
|
44
44
|
for (const key in parsed) {
|
|
45
45
|
// used later
|
|
@@ -63,7 +63,7 @@ class Genexample {
|
|
|
63
63
|
}
|
|
64
64
|
} else {
|
|
65
65
|
// it already exists (which means the user might have it modified a way in which they prefer, so replace exampleSrc with their existing .env.example)
|
|
66
|
-
exampleSrc = fsx.
|
|
66
|
+
exampleSrc = fsx.readFileXSync(this.exampleFilepath)
|
|
67
67
|
|
|
68
68
|
const parsed = dotenvParse(exampleSrc)
|
|
69
69
|
for (const key of [...keys]) {
|
package/src/lib/services/get.js
CHANGED
|
@@ -2,19 +2,28 @@ const Run = require('./run')
|
|
|
2
2
|
const Errors = require('./../helpers/errors')
|
|
3
3
|
|
|
4
4
|
class Get {
|
|
5
|
-
constructor (key, envs = [], overload = false, all = false, envKeysFilepath = null,
|
|
5
|
+
constructor (key, envs = [], overload = false, all = false, envKeysFilepath = null, noOps = false) {
|
|
6
6
|
this.key = key
|
|
7
7
|
this.envs = envs
|
|
8
8
|
this.overload = overload
|
|
9
9
|
this.all = all
|
|
10
10
|
this.envKeysFilepath = envKeysFilepath
|
|
11
|
-
this.
|
|
11
|
+
this.noOps = noOps
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
runSync () {
|
|
15
15
|
const processEnv = { ...process.env }
|
|
16
|
-
const { processedEnvs } = new Run(this.envs, this.overload, processEnv, this.envKeysFilepath, this.
|
|
16
|
+
const { processedEnvs } = new Run(this.envs, this.overload, processEnv, this.envKeysFilepath, this.noOps).runSync()
|
|
17
|
+
return this._result(processedEnvs, processEnv)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async run () {
|
|
21
|
+
const processEnv = { ...process.env }
|
|
22
|
+
const { processedEnvs } = await new Run(this.envs, this.overload, processEnv, this.envKeysFilepath, this.noOps).run()
|
|
23
|
+
return this._result(processedEnvs, processEnv)
|
|
24
|
+
}
|
|
17
25
|
|
|
26
|
+
_result (processedEnvs, processEnv) {
|
|
18
27
|
const errors = []
|
|
19
28
|
for (const processedEnv of processedEnvs) {
|
|
20
29
|
for (const error of processedEnv.errors) {
|
|
@@ -32,27 +41,27 @@ class Get {
|
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
return { parsed, errors }
|
|
35
|
-
}
|
|
36
|
-
// if user wants to return ALL envs (even prior set on machine)
|
|
37
|
-
if (this.all) {
|
|
38
|
-
return { parsed: processEnv, errors }
|
|
39
|
-
}
|
|
44
|
+
}
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
// if user wants to return ALL envs (even prior set on machine)
|
|
47
|
+
if (this.all) {
|
|
48
|
+
return { parsed: processEnv, errors }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// typical scenario - return only envs that were identified in the .env file
|
|
52
|
+
// iterate over all processedEnvs.parsed and grab from processEnv
|
|
53
|
+
/** @type {Record<string, string>} */
|
|
54
|
+
const parsed = {}
|
|
55
|
+
for (const processedEnv of processedEnvs) {
|
|
56
|
+
// parsed means we saw the key in a file or --env flag. this effectively filters out any preset machine envs - while still respecting complex evaluating, expansion, and overload. in other words, the value might be the machine value because the key was displayed in a .env file
|
|
57
|
+
if (processedEnv.parsed) {
|
|
58
|
+
for (const key of Object.keys(processedEnv.parsed)) {
|
|
59
|
+
parsed[key] = processEnv[key]
|
|
51
60
|
}
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
return { parsed, errors }
|
|
55
62
|
}
|
|
63
|
+
|
|
64
|
+
return { parsed, errors }
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
|
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
const {
|
|
2
2
|
keyNames,
|
|
3
|
-
keyValues
|
|
3
|
+
keyValues,
|
|
4
|
+
keyValuesSync
|
|
4
5
|
} = require('./../helpers/keyResolution')
|
|
5
6
|
|
|
6
7
|
class Keypair {
|
|
7
|
-
constructor (envFile = '.env', envKeysFilepath = null,
|
|
8
|
+
constructor (envFile = '.env', envKeysFilepath = null, noOps = false) {
|
|
8
9
|
this.envFile = envFile
|
|
9
10
|
this.envKeysFilepath = envKeysFilepath
|
|
10
|
-
this.
|
|
11
|
+
this.noOps = noOps
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
runSync () {
|
|
14
15
|
const out = {}
|
|
15
16
|
|
|
16
17
|
const filepaths = this._filepaths()
|
|
17
18
|
for (const filepath of filepaths) {
|
|
18
19
|
const { publicKeyName, privateKeyName } = keyNames(filepath)
|
|
19
|
-
const { publicKeyValue, privateKeyValue } =
|
|
20
|
+
const { publicKeyValue, privateKeyValue } = keyValuesSync(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
21
|
+
|
|
22
|
+
out[publicKeyName] = publicKeyValue
|
|
23
|
+
out[privateKeyName] = privateKeyValue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return out
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async run () {
|
|
30
|
+
const out = {}
|
|
31
|
+
|
|
32
|
+
const filepaths = this._filepaths()
|
|
33
|
+
for (const filepath of filepaths) {
|
|
34
|
+
const { publicKeyName, privateKeyName } = keyNames(filepath)
|
|
35
|
+
const { publicKeyValue, privateKeyValue } = await keyValues(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
20
36
|
|
|
21
37
|
out[publicKeyName] = publicKeyValue
|
|
22
38
|
out[privateKeyName] = privateKeyValue
|
|
@@ -7,7 +7,6 @@ const Ls = require('../services/ls')
|
|
|
7
7
|
const Errors = require('../helpers/errors')
|
|
8
8
|
|
|
9
9
|
const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
|
|
10
|
-
const packageJson = require('./../helpers/packageJson')
|
|
11
10
|
const MISSING_DOCKERIGNORE = '.env.keys' // by default only ignore .env.keys. all other .env* files COULD be included - as long as they are encrypted
|
|
12
11
|
|
|
13
12
|
class Prebuild {
|
|
@@ -26,12 +25,12 @@ class Prebuild {
|
|
|
26
25
|
// 1. check for .dockerignore file
|
|
27
26
|
if (!fsx.existsSync('.dockerignore')) {
|
|
28
27
|
const warning = new Errors({
|
|
29
|
-
message:
|
|
28
|
+
message: '.dockerignore missing',
|
|
30
29
|
help: 'fix: [touch .dockerignore]'
|
|
31
30
|
}).custom()
|
|
32
31
|
warnings.push(warning)
|
|
33
32
|
} else {
|
|
34
|
-
dockerignore = fsx.
|
|
33
|
+
dockerignore = fsx.readFileXSync('.dockerignore')
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// 2. check .env* files against .dockerignore file
|
|
@@ -47,22 +46,22 @@ class Prebuild {
|
|
|
47
46
|
if (ig.ignores(file)) {
|
|
48
47
|
if (file === '.env.example' || file === '.env.x') {
|
|
49
48
|
const warning = new Errors({
|
|
50
|
-
message:
|
|
49
|
+
message: `${file} ignored (should not be)`,
|
|
51
50
|
help: `fix: [dotenvx ext gitignore --pattern !${file}]`
|
|
52
51
|
}).custom()
|
|
53
52
|
warnings.push(warning)
|
|
54
53
|
}
|
|
55
54
|
} else {
|
|
56
55
|
if (file !== '.env.example' && file !== '.env.x') {
|
|
57
|
-
const src = fsx.
|
|
56
|
+
const src = fsx.readFileXSync(file)
|
|
58
57
|
const encrypted = isFullyEncrypted(src)
|
|
59
58
|
|
|
60
59
|
// if contents are encrypted don't raise an error
|
|
61
60
|
if (!encrypted) {
|
|
62
|
-
let errorMsg =
|
|
61
|
+
let errorMsg = `${file} not encrypted/dockerignored`
|
|
63
62
|
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
|
|
64
63
|
if (file.includes('.env.keys')) {
|
|
65
|
-
errorMsg =
|
|
64
|
+
errorMsg = `${file} not dockerignored`
|
|
66
65
|
errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -72,11 +71,7 @@ class Prebuild {
|
|
|
72
71
|
}
|
|
73
72
|
})
|
|
74
73
|
|
|
75
|
-
let successMessage =
|
|
76
|
-
|
|
77
|
-
if (count === 0) {
|
|
78
|
-
successMessage = `[dotenvx@${packageJson.version}][prebuild] zero .env files`
|
|
79
|
-
}
|
|
74
|
+
let successMessage = count === 0 ? '▣ no .env files' : `▣ encrypted/dockerignored (${count})`
|
|
80
75
|
if (warnings.length > 0) {
|
|
81
76
|
successMessage += ` with warnings (${warnings.length})`
|
|
82
77
|
}
|
|
@@ -6,7 +6,6 @@ const ignore = require('ignore')
|
|
|
6
6
|
const Ls = require('../services/ls')
|
|
7
7
|
|
|
8
8
|
const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
|
|
9
|
-
const packageJson = require('./../helpers/packageJson')
|
|
10
9
|
const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
|
|
11
10
|
const Errors = require('./../helpers/errors')
|
|
12
11
|
const childProcess = require('child_process')
|
|
@@ -39,12 +38,12 @@ class Precommit {
|
|
|
39
38
|
// 1. check for .gitignore file
|
|
40
39
|
if (!fsx.existsSync('.gitignore')) {
|
|
41
40
|
const warning = new Errors({
|
|
42
|
-
message:
|
|
41
|
+
message: '.gitignore missing',
|
|
43
42
|
help: 'fix: [touch .gitignore]'
|
|
44
43
|
}).custom()
|
|
45
44
|
warnings.push(warning)
|
|
46
45
|
} else {
|
|
47
|
-
gitignore = fsx.
|
|
46
|
+
gitignore = fsx.readFileXSync('.gitignore')
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
// 2. check .env* files against .gitignore file
|
|
@@ -63,22 +62,22 @@ class Precommit {
|
|
|
63
62
|
if (ig.ignores(file)) {
|
|
64
63
|
if (file === '.env.example' || file === '.env.x') {
|
|
65
64
|
const warning = new Errors({
|
|
66
|
-
message:
|
|
65
|
+
message: `${file} ignored (should not be)`,
|
|
67
66
|
help: `fix: [dotenvx ext gitignore --pattern !${file}]`
|
|
68
67
|
}).custom()
|
|
69
68
|
warnings.push(warning)
|
|
70
69
|
}
|
|
71
70
|
} else {
|
|
72
71
|
if (file !== '.env.example' && file !== '.env.x') {
|
|
73
|
-
const src = fsx.
|
|
72
|
+
const src = fsx.readFileXSync(file)
|
|
74
73
|
const encrypted = isFullyEncrypted(src)
|
|
75
74
|
|
|
76
75
|
// if contents are encrypted don't raise an error
|
|
77
76
|
if (!encrypted) {
|
|
78
|
-
let errorMsg =
|
|
77
|
+
let errorMsg = `${file} not encrypted/gitignored`
|
|
79
78
|
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
|
|
80
79
|
if (file.includes('.env.keys')) {
|
|
81
|
-
errorMsg =
|
|
80
|
+
errorMsg = `${file} not gitignored`
|
|
82
81
|
errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
|
|
83
82
|
}
|
|
84
83
|
|
|
@@ -89,10 +88,7 @@ class Precommit {
|
|
|
89
88
|
}
|
|
90
89
|
})
|
|
91
90
|
|
|
92
|
-
let successMessage =
|
|
93
|
-
if (count === 0) {
|
|
94
|
-
successMessage = `[dotenvx@${packageJson.version}][precommit] zero .env files`
|
|
95
|
-
}
|
|
91
|
+
let successMessage = count === 0 ? '▣ no .env files' : `▣ encrypted/gitignored (${count})`
|
|
96
92
|
if (warnings.length > 0) {
|
|
97
93
|
successMessage += ` with warnings (${warnings.length})`
|
|
98
94
|
}
|
|
@@ -29,12 +29,12 @@ const dotenvParse = require('./../helpers/dotenvParse')
|
|
|
29
29
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
30
30
|
|
|
31
31
|
class Rotate {
|
|
32
|
-
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null,
|
|
32
|
+
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noOps = false) {
|
|
33
33
|
this.envs = determine(envs, process.env)
|
|
34
34
|
this.key = key
|
|
35
35
|
this.excludeKey = excludeKey
|
|
36
36
|
this.envKeysFilepath = envKeysFilepath
|
|
37
|
-
this.
|
|
37
|
+
this.noOps = noOps
|
|
38
38
|
|
|
39
39
|
this.processedEnvs = []
|
|
40
40
|
this.changedFilepaths = new Set()
|
|
@@ -43,7 +43,7 @@ class Rotate {
|
|
|
43
43
|
this.envKeysSources = {}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
run () {
|
|
46
|
+
async run () {
|
|
47
47
|
// example
|
|
48
48
|
// envs [
|
|
49
49
|
// { type: 'envFile', value: '.env' }
|
|
@@ -57,7 +57,7 @@ class Rotate {
|
|
|
57
57
|
|
|
58
58
|
for (const env of this.envs) {
|
|
59
59
|
if (env.type === TYPE_ENV_FILE) {
|
|
60
|
-
this._rotateEnvFile(env.value)
|
|
60
|
+
await this._rotateEnvFile(env.value)
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -68,7 +68,7 @@ class Rotate {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
_rotateEnvFile (envFilepath) {
|
|
71
|
+
async _rotateEnvFile (envFilepath) {
|
|
72
72
|
const row = {}
|
|
73
73
|
row.keys = []
|
|
74
74
|
row.type = TYPE_ENV_FILE
|
|
@@ -78,38 +78,38 @@ class Rotate {
|
|
|
78
78
|
row.envFilepath = envFilepath
|
|
79
79
|
|
|
80
80
|
try {
|
|
81
|
-
const encoding = detectEncoding(filepath)
|
|
82
|
-
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
81
|
+
const encoding = await detectEncoding(filepath)
|
|
82
|
+
let envSrc = await fsx.readFileX(filepath, { encoding })
|
|
83
83
|
const envParsed = dotenvParse(envSrc)
|
|
84
84
|
|
|
85
85
|
const { publicKeyName, privateKeyName } = keyNames(envFilepath)
|
|
86
|
-
const { privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath,
|
|
86
|
+
const { privateKeyValue } = await keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
87
87
|
|
|
88
88
|
let newPublicKey
|
|
89
89
|
let newPrivateKey
|
|
90
90
|
let envKeysFilepath
|
|
91
91
|
let envKeysSrc
|
|
92
92
|
|
|
93
|
-
if (this.
|
|
94
|
-
const kp = opsKeypair()
|
|
93
|
+
if (!this.noOps) {
|
|
94
|
+
const kp = await opsKeypair()
|
|
95
95
|
newPublicKey = kp.publicKey
|
|
96
96
|
newPrivateKey = kp.privateKey
|
|
97
97
|
|
|
98
|
-
row.
|
|
98
|
+
row.localPrivateKeyAdded = false
|
|
99
|
+
row.remotePrivateKeyAdded = true
|
|
99
100
|
} else {
|
|
100
|
-
envKeysFilepath = path.join(path.dirname(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
row.envKeysFilepath = envKeysFilepath
|
|
105
|
-
this.envKeysSources[envKeysFilepath] ||= fsx.readFileX(envKeysFilepath, { encoding: detectEncoding(envKeysFilepath) })
|
|
101
|
+
row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), '.env.keys')
|
|
102
|
+
envKeysFilepath = path.resolve(row.envKeysFilepath)
|
|
103
|
+
const encodingForKeys = await detectEncoding(envKeysFilepath)
|
|
104
|
+
this.envKeysSources[envKeysFilepath] ||= await fsx.readFileX(envKeysFilepath, { encoding: encodingForKeys })
|
|
106
105
|
envKeysSrc = this.envKeysSources[envKeysFilepath]
|
|
107
106
|
|
|
108
107
|
const kp = localKeypair()
|
|
109
108
|
newPublicKey = kp.publicKey
|
|
110
109
|
newPrivateKey = kp.privateKey
|
|
111
110
|
|
|
112
|
-
row.
|
|
111
|
+
row.localPrivateKeyAdded = true
|
|
112
|
+
row.remotePrivateKeyAdded = false
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// .env
|
|
@@ -145,7 +145,7 @@ class Rotate {
|
|
|
145
145
|
row.privateKeyName = privateKeyName
|
|
146
146
|
row.privateKey = newPrivateKey
|
|
147
147
|
|
|
148
|
-
if (
|
|
148
|
+
if (this.noOps) {
|
|
149
149
|
// keys src only for ops
|
|
150
150
|
envKeysSrc = append(envKeysSrc, privateKeyName, newPrivateKey) // append privateKey
|
|
151
151
|
this.envKeysSources[envKeysFilepath] = envKeysSrc
|
|
@@ -155,7 +155,13 @@ class Rotate {
|
|
|
155
155
|
this.changedFilepaths.add(envFilepath)
|
|
156
156
|
} catch (e) {
|
|
157
157
|
if (e.code === 'ENOENT') {
|
|
158
|
-
|
|
158
|
+
const missingPath = e.path ? path.resolve(e.path) : null
|
|
159
|
+
const expectedEnvKeysPath = row.envKeysFilepath ? path.resolve(row.envKeysFilepath) : null
|
|
160
|
+
if (this.noOps && expectedEnvKeysPath && missingPath === expectedEnvKeysPath) {
|
|
161
|
+
row.error = new Errors({ envKeysFilepath: row.envKeysFilepath }).missingEnvKeysFile()
|
|
162
|
+
} else {
|
|
163
|
+
row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
164
|
+
}
|
|
159
165
|
} else {
|
|
160
166
|
row.error = e
|
|
161
167
|
}
|
package/src/lib/services/run.js
CHANGED
|
@@ -7,10 +7,12 @@ const TYPE_ENV_FILE = 'envFile'
|
|
|
7
7
|
const Parse = require('./../helpers/parse')
|
|
8
8
|
const Errors = require('./../helpers/errors')
|
|
9
9
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
10
|
+
const detectEncodingSync = require('./../helpers/detectEncodingSync')
|
|
10
11
|
|
|
11
12
|
const {
|
|
12
13
|
keyNames,
|
|
13
|
-
keyValues
|
|
14
|
+
keyValues,
|
|
15
|
+
keyValuesSync
|
|
14
16
|
} = require('./../helpers/keyResolution')
|
|
15
17
|
|
|
16
18
|
const {
|
|
@@ -18,12 +20,12 @@ const {
|
|
|
18
20
|
} = require('./../helpers/envResolution')
|
|
19
21
|
|
|
20
22
|
class Run {
|
|
21
|
-
constructor (envs = [], overload = false, processEnv = process.env, envKeysFilepath = null,
|
|
23
|
+
constructor (envs = [], overload = false, processEnv = process.env, envKeysFilepath = null, noOps = false) {
|
|
22
24
|
this.envs = determine(envs, processEnv)
|
|
23
25
|
this.overload = overload
|
|
24
26
|
this.processEnv = processEnv
|
|
25
27
|
this.envKeysFilepath = envKeysFilepath
|
|
26
|
-
this.
|
|
28
|
+
this.noOps = noOps
|
|
27
29
|
|
|
28
30
|
this.processedEnvs = []
|
|
29
31
|
this.readableFilepaths = new Set()
|
|
@@ -32,7 +34,7 @@ class Run {
|
|
|
32
34
|
this.beforeEnv = { ...this.processEnv }
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
runSync () {
|
|
36
38
|
// example
|
|
37
39
|
// envs [
|
|
38
40
|
// { type: 'env', value: 'HELLO=one' },
|
|
@@ -42,7 +44,33 @@ class Run {
|
|
|
42
44
|
|
|
43
45
|
for (const env of this.envs) {
|
|
44
46
|
if (env.type === TYPE_ENV_FILE) {
|
|
45
|
-
this.
|
|
47
|
+
this._injectEnvFileSync(env.value)
|
|
48
|
+
} else if (env.type === TYPE_ENV) {
|
|
49
|
+
this._injectEnv(env.value)
|
|
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)
|
|
46
74
|
} else if (env.type === TYPE_ENV) {
|
|
47
75
|
this._injectEnv(env.value)
|
|
48
76
|
}
|
|
@@ -90,19 +118,64 @@ class Run {
|
|
|
90
118
|
this.processedEnvs.push(row)
|
|
91
119
|
}
|
|
92
120
|
|
|
93
|
-
|
|
121
|
+
_injectEnvFileSync (envFilepath) {
|
|
122
|
+
const row = {}
|
|
123
|
+
row.type = TYPE_ENV_FILE
|
|
124
|
+
row.filepath = envFilepath
|
|
125
|
+
|
|
126
|
+
const filepath = path.resolve(envFilepath)
|
|
127
|
+
try {
|
|
128
|
+
const encoding = detectEncodingSync(filepath)
|
|
129
|
+
const src = fsx.readFileXSync(filepath, { encoding })
|
|
130
|
+
this.readableFilepaths.add(envFilepath)
|
|
131
|
+
|
|
132
|
+
const { privateKeyName } = keyNames(filepath)
|
|
133
|
+
const { privateKeyValue } = keyValuesSync(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
134
|
+
|
|
135
|
+
const {
|
|
136
|
+
parsed,
|
|
137
|
+
errors,
|
|
138
|
+
injected,
|
|
139
|
+
preExisted
|
|
140
|
+
} = new Parse(src, privateKeyValue, this.processEnv, this.overload, privateKeyName).run()
|
|
141
|
+
|
|
142
|
+
row.privateKeyName = privateKeyName
|
|
143
|
+
row.privateKey = privateKeyValue
|
|
144
|
+
row.src = src
|
|
145
|
+
row.parsed = parsed
|
|
146
|
+
row.errors = errors
|
|
147
|
+
row.injected = injected
|
|
148
|
+
row.preExisted = preExisted
|
|
149
|
+
|
|
150
|
+
this.inject(row.parsed) // inject
|
|
151
|
+
|
|
152
|
+
for (const key of Object.keys(injected)) {
|
|
153
|
+
this.uniqueInjectedKeys.add(key) // track uniqueInjectedKeys across multiple files
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (e.code === 'ENOENT' || e.code === 'EISDIR') {
|
|
157
|
+
row.errors = [new Errors({ envFilepath, filepath }).missingEnvFile()]
|
|
158
|
+
} else {
|
|
159
|
+
row.errors = [e]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.processedEnvs.push(row)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async _injectEnvFile (envFilepath) {
|
|
94
167
|
const row = {}
|
|
95
168
|
row.type = TYPE_ENV_FILE
|
|
96
169
|
row.filepath = envFilepath
|
|
97
170
|
|
|
98
171
|
const filepath = path.resolve(envFilepath)
|
|
99
172
|
try {
|
|
100
|
-
const encoding = detectEncoding(filepath)
|
|
101
|
-
const src = fsx.readFileX(filepath, { encoding })
|
|
173
|
+
const encoding = await detectEncoding(filepath)
|
|
174
|
+
const src = await fsx.readFileX(filepath, { encoding })
|
|
102
175
|
this.readableFilepaths.add(envFilepath)
|
|
103
176
|
|
|
104
177
|
const { privateKeyName } = keyNames(filepath)
|
|
105
|
-
const { privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath,
|
|
178
|
+
const { privateKeyValue } = await keyValues(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
106
179
|
|
|
107
180
|
const {
|
|
108
181
|
parsed,
|