@dotenvx/dotenvx 1.55.1 → 1.56.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.
- package/CHANGELOG.md +12 -1
- package/package.json +3 -3
- package/src/cli/actions/encrypt.js +0 -3
- package/src/cli/actions/get.js +4 -2
- package/src/cli/actions/keypair.js +3 -2
- package/src/cli/actions/rotate.js +10 -5
- package/src/cli/actions/run.js +1 -14
- package/src/cli/actions/set.js +3 -2
- package/src/cli/commands/ext.js +0 -3
- package/src/cli/dotenvx.js +12 -9
- package/src/db/session.js +21 -0
- package/src/lib/extensions/ops.js +98 -0
- package/src/lib/helpers/buildEnvs.js +2 -15
- package/src/lib/helpers/{decryptKeyValue.js → cryptography/decryptKeyValue.js} +1 -1
- package/src/lib/helpers/cryptography/index.js +14 -0
- package/src/lib/helpers/{isPublicKey.js → cryptography/isPublicKey.js} +1 -1
- package/src/lib/helpers/{keypair.js → cryptography/localKeypair.js} +2 -2
- package/src/lib/helpers/cryptography/mutateKeysSrc.js +38 -0
- package/src/lib/helpers/cryptography/mutateSrc.js +24 -0
- package/src/lib/helpers/cryptography/opsKeypair.js +14 -0
- package/src/lib/helpers/cryptography/provision.js +47 -0
- package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +26 -0
- package/src/lib/helpers/envResolution/determine.js +46 -0
- package/src/lib/helpers/{guessEnvironment.js → envResolution/environment.js} +4 -6
- package/src/lib/helpers/envResolution/index.js +8 -0
- package/src/lib/helpers/errors.js +13 -0
- package/src/lib/helpers/findEnvFiles.js +1 -1
- package/src/lib/helpers/isFullyEncrypted.js +3 -3
- package/src/lib/helpers/keyResolution/index.js +13 -0
- package/src/lib/helpers/keyResolution/keyNames.js +24 -0
- package/src/lib/helpers/keyResolution/keyValues.js +85 -0
- package/src/lib/helpers/keyResolution/readFileKey.js +15 -0
- package/src/lib/helpers/keyResolution/readProcessKey.js +7 -0
- package/src/lib/helpers/parse.js +1 -1
- package/src/lib/helpers/prependPublicKey.js +17 -0
- package/src/lib/helpers/preserveShebang.js +16 -0
- package/src/lib/main.d.ts +19 -3
- package/src/lib/main.js +9 -17
- package/src/lib/services/decrypt.js +23 -19
- package/src/lib/services/encrypt.js +41 -136
- package/src/lib/services/get.js +3 -3
- package/src/lib/services/keypair.js +12 -16
- package/src/lib/services/prebuild.js +2 -2
- package/src/lib/services/precommit.js +2 -2
- package/src/lib/services/rotate.js +59 -42
- package/src/lib/services/run.js +29 -112
- package/src/lib/services/sets.js +40 -124
- package/src/lib/helpers/decrypt.js +0 -31
- package/src/lib/helpers/deprecationNotice.js +0 -17
- package/src/lib/helpers/determineEnvs.js +0 -65
- package/src/lib/helpers/encrypt.js +0 -29
- package/src/lib/helpers/findPrivateKey.js +0 -25
- package/src/lib/helpers/findPublicKey.js +0 -15
- package/src/lib/helpers/guessPrivateKeyName.js +0 -18
- package/src/lib/helpers/guessPublicKeyName.js +0 -18
- package/src/lib/helpers/parseEncryptionKeyFromDotenvKey.js +0 -19
- package/src/lib/helpers/parseEnvironmentFromDotenvKey.js +0 -19
- package/src/lib/helpers/smartDotenvPrivateKey.js +0 -89
- package/src/lib/helpers/smartDotenvPublicKey.js +0 -42
- package/src/lib/services/ops.js +0 -136
- /package/src/lib/helpers/{encryptValue.js → cryptography/encryptValue.js} +0 -0
- /package/src/lib/helpers/{isEncrypted.js → cryptography/isEncrypted.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.56.0...main)
|
|
6
|
+
|
|
7
|
+
## [1.56.0](https://github.com/dotenvx/dotenvx/compare/v1.55.1...v1.56.0) (2026-03-19)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `ops off` flag — now respected by `get`, `keypair`, `rotate`, and `encrypt` ([#750](https://github.com/dotenvx/dotenvx/pull/750))
|
|
12
|
+
- `--pp` alias — added as shorthand for `--pretty-print`; toward sunsetting `-pp` ([#750](https://github.com/dotenvx/dotenvx/pull/750))
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
|
|
16
|
+
* Remove support for `.env.vault` files ([#750](https://github.com/dotenvx/dotenvx/pull/750))
|
|
6
17
|
|
|
7
18
|
## [1.55.1](https://github.com/dotenvx/dotenvx/compare/v1.55.0...v1.55.1) (2026-03-13)
|
|
8
19
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.56.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"standard": "standard",
|
|
37
37
|
"standard:fix": "standard --fix",
|
|
38
|
-
"test": "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
39
|
-
"test-coverage": "tap run --show-full-coverage --timeout=60000",
|
|
38
|
+
"test": "tap run --test-env=DOTENVX_OPS_OFF=true --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
39
|
+
"test-coverage": "tap run --test-env=DOTENVX_OPS_OFF=true --show-full-coverage --timeout=60000",
|
|
40
40
|
"testshell": "bash shellspec",
|
|
41
41
|
"prerelease": "npm test && npm run testshell",
|
|
42
42
|
"release": "standard-version"
|
|
@@ -62,9 +62,6 @@ function encrypt () {
|
|
|
62
62
|
|
|
63
63
|
for (const processedEnv of processedEnvs) {
|
|
64
64
|
if (processedEnv.privateKeyAdded) {
|
|
65
|
-
// Ops hook point (first-time key created for this env file):
|
|
66
|
-
// gate with `opsOn` and an Ops-installed check before calling your
|
|
67
|
-
// Ops service (for example: backup/register processedEnv.privateKey).
|
|
68
65
|
logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
|
|
69
66
|
// logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
|
|
70
67
|
|
package/src/cli/actions/get.js
CHANGED
|
@@ -12,6 +12,7 @@ function get (key) {
|
|
|
12
12
|
|
|
13
13
|
const options = this.opts()
|
|
14
14
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
15
|
+
const prettyPrint = options.prettyPrint || options.pp
|
|
15
16
|
|
|
16
17
|
const ignore = options.ignore || []
|
|
17
18
|
|
|
@@ -24,7 +25,8 @@ function get (key) {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
|
-
const
|
|
28
|
+
const opsOn = options.opsOff !== true
|
|
29
|
+
const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, opsOn).run()
|
|
28
30
|
|
|
29
31
|
for (const error of errors || []) {
|
|
30
32
|
if (options.strict) throw error // throw immediately if strict
|
|
@@ -65,7 +67,7 @@ function get (key) {
|
|
|
65
67
|
console.log(inline)
|
|
66
68
|
} else {
|
|
67
69
|
let space = 0
|
|
68
|
-
if (
|
|
70
|
+
if (prettyPrint) {
|
|
69
71
|
space = 2
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -9,8 +9,9 @@ function keypair (key) {
|
|
|
9
9
|
|
|
10
10
|
const options = this.opts()
|
|
11
11
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
12
|
+
const prettyPrint = options.prettyPrint || options.pp
|
|
12
13
|
|
|
13
|
-
const results = main.keypair(options.envFile, key, options.envKeysFile)
|
|
14
|
+
const results = main.keypair(options.envFile, key, options.envKeysFile, options.opsOff)
|
|
14
15
|
|
|
15
16
|
if (typeof results === 'object' && results !== null) {
|
|
16
17
|
// inline shell format - env $(dotenvx keypair --format=shell) your-command
|
|
@@ -25,7 +26,7 @@ function keypair (key) {
|
|
|
25
26
|
// json format
|
|
26
27
|
} else {
|
|
27
28
|
let space = 0
|
|
28
|
-
if (
|
|
29
|
+
if (prettyPrint) {
|
|
29
30
|
space = 2
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -11,17 +11,20 @@ function rotate () {
|
|
|
11
11
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
12
12
|
|
|
13
13
|
const envs = this.envs
|
|
14
|
+
const opsOn = options.opsOff !== true
|
|
14
15
|
|
|
15
16
|
// stdout - should not have a try so that exit codes can surface to stdout
|
|
16
17
|
if (options.stdout) {
|
|
17
18
|
const {
|
|
18
19
|
processedEnvs
|
|
19
|
-
} = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
20
|
+
} = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
|
|
20
21
|
|
|
21
22
|
for (const processedEnv of processedEnvs) {
|
|
22
23
|
console.log(processedEnv.envSrc)
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
if (processedEnv.privateKeyAdded) {
|
|
25
|
+
console.log('')
|
|
26
|
+
console.log(processedEnv.envKeysSrc)
|
|
27
|
+
}
|
|
25
28
|
}
|
|
26
29
|
process.exit(0) // exit early
|
|
27
30
|
} else {
|
|
@@ -30,7 +33,7 @@ function rotate () {
|
|
|
30
33
|
processedEnvs,
|
|
31
34
|
changedFilepaths,
|
|
32
35
|
unchangedFilepaths
|
|
33
|
-
} = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile).run()
|
|
36
|
+
} = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
|
|
34
37
|
|
|
35
38
|
for (const processedEnv of processedEnvs) {
|
|
36
39
|
logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
@@ -46,7 +49,9 @@ function rotate () {
|
|
|
46
49
|
}
|
|
47
50
|
} else if (processedEnv.changed) {
|
|
48
51
|
fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
|
|
49
|
-
|
|
52
|
+
if (processedEnv.privateKeyAdded) {
|
|
53
|
+
fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
|
|
54
|
+
}
|
|
50
55
|
|
|
51
56
|
logger.verbose(`rotated ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
52
57
|
} else {
|
package/src/cli/actions/run.js
CHANGED
|
@@ -5,7 +5,6 @@ const executeCommand = require('./../../lib/helpers/executeCommand')
|
|
|
5
5
|
const Run = require('./../../lib/services/run')
|
|
6
6
|
|
|
7
7
|
const conventions = require('./../../lib/helpers/conventions')
|
|
8
|
-
const DeprecationNotice = require('./../../lib/helpers/deprecationNotice')
|
|
9
8
|
|
|
10
9
|
async function run () {
|
|
11
10
|
const commandArgs = this.args
|
|
@@ -41,26 +40,14 @@ async function run () {
|
|
|
41
40
|
envs = this.envs
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
new DeprecationNotice().dotenvKey() // DEPRECATION NOTICE
|
|
45
|
-
|
|
46
43
|
const {
|
|
47
44
|
processedEnvs,
|
|
48
45
|
readableStrings,
|
|
49
46
|
readableFilepaths,
|
|
50
47
|
uniqueInjectedKeys
|
|
51
|
-
} = new Run(envs, options.overload, process.env
|
|
52
|
-
|
|
53
|
-
if (opsOn) {
|
|
54
|
-
// removed radar feature for now. contact me at mot@dotenvx.com if still needed for your organization.
|
|
55
|
-
// try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
|
|
56
|
-
}
|
|
48
|
+
} = new Run(envs, options.overload, process.env, options.envKeysFile, opsOn).run()
|
|
57
49
|
|
|
58
50
|
for (const processedEnv of processedEnvs) {
|
|
59
|
-
if (processedEnv.type === 'envVaultFile') {
|
|
60
|
-
logger.verbose(`loading env from encrypted ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
61
|
-
logger.debug(`decrypting encrypted env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
51
|
if (processedEnv.type === 'envFile') {
|
|
65
52
|
logger.verbose(`loading env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
|
|
66
53
|
}
|
package/src/cli/actions/set.js
CHANGED
|
@@ -22,12 +22,13 @@ function set (key, value) {
|
|
|
22
22
|
try {
|
|
23
23
|
const envs = this.envs
|
|
24
24
|
const envKeysFilepath = options.envKeysFile
|
|
25
|
+
const opsOn = options.opsOff !== true
|
|
25
26
|
|
|
26
27
|
const {
|
|
27
28
|
processedEnvs,
|
|
28
29
|
changedFilepaths,
|
|
29
30
|
unchangedFilepaths
|
|
30
|
-
} = new Sets(key, value, envs, encrypt, envKeysFilepath).run()
|
|
31
|
+
} = new Sets(key, value, envs, encrypt, envKeysFilepath, opsOn).run()
|
|
31
32
|
|
|
32
33
|
let withEncryption = ''
|
|
33
34
|
|
|
@@ -65,7 +66,7 @@ function set (key, value) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
for (const processedEnv of processedEnvs) {
|
|
68
|
-
if (processedEnv.privateKeyAdded) {
|
|
69
|
+
if (processedEnv.privateKeyAdded) { // TODO: change to localPrivateKeyAdded
|
|
69
70
|
logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
|
|
70
71
|
// logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
|
|
71
72
|
|
package/src/cli/commands/ext.js
CHANGED
|
@@ -10,9 +10,6 @@ ext
|
|
|
10
10
|
.description('🔌 extensions')
|
|
11
11
|
.allowUnknownOption()
|
|
12
12
|
|
|
13
|
-
// list known extensions here you want to display
|
|
14
|
-
ext.addHelpText('after', ' vault 🔐 manage .env.vault files')
|
|
15
|
-
|
|
16
13
|
ext
|
|
17
14
|
.argument('[command]', 'dynamic ext command')
|
|
18
15
|
.argument('[args...]', 'dynamic ext command arguments')
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -13,6 +13,9 @@ const executeDynamic = require('./../lib/helpers/executeDynamic')
|
|
|
13
13
|
const removeDynamicHelpSection = require('./../lib/helpers/removeDynamicHelpSection')
|
|
14
14
|
const removeOptionsHelpParts = require('./../lib/helpers/removeOptionsHelpParts')
|
|
15
15
|
|
|
16
|
+
const Session = require('./../db/session')
|
|
17
|
+
const sesh = new Session()
|
|
18
|
+
|
|
16
19
|
// for use with run
|
|
17
20
|
const envs = []
|
|
18
21
|
function collectEnvs (type) {
|
|
@@ -68,12 +71,11 @@ program.command('run')
|
|
|
68
71
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
69
72
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
70
73
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
71
|
-
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
72
74
|
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
73
75
|
.option('--strict', 'process.exit(1) on any errors', false)
|
|
74
76
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
|
|
75
77
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
76
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
78
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
77
79
|
.action(function (...args) {
|
|
78
80
|
this.envs = envs
|
|
79
81
|
runAction.apply(this, args)
|
|
@@ -88,15 +90,15 @@ program.command('get')
|
|
|
88
90
|
.option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
|
|
89
91
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
90
92
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
91
|
-
.option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
|
|
92
93
|
.option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
|
|
93
94
|
.option('--strict', 'process.exit(1) on any errors', false)
|
|
94
95
|
.option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
|
|
95
96
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
96
97
|
.option('-a, --all', 'include all machine envs as well')
|
|
97
98
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
99
|
+
.option('--pp', 'pretty print output (alias)')
|
|
98
100
|
.option('--format <type>', 'format of the output (json, shell, eval)', 'json')
|
|
99
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
101
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
100
102
|
.action(function (...args) {
|
|
101
103
|
this.envs = envs
|
|
102
104
|
getAction.apply(this, args)
|
|
@@ -115,7 +117,7 @@ program.command('set')
|
|
|
115
117
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
116
118
|
.option('-c, --encrypt', 'encrypt value', true)
|
|
117
119
|
.option('-p, --plain', 'store value as plain text', false)
|
|
118
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
120
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
119
121
|
.action(function (...args) {
|
|
120
122
|
this.envs = envs
|
|
121
123
|
setAction.apply(this, args)
|
|
@@ -129,7 +131,7 @@ program.command('encrypt')
|
|
|
129
131
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
130
132
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
131
133
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
132
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
134
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
133
135
|
.option('--stdout', 'send to stdout')
|
|
134
136
|
.action(function (...args) {
|
|
135
137
|
this.envs = envs
|
|
@@ -144,7 +146,7 @@ program.command('decrypt')
|
|
|
144
146
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
145
147
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
146
148
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
147
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
149
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
148
150
|
.option('--stdout', 'send to stdout')
|
|
149
151
|
.action(function (...args) {
|
|
150
152
|
this.envs = envs
|
|
@@ -159,8 +161,9 @@ program.command('keypair')
|
|
|
159
161
|
.argument('[KEY]', 'environment variable key name')
|
|
160
162
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
161
163
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
162
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
164
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
163
165
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
166
|
+
.option('--pp', 'pretty print output (alias)')
|
|
164
167
|
.option('--format <type>', 'format of the output (json, shell)', 'json')
|
|
165
168
|
.action(keypairAction)
|
|
166
169
|
|
|
@@ -181,7 +184,7 @@ program.command('rotate')
|
|
|
181
184
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
182
185
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
183
186
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
184
|
-
.option('--ops-off', 'disable dotenvx-ops features',
|
|
187
|
+
.option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
|
|
185
188
|
.option('--stdout', 'send to stdout')
|
|
186
189
|
.action(function (...args) {
|
|
187
190
|
this.envs = envs
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const Ops = require('./../lib/extensions/ops')
|
|
2
|
+
|
|
3
|
+
class Session {
|
|
4
|
+
constructor () {
|
|
5
|
+
this.ops = new Ops()
|
|
6
|
+
this.opsStatus = this.ops.status()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//
|
|
10
|
+
// opsOff/On
|
|
11
|
+
//
|
|
12
|
+
opsOn () {
|
|
13
|
+
return this.opsStatus === 'on'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
opsOff () {
|
|
17
|
+
return !this.opsOn()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = Session
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const childProcess = require('child_process')
|
|
3
|
+
|
|
4
|
+
// const { logger } = require('./../../shared/logger')
|
|
5
|
+
|
|
6
|
+
class Ops {
|
|
7
|
+
constructor () {
|
|
8
|
+
this.opsLib = null
|
|
9
|
+
|
|
10
|
+
if (this._isForcedOff()) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// check npm lib
|
|
15
|
+
try { this.opsLib = this._opsNpm() } catch (_e) {}
|
|
16
|
+
|
|
17
|
+
// check binary cli
|
|
18
|
+
if (!this.opsLib) {
|
|
19
|
+
try { this.opsLib = this._opsCli() } catch (_e) {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (this.opsLib) {
|
|
23
|
+
// logger.successv(`🛡️ ops: ${this.opsLib.status()}`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
status () {
|
|
28
|
+
if (this._isForcedOff() || !this.opsLib) {
|
|
29
|
+
return 'off'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return this.opsLib.status()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
keypair (publicKey) {
|
|
36
|
+
if (this._isForcedOff() || !this.opsLib) {
|
|
37
|
+
return {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this.opsLib.keypair(publicKey)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
observe (payload) {
|
|
44
|
+
if (!this._isForcedOff() && this.opsLib && this.opsLib.status() !== 'off') {
|
|
45
|
+
const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
|
|
46
|
+
this.opsLib.observe(encoded)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//
|
|
51
|
+
// private
|
|
52
|
+
//
|
|
53
|
+
_opsNpm () {
|
|
54
|
+
const npmBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
|
|
55
|
+
return this._opsLib(npmBin)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_opsCli () {
|
|
59
|
+
return this._opsLib('dotenvx-ops')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_opsLib (binary) {
|
|
63
|
+
childProcess.execFileSync(binary, ['--version'], { stdio: ['pipe', 'pipe', 'ignore'] })
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
status: () => {
|
|
67
|
+
return childProcess.execFileSync(binary, ['status'], { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
68
|
+
},
|
|
69
|
+
keypair: (publicKey) => {
|
|
70
|
+
const args = ['keypair']
|
|
71
|
+
if (publicKey) {
|
|
72
|
+
args.push(publicKey)
|
|
73
|
+
}
|
|
74
|
+
const output = childProcess.execFileSync(binary, args, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
75
|
+
const parsed = JSON.parse(output.toString())
|
|
76
|
+
return parsed
|
|
77
|
+
},
|
|
78
|
+
observe: (encoded) => {
|
|
79
|
+
try {
|
|
80
|
+
const subprocess = childProcess.spawn(binary, ['observe', encoded], {
|
|
81
|
+
stdio: 'ignore',
|
|
82
|
+
detached: true
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
subprocess.unref() // let it run independently
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// noop
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_isForcedOff () {
|
|
94
|
+
return process.env.DOTENVX_OPS_OFF === 'true'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = Ops
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
|
|
3
1
|
const conventions = require('./conventions')
|
|
4
2
|
const dotenvOptionPaths = require('./dotenvOptionPaths')
|
|
5
|
-
const DeprecationNotice = require('./deprecationNotice')
|
|
6
3
|
|
|
7
|
-
function buildEnvs (options
|
|
4
|
+
function buildEnvs (options) {
|
|
8
5
|
// build envs using user set option.path
|
|
9
6
|
const optionPaths = dotenvOptionPaths(options) // [ '.env' ]
|
|
10
7
|
|
|
@@ -13,18 +10,8 @@ function buildEnvs (options, DOTENV_KEY = undefined) {
|
|
|
13
10
|
envs = conventions(options.convention).concat(envs)
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
new DeprecationNotice({ DOTENV_KEY }).dotenvKey() // DEPRECATION NOTICE
|
|
17
|
-
|
|
18
13
|
for (const optionPath of optionPaths) {
|
|
19
|
-
|
|
20
|
-
if (DOTENV_KEY) {
|
|
21
|
-
envs.push({
|
|
22
|
-
type: 'envVaultFile',
|
|
23
|
-
value: path.join(path.dirname(optionPath), '.env.vault')
|
|
24
|
-
})
|
|
25
|
-
} else {
|
|
26
|
-
envs.push({ type: 'envFile', value: optionPath })
|
|
27
|
-
}
|
|
14
|
+
envs.push({ type: 'envFile', value: optionPath })
|
|
28
15
|
}
|
|
29
16
|
|
|
30
17
|
return envs
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
opsKeypair: require('./opsKeypair'),
|
|
3
|
+
localKeypair: require('./localKeypair'),
|
|
4
|
+
encryptValue: require('./encryptValue'),
|
|
5
|
+
decryptKeyValue: require('./decryptKeyValue'),
|
|
6
|
+
isEncrypted: require('./isEncrypted'),
|
|
7
|
+
isPublicKey: require('./isPublicKey'),
|
|
8
|
+
provision: require('./provision'),
|
|
9
|
+
provisionWithPrivateKey: require('./provisionWithPrivateKey'),
|
|
10
|
+
mutateSrc: require('./mutateSrc'),
|
|
11
|
+
|
|
12
|
+
// other
|
|
13
|
+
isFullyEncrypted: require('./../isFullyEncrypted')
|
|
14
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { PrivateKey } = require('eciesjs')
|
|
2
2
|
|
|
3
|
-
function
|
|
3
|
+
function localKeypair (existingPrivateKey) {
|
|
4
4
|
let kp
|
|
5
5
|
|
|
6
6
|
if (existingPrivateKey) {
|
|
@@ -18,4 +18,4 @@ function keypair (existingPrivateKey) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
module.exports =
|
|
21
|
+
module.exports = localKeypair
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const FIRST_TIME_KEYS_SRC = [
|
|
2
|
+
'#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
|
|
3
|
+
'#/ private decryption keys. DO NOT commit to source control /',
|
|
4
|
+
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
5
|
+
// '#/ backup with: `dotenvx ops backup` /',
|
|
6
|
+
'#/----------------------------------------------------------/'
|
|
7
|
+
].join('\n')
|
|
8
|
+
|
|
9
|
+
const path = require('path')
|
|
10
|
+
const fsx = require('./../fsx')
|
|
11
|
+
|
|
12
|
+
function mutateKeysSrc ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
|
|
13
|
+
const filename = path.basename(envFilepath)
|
|
14
|
+
const filepath = path.resolve(envFilepath)
|
|
15
|
+
let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
16
|
+
if (keysFilepath) {
|
|
17
|
+
resolvedKeysFilepath = path.resolve(keysFilepath)
|
|
18
|
+
}
|
|
19
|
+
const appendPrivateKey = [`# ${filename}`, `${privateKeyName}=${privateKeyValue}`, ''].join('\n')
|
|
20
|
+
|
|
21
|
+
let keysSrc = ''
|
|
22
|
+
if (fsx.existsSync(resolvedKeysFilepath)) {
|
|
23
|
+
keysSrc = fsx.readFileX(resolvedKeysFilepath)
|
|
24
|
+
}
|
|
25
|
+
keysSrc = keysSrc.length > 1 ? keysSrc : `${FIRST_TIME_KEYS_SRC}\n`
|
|
26
|
+
keysSrc = `${keysSrc}\n${appendPrivateKey}`
|
|
27
|
+
|
|
28
|
+
fsx.writeFileX(resolvedKeysFilepath, keysSrc) // TODO: don't write if ops
|
|
29
|
+
|
|
30
|
+
const envKeysFilepath = keysFilepath || path.join(path.dirname(envFilepath), path.basename(resolvedKeysFilepath))
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
keysSrc,
|
|
34
|
+
envKeysFilepath
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = mutateKeysSrc
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const preserveShebang = require('./../preserveShebang')
|
|
3
|
+
const prependPublicKey = require('./../prependPublicKey')
|
|
4
|
+
|
|
5
|
+
function mutateSrc ({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue }) {
|
|
6
|
+
const filename = path.basename(envFilepath)
|
|
7
|
+
const filepath = path.resolve(envFilepath)
|
|
8
|
+
let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
|
|
9
|
+
if (keysFilepath) {
|
|
10
|
+
resolvedKeysFilepath = path.resolve(keysFilepath)
|
|
11
|
+
}
|
|
12
|
+
const relativeFilepath = path.relative(path.dirname(filepath), resolvedKeysFilepath)
|
|
13
|
+
|
|
14
|
+
const ps = preserveShebang(envSrc)
|
|
15
|
+
const prependedPublicKey = prependPublicKey(publicKeyName, publicKeyValue, filename, relativeFilepath)
|
|
16
|
+
|
|
17
|
+
envSrc = `${ps.firstLinePreserved}${prependedPublicKey}\n${ps.envSrc}`
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
envSrc
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = mutateSrc
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const Ops = require('../../extensions/ops')
|
|
2
|
+
|
|
3
|
+
function opsKeypair (existingPublicKey) {
|
|
4
|
+
const kp = new Ops().keypair(existingPublicKey)
|
|
5
|
+
const publicKey = kp.public_key
|
|
6
|
+
const privateKey = kp.private_key
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
publicKey,
|
|
10
|
+
privateKey
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = opsKeypair
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const mutateSrc = require('./mutateSrc')
|
|
2
|
+
const mutateKeysSrc = require('./mutateKeysSrc')
|
|
3
|
+
const opsKeypair = require('./opsKeypair')
|
|
4
|
+
const localKeypair = require('./localKeypair')
|
|
5
|
+
const { keyNames } = require('../keyResolution')
|
|
6
|
+
|
|
7
|
+
function provision ({ envSrc, envFilepath, keysFilepath, opsOn }) {
|
|
8
|
+
opsOn = opsOn === true
|
|
9
|
+
const { publicKeyName, privateKeyName } = keyNames(envFilepath)
|
|
10
|
+
|
|
11
|
+
let publicKey
|
|
12
|
+
let privateKey
|
|
13
|
+
let keysSrc
|
|
14
|
+
let envKeysFilepath
|
|
15
|
+
let privateKeyAdded = false
|
|
16
|
+
|
|
17
|
+
if (opsOn) {
|
|
18
|
+
const kp = opsKeypair()
|
|
19
|
+
publicKey = kp.publicKey
|
|
20
|
+
privateKey = kp.privateKey
|
|
21
|
+
} else {
|
|
22
|
+
const kp = localKeypair()
|
|
23
|
+
publicKey = kp.publicKey
|
|
24
|
+
privateKey = kp.privateKey
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
|
|
28
|
+
envSrc = mutated.envSrc
|
|
29
|
+
|
|
30
|
+
if (!opsOn) {
|
|
31
|
+
const mutated = mutateKeysSrc({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
|
|
32
|
+
keysSrc = mutated.keysSrc
|
|
33
|
+
envKeysFilepath = mutated.envKeysFilepath
|
|
34
|
+
privateKeyAdded = true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
envSrc,
|
|
39
|
+
keysSrc,
|
|
40
|
+
publicKey,
|
|
41
|
+
privateKey,
|
|
42
|
+
privateKeyAdded,
|
|
43
|
+
envKeysFilepath
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = provision
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const Errors = require('./../errors')
|
|
2
|
+
const mutateSrc = require('./mutateSrc')
|
|
3
|
+
const localKeypair = require('./localKeypair')
|
|
4
|
+
|
|
5
|
+
function provisionWithPrivateKey ({ envSrc, envFilepath, keysFilepath, privateKeyValue, publicKeyValue, publicKeyName }) {
|
|
6
|
+
const { publicKey, privateKey } = localKeypair(privateKeyValue) // opsOn doesn't matter here since privateKeyValue was already discovered prior (via ops and local) and passed as privateKeyValue
|
|
7
|
+
|
|
8
|
+
// if derivation doesn't match what's in the file (or preset in env)
|
|
9
|
+
if (publicKeyValue && publicKeyValue !== publicKey) {
|
|
10
|
+
throw new Errors({ publicKey, publicKeyExisting: publicKeyValue }).mispairedPrivateKey()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
|
|
14
|
+
if (!publicKeyValue) {
|
|
15
|
+
const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
|
|
16
|
+
envSrc = mutated.envSrc
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
envSrc,
|
|
21
|
+
publicKey,
|
|
22
|
+
privateKey
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = provisionWithPrivateKey
|