@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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
function detectEncodingSync (filepath) {
|
|
4
|
+
const buffer = fs.readFileSync(filepath)
|
|
5
|
+
|
|
6
|
+
// check for UTF-16LE BOM (Byte Order Mark)
|
|
7
|
+
if (buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
|
|
8
|
+
return 'utf16le'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* c8 ignore start */
|
|
12
|
+
// check for UTF-8 BOM
|
|
13
|
+
if (buffer.length >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
|
|
14
|
+
return 'utf8'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* c8 ignore stop */
|
|
18
|
+
|
|
19
|
+
return 'utf8'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = detectEncodingSync
|
|
@@ -13,6 +13,7 @@ const ISSUE_BY_CODE = {
|
|
|
13
13
|
MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
|
|
14
14
|
MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
|
|
15
15
|
MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
|
|
16
|
+
MISSING_ENV_KEYS_FILE: 'https://github.com/dotenvx/dotenvx/issues/775',
|
|
16
17
|
MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
|
|
17
18
|
MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
|
|
18
19
|
MISSING_LOG_LEVEL: 'must be valid log level',
|
|
@@ -25,6 +26,7 @@ class Errors {
|
|
|
25
26
|
constructor (options = {}) {
|
|
26
27
|
this.filepath = options.filepath
|
|
27
28
|
this.envFilepath = options.envFilepath
|
|
29
|
+
this.envKeysFilepath = options.envKeysFilepath
|
|
28
30
|
|
|
29
31
|
this.key = options.key
|
|
30
32
|
this.privateKey = options.privateKey
|
|
@@ -202,6 +204,19 @@ class Errors {
|
|
|
202
204
|
return e
|
|
203
205
|
}
|
|
204
206
|
|
|
207
|
+
missingEnvKeysFile () {
|
|
208
|
+
const code = 'MISSING_ENV_KEYS_FILE'
|
|
209
|
+
const envKeysFilepath = this.envKeysFilepath || '.env.keys'
|
|
210
|
+
const message = `[${code}] missing file (${envKeysFilepath})`
|
|
211
|
+
const help = `fix: [${ISSUE_BY_CODE[code]}]`
|
|
212
|
+
|
|
213
|
+
const e = new Error(message)
|
|
214
|
+
e.code = code
|
|
215
|
+
e.help = help
|
|
216
|
+
e.messageWithHelp = `${message}. ${help}`
|
|
217
|
+
return e
|
|
218
|
+
}
|
|
219
|
+
|
|
205
220
|
missingEnvFiles () {
|
|
206
221
|
const code = 'MISSING_ENV_FILES'
|
|
207
222
|
const message = `[${code}] no .env* files found`
|
package/src/lib/helpers/fsx.js
CHANGED
|
@@ -2,7 +2,15 @@ const fs = require('fs')
|
|
|
2
2
|
|
|
3
3
|
const ENCODING = 'utf8'
|
|
4
4
|
|
|
5
|
-
function readFileX (filepath, encoding = null) {
|
|
5
|
+
async function readFileX (filepath, encoding = null) {
|
|
6
|
+
if (!encoding) {
|
|
7
|
+
encoding = ENCODING
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return fs.promises.readFile(filepath, encoding)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readFileXSync (filepath, encoding = null) {
|
|
6
14
|
if (!encoding) {
|
|
7
15
|
encoding = ENCODING
|
|
8
16
|
}
|
|
@@ -10,12 +18,26 @@ function readFileX (filepath, encoding = null) {
|
|
|
10
18
|
return fs.readFileSync(filepath, encoding) // utf8 default so it returns a string
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
function
|
|
21
|
+
function writeFileXSync (filepath, str) {
|
|
14
22
|
return fs.writeFileSync(filepath, str, ENCODING) // utf8 always
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
async function writeFileX (filepath, str) {
|
|
26
|
+
return fs.promises.writeFile(filepath, str, ENCODING)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function exists (filepath) {
|
|
30
|
+
try {
|
|
31
|
+
await fs.promises.access(filepath)
|
|
32
|
+
return true
|
|
33
|
+
} catch (_e) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
const fsx = {
|
|
18
39
|
chmodSync: fs.chmodSync,
|
|
40
|
+
exists,
|
|
19
41
|
existsSync: fs.existsSync,
|
|
20
42
|
readdirSync: fs.readdirSync,
|
|
21
43
|
readFileSync: fs.readFileSync,
|
|
@@ -24,7 +46,9 @@ const fsx = {
|
|
|
24
46
|
|
|
25
47
|
// fsx special commands
|
|
26
48
|
readFileX,
|
|
27
|
-
|
|
49
|
+
readFileXSync,
|
|
50
|
+
writeFileX,
|
|
51
|
+
writeFileXSync
|
|
28
52
|
}
|
|
29
53
|
|
|
30
54
|
module.exports = fsx
|
|
@@ -55,12 +55,12 @@ class InstallPrecommitHook {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
_currentHook () {
|
|
58
|
-
return fsx.
|
|
58
|
+
return fsx.readFileXSync(this.hookPath)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
_createHook () {
|
|
62
62
|
// If the pre-commit file doesn't exist, create a new one with the hookScript
|
|
63
|
-
fsx.
|
|
63
|
+
fsx.writeFileXSync(this.hookPath, HOOK_SCRIPT)
|
|
64
64
|
fsx.chmodSync(this.hookPath, '755') // Make the file executable
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
keyNames: require('./keyNames'),
|
|
3
3
|
keyValues: require('./keyValues'),
|
|
4
|
+
keyValuesSync: require('./keyValuesSync'),
|
|
4
5
|
|
|
5
6
|
// private
|
|
6
|
-
// private keys are resolved via
|
|
7
|
+
// private keys are resolved via keyValuesSync()
|
|
7
8
|
|
|
8
9
|
// other
|
|
9
10
|
readProcessKey: require('./readProcessKey'),
|
|
10
11
|
readFileKey: require('./readFileKey'),
|
|
12
|
+
readFileKeySync: require('./readFileKeySync'),
|
|
11
13
|
guessPrivateKeyFilename: require('./../guessPrivateKeyFilename'),
|
|
12
14
|
dotenvPrivateKeyNames: require('./../dotenvPrivateKeyNames')
|
|
13
15
|
}
|
|
@@ -7,15 +7,15 @@ const readProcessKey = require('./readProcessKey')
|
|
|
7
7
|
const readFileKey = require('./readFileKey')
|
|
8
8
|
const opsKeypair = require('../cryptography/opsKeypair')
|
|
9
9
|
|
|
10
|
-
function invertForPrivateKeyName (filepath) {
|
|
10
|
+
async function invertForPrivateKeyName (filepath) {
|
|
11
11
|
const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
|
|
12
12
|
const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
|
|
13
13
|
|
|
14
|
-
if (!fsx.
|
|
14
|
+
if (!(await fsx.exists(filepath))) {
|
|
15
15
|
return null
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const envSrc = fsx.readFileX(filepath)
|
|
18
|
+
const envSrc = await fsx.readFileX(filepath)
|
|
19
19
|
const envParsed = dotenvParse(envSrc)
|
|
20
20
|
|
|
21
21
|
let publicKeyName
|
|
@@ -32,9 +32,9 @@ function invertForPrivateKeyName (filepath) {
|
|
|
32
32
|
return null
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function keyValues (filepath, opts = {}) {
|
|
35
|
+
async function keyValues (filepath, opts = {}) {
|
|
36
36
|
let keysFilepath = opts.keysFilepath || null
|
|
37
|
-
const
|
|
37
|
+
const noOps = opts.noOps === true
|
|
38
38
|
const names = keyNames(filepath)
|
|
39
39
|
const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
|
|
40
40
|
let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
|
|
@@ -45,7 +45,7 @@ function keyValues (filepath, opts = {}) {
|
|
|
45
45
|
// public key: process.env first, then .env*
|
|
46
46
|
publicKey = readProcessKey(publicKeyName)
|
|
47
47
|
if (!publicKey) {
|
|
48
|
-
publicKey = readFileKey(publicKeyName, filepath) || null
|
|
48
|
+
publicKey = await readFileKey(publicKeyName, filepath) || null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// private key: process.env first, then .env.keys, then invert public key
|
|
@@ -57,28 +57,28 @@ function keyValues (filepath, opts = {}) {
|
|
|
57
57
|
keysFilepath = path.resolve(path.dirname(filepath), '.env.keys') // typical scenario
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
privateKey = readFileKey(privateKeyName, keysFilepath)
|
|
60
|
+
privateKey = await readFileKey(privateKeyName, keysFilepath)
|
|
61
61
|
}
|
|
62
62
|
// invert
|
|
63
63
|
if (!privateKey) {
|
|
64
|
-
privateKeyName = invertForPrivateKeyName(filepath)
|
|
64
|
+
privateKeyName = await invertForPrivateKeyName(filepath)
|
|
65
65
|
if (privateKeyName) {
|
|
66
66
|
privateKey = readProcessKey(privateKeyName)
|
|
67
67
|
if (!privateKey) {
|
|
68
|
-
privateKey = readFileKey(privateKeyName, keysFilepath)
|
|
68
|
+
privateKey = await readFileKey(privateKeyName, keysFilepath)
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// ops
|
|
74
|
-
if (
|
|
75
|
-
const kp = opsKeypair(publicKey)
|
|
74
|
+
if (!noOps && !privateKey && publicKey && publicKey.length > 0) {
|
|
75
|
+
const kp = await opsKeypair(publicKey)
|
|
76
76
|
privateKey = kp.privateKey
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
return {
|
|
80
80
|
publicKeyValue: publicKey || null, // important to make sure name is rendered
|
|
81
|
-
privateKeyValue: privateKey || null //
|
|
81
|
+
privateKeyValue: privateKey || null // important to make sure name is rendered
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
const fsx = require('./../fsx')
|
|
4
|
+
const dotenvParse = require('./../dotenvParse')
|
|
5
|
+
const keyNames = require('./keyNames')
|
|
6
|
+
const readProcessKey = require('./readProcessKey')
|
|
7
|
+
const readFileKeySync = require('./readFileKeySync')
|
|
8
|
+
const opsKeypairSync = require('../cryptography/opsKeypairSync')
|
|
9
|
+
|
|
10
|
+
function invertForPrivateKeyName (filepath) {
|
|
11
|
+
const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
|
|
12
|
+
const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
|
|
13
|
+
|
|
14
|
+
if (!fsx.existsSync(filepath)) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const envSrc = fsx.readFileXSync(filepath)
|
|
19
|
+
const envParsed = dotenvParse(envSrc)
|
|
20
|
+
|
|
21
|
+
let publicKeyName
|
|
22
|
+
for (const keyName of Object.keys(envParsed)) {
|
|
23
|
+
if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
|
|
24
|
+
publicKeyName = keyName // find DOTENV_PUBLIC_KEY* in filename
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (publicKeyName) {
|
|
29
|
+
return publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA) // return inverted (DOTENV_PUBLIC_KEY* -> DOTENV_PRIVATE_KEY*) if found
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function keyValuesSync (filepath, opts = {}) {
|
|
36
|
+
let keysFilepath = opts.keysFilepath || null
|
|
37
|
+
const noOps = opts.noOps === true
|
|
38
|
+
const names = keyNames(filepath)
|
|
39
|
+
const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
|
|
40
|
+
let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
|
|
41
|
+
|
|
42
|
+
let publicKey = null
|
|
43
|
+
let privateKey = null
|
|
44
|
+
|
|
45
|
+
// public key: process.env first, then .env*
|
|
46
|
+
publicKey = readProcessKey(publicKeyName)
|
|
47
|
+
if (!publicKey) {
|
|
48
|
+
publicKey = readFileKeySync(publicKeyName, filepath) || null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// private key: process.env first, then .env.keys, then invert public key
|
|
52
|
+
privateKey = readProcessKey(privateKeyName)
|
|
53
|
+
if (!privateKey) {
|
|
54
|
+
if (keysFilepath) { // user specified -fk flag
|
|
55
|
+
keysFilepath = path.resolve(keysFilepath)
|
|
56
|
+
} else {
|
|
57
|
+
keysFilepath = path.resolve(path.dirname(filepath), '.env.keys') // typical scenario
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
privateKey = readFileKeySync(privateKeyName, keysFilepath)
|
|
61
|
+
}
|
|
62
|
+
// invert
|
|
63
|
+
if (!privateKey) {
|
|
64
|
+
privateKeyName = invertForPrivateKeyName(filepath)
|
|
65
|
+
if (privateKeyName) {
|
|
66
|
+
privateKey = readProcessKey(privateKeyName)
|
|
67
|
+
if (!privateKey) {
|
|
68
|
+
privateKey = readFileKeySync(privateKeyName, keysFilepath)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ops
|
|
74
|
+
if (!noOps && !privateKey && publicKey && publicKey.length > 0) {
|
|
75
|
+
const kp = opsKeypairSync(publicKey)
|
|
76
|
+
privateKey = kp.privateKey
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
publicKeyValue: publicKey || null, // important to make sure name is rendered
|
|
81
|
+
privateKeyValue: privateKey || null // important to make sure name is rendered
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = keyValuesSync
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
const fsx = require('./../fsx')
|
|
2
2
|
const dotenvParse = require('./../dotenvParse')
|
|
3
3
|
|
|
4
|
-
function readFileKey (keyName, filepath) {
|
|
5
|
-
if (fsx.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
async function readFileKey (keyName, filepath) {
|
|
5
|
+
if (!(await fsx.exists(filepath))) {
|
|
6
|
+
return undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const src = await fsx.readFileX(filepath)
|
|
10
|
+
const parsed = dotenvParse(src)
|
|
11
|
+
|
|
12
|
+
if (parsed[keyName] && parsed[keyName].length > 0) {
|
|
13
|
+
return parsed[keyName]
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const fsx = require('./../fsx')
|
|
2
|
+
const dotenvParse = require('./../dotenvParse')
|
|
3
|
+
|
|
4
|
+
function readFileKeySync (keyName, filepath) {
|
|
5
|
+
if (fsx.existsSync(filepath)) {
|
|
6
|
+
const src = fsx.readFileXSync(filepath)
|
|
7
|
+
const parsed = dotenvParse(src)
|
|
8
|
+
|
|
9
|
+
if (parsed[keyName] && parsed[keyName].length > 0) {
|
|
10
|
+
return parsed[keyName]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = readFileKeySync
|
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
const SAMPLE_ENV_KIT = `
|
|
2
2
|
HELLO="Dotenvx"
|
|
3
3
|
|
|
4
|
-
# ──
|
|
4
|
+
# ── Hosting ──────────────────────────────────────
|
|
5
|
+
AWS_ACCESS_KEY_ID="xxxx"
|
|
6
|
+
AWS_SECRET_ACCESS_KEY="xxxx"
|
|
5
7
|
DATABASE_URL="postgresql://postgres:pass@db.ref.supabase.co:5432/postgres"
|
|
8
|
+
VERCEL_TOKEN="vcp_xxxx"
|
|
6
9
|
|
|
7
|
-
# ──
|
|
8
|
-
AUTH0_CLIENT_ID="xxxx"
|
|
9
|
-
AUTH0_CLIENT_SECRET="xxxx"
|
|
10
|
-
|
|
11
|
-
# ── AI / LLM ────────────────────────────────────
|
|
10
|
+
# ── AI ───────────────────────────────────────────
|
|
12
11
|
OPENAI_API_KEY="sk-xxxx"
|
|
13
12
|
ANTHROPIC_API_KEY="sk-ant-xxxx"
|
|
14
13
|
|
|
15
|
-
# ──
|
|
14
|
+
# ── Infrastructure ───────────────────────────────
|
|
15
|
+
AUTH0_CLIENT_ID="xxxx"
|
|
16
|
+
AUTH0_CLIENT_SECRET="xxxx"
|
|
16
17
|
RESEND_API_KEY="re_xxxx"
|
|
17
|
-
|
|
18
|
-
# ── Cloud Storage ────────────────────────────────
|
|
19
|
-
AWS_ACCESS_KEY_ID="xxxx"
|
|
20
|
-
AWS_SECRET_ACCESS_KEY="xxxx"
|
|
21
|
-
|
|
22
|
-
# ── Analytics / Monitoring ───────────────────────
|
|
23
|
-
SENTRY_DSN="https://hex@o1234.ingest.us.sentry.io/1234567"
|
|
24
|
-
|
|
25
|
-
# ── Payments ─────────────────────────────────────
|
|
26
18
|
STRIPE_API_KEY="sk_test_xxxx"
|
|
27
|
-
|
|
28
|
-
# ── Feature Flags ────────────────────────────────
|
|
29
19
|
FLAGSMITH_ENV_ID="xxxx"
|
|
30
|
-
|
|
31
|
-
# ── CI/CD / Deployment ──────────────────────────
|
|
32
|
-
VERCEL_TOKEN="vcp_xxxx"
|
|
20
|
+
SENTRY_DSN="https://hex@o1234.ingest.us.sentry.io/1234567"
|
|
33
21
|
`.trimStart()
|
|
34
22
|
|
|
35
23
|
module.exports = SAMPLE_ENV_KIT
|
package/src/lib/main.d.ts
CHANGED
|
@@ -151,7 +151,12 @@ export interface DotenvConfigOptions {
|
|
|
151
151
|
* Turn off Dotenvx Ops features - https://dotenvx.com/ops
|
|
152
152
|
*
|
|
153
153
|
* @default false
|
|
154
|
-
* @example require('@dotenvx/dotenvx').config({
|
|
154
|
+
* @example require('@dotenvx/dotenvx').config({ noOps: true })
|
|
155
|
+
*/
|
|
156
|
+
noOps?: boolean;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @deprecated use `noOps` instead.
|
|
155
160
|
*/
|
|
156
161
|
opsOff?: boolean;
|
|
157
162
|
}
|
|
@@ -210,7 +215,12 @@ export interface SetOptions {
|
|
|
210
215
|
* Turn off Dotenvx Ops features - https://dotenvx.com/ops
|
|
211
216
|
*
|
|
212
217
|
* @default false
|
|
213
|
-
* @example require('@dotenvx/dotenvx').set(key, value, {
|
|
218
|
+
* @example require('@dotenvx/dotenvx').set(key, value, { noOps: true })
|
|
219
|
+
*/
|
|
220
|
+
noOps?: boolean;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @deprecated use `noOps` instead.
|
|
214
224
|
*/
|
|
215
225
|
opsOff?: boolean;
|
|
216
226
|
}
|
|
@@ -225,7 +235,8 @@ export type SetProcessedEnv = {
|
|
|
225
235
|
encryptedValue?: string;
|
|
226
236
|
publicKey?: string;
|
|
227
237
|
privateKey?: string;
|
|
228
|
-
|
|
238
|
+
localPrivateKeyAdded?: boolean;
|
|
239
|
+
remotePrivateKeyAdded?: boolean;
|
|
229
240
|
privateKeyName?: string;
|
|
230
241
|
error?: Error;
|
|
231
242
|
};
|
|
@@ -285,7 +296,12 @@ export interface GetOptions {
|
|
|
285
296
|
* Turn off Dotenvx Ops features - https://dotenvx.com/ops
|
|
286
297
|
*
|
|
287
298
|
* @default false
|
|
288
|
-
* @example require('@dotenvx/dotenvx').get('KEY', {
|
|
299
|
+
* @example require('@dotenvx/dotenvx').get('KEY', { noOps: true })
|
|
300
|
+
*/
|
|
301
|
+
noOps?: boolean;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated use `noOps` instead.
|
|
289
305
|
*/
|
|
290
306
|
opsOff?: boolean;
|
|
291
307
|
}
|
package/src/lib/main.js
CHANGED
|
@@ -12,6 +12,7 @@ const Sets = require('./services/sets')
|
|
|
12
12
|
const Get = require('./services/get')
|
|
13
13
|
const Keypair = require('./services/keypair')
|
|
14
14
|
const Genexample = require('./services/genexample')
|
|
15
|
+
const Session = require('./../db/session')
|
|
15
16
|
|
|
16
17
|
// helpers
|
|
17
18
|
const buildEnvs = require('./helpers/buildEnvs')
|
|
@@ -39,27 +40,22 @@ const config = function (options = {}) {
|
|
|
39
40
|
// envKeysFile
|
|
40
41
|
const envKeysFile = options.envKeysFile
|
|
41
42
|
|
|
42
|
-
// dotenvx-ops related
|
|
43
|
-
const opsOn = options.opsOff !== true
|
|
44
|
-
|
|
45
43
|
if (options) {
|
|
46
44
|
setLogLevel(options)
|
|
47
45
|
setLogName(options)
|
|
48
46
|
setLogVersion(options)
|
|
49
47
|
}
|
|
50
48
|
|
|
49
|
+
// dotenvx-ops related
|
|
50
|
+
const noOps = resolveNoOps(options)
|
|
51
|
+
|
|
51
52
|
try {
|
|
52
53
|
const envs = buildEnvs(options)
|
|
53
54
|
const {
|
|
54
55
|
processedEnvs,
|
|
55
56
|
readableFilepaths,
|
|
56
57
|
uniqueInjectedKeys
|
|
57
|
-
} = new Run(envs, overload, processEnv, envKeysFile,
|
|
58
|
-
|
|
59
|
-
if (opsOn) {
|
|
60
|
-
// removed radar feature for now. contact me at mot@dotenvx.com if still needed for your organization.
|
|
61
|
-
// try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
|
|
62
|
-
}
|
|
58
|
+
} = new Run(envs, overload, processEnv, envKeysFile, noOps).runSync()
|
|
63
59
|
|
|
64
60
|
let lastError
|
|
65
61
|
/** @type {Record<string, string>} */
|
|
@@ -169,13 +165,13 @@ const set = function (key, value, options = {}) {
|
|
|
169
165
|
|
|
170
166
|
const envs = buildEnvs(options)
|
|
171
167
|
const envKeysFilepath = options.envKeysFile
|
|
172
|
-
const
|
|
168
|
+
const noOps = resolveNoOps(options)
|
|
173
169
|
|
|
174
170
|
const {
|
|
175
171
|
processedEnvs,
|
|
176
172
|
changedFilepaths,
|
|
177
173
|
unchangedFilepaths
|
|
178
|
-
} = new Sets(key, value, envs, encrypt, envKeysFilepath,
|
|
174
|
+
} = new Sets(key, value, envs, encrypt, envKeysFilepath, noOps).runSync()
|
|
179
175
|
|
|
180
176
|
let withEncryption = ''
|
|
181
177
|
|
|
@@ -191,15 +187,23 @@ const set = function (key, value, options = {}) {
|
|
|
191
187
|
const message = error.messageWithHelp || (error.help ? `${error.message}. ${error.help}` : error.message)
|
|
192
188
|
logger.warn(message)
|
|
193
189
|
} else {
|
|
194
|
-
fsx.
|
|
190
|
+
fsx.writeFileXSync(processedEnv.filepath, processedEnv.envSrc)
|
|
195
191
|
|
|
196
192
|
logger.verbose(`${processedEnv.key} set${withEncryption} (${processedEnv.envFilepath})`)
|
|
197
193
|
logger.debug(`${processedEnv.key} set${withEncryption} to ${processedEnv.value} (${processedEnv.envFilepath})`)
|
|
198
194
|
}
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
const
|
|
197
|
+
let keyAddedSuffix = ''
|
|
198
|
+
const localKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.localPrivateKeyAdded)
|
|
199
|
+
const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
|
|
200
|
+
|
|
201
|
+
if (localKeyAddedEnv) {
|
|
202
|
+
keyAddedSuffix = ` + key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
|
|
203
|
+
}
|
|
204
|
+
if (remoteKeyAddedEnv) {
|
|
205
|
+
keyAddedSuffix = ' + key ⛨'
|
|
206
|
+
}
|
|
203
207
|
|
|
204
208
|
if (changedFilepaths.length > 0) {
|
|
205
209
|
if (encrypt) {
|
|
@@ -207,11 +211,11 @@ const set = function (key, value, options = {}) {
|
|
|
207
211
|
} else {
|
|
208
212
|
logger.success(`◇ set ${key} (${changedFilepaths.join(',')})`)
|
|
209
213
|
}
|
|
210
|
-
} else if (encrypt &&
|
|
211
|
-
const keyAddedEnvFilepath =
|
|
214
|
+
} else if (encrypt && localKeyAddedEnv) {
|
|
215
|
+
const keyAddedEnvFilepath = localKeyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
|
|
212
216
|
logger.success(`◈ encrypted ${key} (${keyAddedEnvFilepath})${keyAddedSuffix}`)
|
|
213
217
|
} else if (unchangedFilepaths.length > 0) {
|
|
214
|
-
logger.info(`○ no
|
|
218
|
+
logger.info(`○ no change (${unchangedFilepaths})`)
|
|
215
219
|
} else {
|
|
216
220
|
// do nothing
|
|
217
221
|
}
|
|
@@ -228,12 +232,12 @@ const set = function (key, value, options = {}) {
|
|
|
228
232
|
/* @type {import('./main').get} */
|
|
229
233
|
const get = function (key, options = {}) {
|
|
230
234
|
const envs = buildEnvs(options)
|
|
231
|
-
const
|
|
235
|
+
const noOps = resolveNoOps(options)
|
|
232
236
|
|
|
233
237
|
// ignore
|
|
234
238
|
const ignore = options.ignore || []
|
|
235
239
|
|
|
236
|
-
const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile,
|
|
240
|
+
const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, noOps).runSync()
|
|
237
241
|
|
|
238
242
|
for (const error of errors || []) {
|
|
239
243
|
if (ignore.includes(error.code)) {
|
|
@@ -286,9 +290,8 @@ const genexample = function (directory, envFile) {
|
|
|
286
290
|
}
|
|
287
291
|
|
|
288
292
|
/** @type {import('./main').keypair} */
|
|
289
|
-
const keypair = function (envFile, key, envKeysFile = null,
|
|
290
|
-
const
|
|
291
|
-
const keypairs = new Keypair(envFile, envKeysFile, opsOn).run()
|
|
293
|
+
const keypair = function (envFile, key, envKeysFile = null, noOps = false) {
|
|
294
|
+
const keypairs = new Keypair(envFile, envKeysFile, noOps).runSync()
|
|
292
295
|
if (key) {
|
|
293
296
|
return keypairs[key]
|
|
294
297
|
} else {
|
|
@@ -296,6 +299,11 @@ const keypair = function (envFile, key, envKeysFile = null, opsOff = false) {
|
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
301
|
|
|
302
|
+
function resolveNoOps (options = {}) {
|
|
303
|
+
const sesh = new Session()
|
|
304
|
+
return options.noOps === true || options.opsOff === true || sesh.noOpsSync()
|
|
305
|
+
}
|
|
306
|
+
|
|
299
307
|
module.exports = {
|
|
300
308
|
// dotenv proxies
|
|
301
309
|
config,
|
|
@@ -25,19 +25,19 @@ const dotenvParse = require('./../helpers/dotenvParse')
|
|
|
25
25
|
const detectEncoding = require('./../helpers/detectEncoding')
|
|
26
26
|
|
|
27
27
|
class Decrypt {
|
|
28
|
-
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null,
|
|
28
|
+
constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noOps = false) {
|
|
29
29
|
this.envs = determine(envs, process.env)
|
|
30
30
|
this.key = key
|
|
31
31
|
this.excludeKey = excludeKey
|
|
32
32
|
this.envKeysFilepath = envKeysFilepath
|
|
33
|
-
this.
|
|
33
|
+
this.noOps = noOps
|
|
34
34
|
|
|
35
35
|
this.processedEnvs = []
|
|
36
36
|
this.changedFilepaths = new Set()
|
|
37
37
|
this.unchangedFilepaths = new Set()
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
run () {
|
|
40
|
+
async run () {
|
|
41
41
|
// example
|
|
42
42
|
// envs [
|
|
43
43
|
// { type: 'envFile', value: '.env' }
|
|
@@ -51,7 +51,7 @@ class Decrypt {
|
|
|
51
51
|
|
|
52
52
|
for (const env of this.envs) {
|
|
53
53
|
if (env.type === TYPE_ENV_FILE) {
|
|
54
|
-
this._decryptEnvFile(env.value)
|
|
54
|
+
await this._decryptEnvFile(env.value)
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -62,7 +62,7 @@ class Decrypt {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
_decryptEnvFile (envFilepath) {
|
|
65
|
+
async _decryptEnvFile (envFilepath) {
|
|
66
66
|
const row = {}
|
|
67
67
|
row.keys = []
|
|
68
68
|
row.type = TYPE_ENV_FILE
|
|
@@ -72,12 +72,12 @@ class Decrypt {
|
|
|
72
72
|
row.envFilepath = envFilepath
|
|
73
73
|
|
|
74
74
|
try {
|
|
75
|
-
const encoding = detectEncoding(filepath)
|
|
76
|
-
let envSrc = fsx.readFileX(filepath, { encoding })
|
|
75
|
+
const encoding = await detectEncoding(filepath)
|
|
76
|
+
let envSrc = await fsx.readFileX(filepath, { encoding })
|
|
77
77
|
const envParsed = dotenvParse(envSrc)
|
|
78
78
|
|
|
79
79
|
const { privateKeyName } = keyNames(envFilepath)
|
|
80
|
-
const { privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath,
|
|
80
|
+
const { privateKeyValue } = await keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
|
|
81
81
|
|
|
82
82
|
row.privateKey = privateKeyValue
|
|
83
83
|
row.privateKeyName = privateKeyName
|