@dotenvx/dotenvx 0.17.1 → 0.19.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/README.md +12 -0
- package/package.json +1 -1
- package/src/cli/actions/encrypt.js +6 -21
- package/src/cli/actions/get.js +24 -0
- package/src/cli/actions/scan.js +27 -0
- package/src/cli/dotenvx.js +15 -0
- package/src/lib/main.js +7 -1
- package/src/lib/services/encrypt.js +16 -3
- package/src/lib/services/get.js +47 -0
package/README.md
CHANGED
|
@@ -56,6 +56,18 @@ see [extended quickstart guide](https://dotenvx.com/docs/quickstart)
|
|
|
56
56
|
|
|
57
57
|
More examples
|
|
58
58
|
|
|
59
|
+
* <details><summary>TypeScript 📘</summary><br>
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
$ echo "HELLO=World" > .env
|
|
63
|
+
$ echo "console.log('Hello ' + process.env.HELLO)" > index.ts
|
|
64
|
+
|
|
65
|
+
$ dotenvx run -- npx ts-node index.ts
|
|
66
|
+
Hello World
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
</details>
|
|
70
|
+
|
|
59
71
|
* <details><summary>Python 🐍</summary><br>
|
|
60
72
|
|
|
61
73
|
```sh
|
package/package.json
CHANGED
|
@@ -7,20 +7,6 @@ const helpers = require('./../helpers')
|
|
|
7
7
|
const createSpinner = require('./../../shared/createSpinner')
|
|
8
8
|
const spinner = createSpinner('encrypting')
|
|
9
9
|
|
|
10
|
-
const RESERVED_ENV_FILES = ['.env.vault', '.env.project', '.env.keys', '.env.me', '.env.x']
|
|
11
|
-
|
|
12
|
-
const findEnvFiles = function (directory) {
|
|
13
|
-
const files = fs.readdirSync(directory)
|
|
14
|
-
|
|
15
|
-
const envFiles = files.filter(file =>
|
|
16
|
-
file.startsWith('.env') &&
|
|
17
|
-
!file.endsWith('.previous') &&
|
|
18
|
-
!RESERVED_ENV_FILES.includes(file)
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
return envFiles
|
|
22
|
-
}
|
|
23
|
-
|
|
24
10
|
async function encrypt (directory) {
|
|
25
11
|
spinner.start()
|
|
26
12
|
await helpers.sleep(500) // better dx
|
|
@@ -30,8 +16,6 @@ async function encrypt (directory) {
|
|
|
30
16
|
const options = this.opts()
|
|
31
17
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
32
18
|
|
|
33
|
-
const optionEnvFile = options.envFile || findEnvFiles(directory)
|
|
34
|
-
|
|
35
19
|
try {
|
|
36
20
|
const {
|
|
37
21
|
dotenvKeys,
|
|
@@ -41,10 +25,11 @@ async function encrypt (directory) {
|
|
|
41
25
|
dotenvVaultFile,
|
|
42
26
|
addedVaults,
|
|
43
27
|
existingVaults,
|
|
44
|
-
addedDotenvFilenames
|
|
45
|
-
|
|
28
|
+
addedDotenvFilenames,
|
|
29
|
+
envFile
|
|
30
|
+
} = main.encrypt(directory, options.envFile)
|
|
46
31
|
|
|
47
|
-
logger.verbose(`generating .env.keys from ${
|
|
32
|
+
logger.verbose(`generating .env.keys from ${envFile}`)
|
|
48
33
|
if (addedKeys.length > 0) {
|
|
49
34
|
logger.verbose(`generated ${addedKeys}`)
|
|
50
35
|
}
|
|
@@ -53,7 +38,7 @@ async function encrypt (directory) {
|
|
|
53
38
|
}
|
|
54
39
|
fs.writeFileSync(path.resolve(directory, '.env.keys'), dotenvKeysFile)
|
|
55
40
|
|
|
56
|
-
logger.verbose(`generating .env.vault from ${
|
|
41
|
+
logger.verbose(`generating .env.vault from ${envFile}`)
|
|
57
42
|
if (addedVaults.length > 0) {
|
|
58
43
|
logger.verbose(`encrypting ${addedVaults}`)
|
|
59
44
|
}
|
|
@@ -66,7 +51,7 @@ async function encrypt (directory) {
|
|
|
66
51
|
spinner.succeed(`encrypted to .env.vault (${addedDotenvFilenames})`)
|
|
67
52
|
logger.help2('ℹ commit .env.vault to code: [git commit -am ".env.vault"]')
|
|
68
53
|
} else {
|
|
69
|
-
spinner.done(`no changes (${
|
|
54
|
+
spinner.done(`no changes (${envFile})`)
|
|
70
55
|
}
|
|
71
56
|
|
|
72
57
|
if (addedKeys.length > 0) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const logger = require('./../../shared/logger')
|
|
2
|
+
|
|
3
|
+
const main = require('./../../lib/main')
|
|
4
|
+
|
|
5
|
+
function get (key) {
|
|
6
|
+
logger.debug(`key: ${key}`)
|
|
7
|
+
|
|
8
|
+
const options = this.opts()
|
|
9
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
|
+
|
|
11
|
+
const value = main.get(key, options.envFile, options.overload, options.all)
|
|
12
|
+
|
|
13
|
+
if (typeof value === 'object' && value !== null) {
|
|
14
|
+
if (options.prettyPrint) {
|
|
15
|
+
logger.blank(JSON.stringify(value, null, 2))
|
|
16
|
+
} else {
|
|
17
|
+
logger.blank(value)
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
logger.blank(value)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = get
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const execa = require('execa')
|
|
2
|
+
|
|
3
|
+
const logger = require('./../../shared/logger')
|
|
4
|
+
|
|
5
|
+
async function scan () {
|
|
6
|
+
const options = this.opts()
|
|
7
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
await execa('gitleaks', ['version'])
|
|
11
|
+
} catch (error) {
|
|
12
|
+
logger.error('gitleaks command not found')
|
|
13
|
+
logger.help('? install gitleaks: [brew install gitleaks]')
|
|
14
|
+
logger.help2('? other install options: [https://github.com/gitleaks/gitleaks]')
|
|
15
|
+
process.exit(1)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const { stderr } = await execa('gitleaks', ['detect', '-v'])
|
|
20
|
+
logger.blank(stderr) // gitleaks sends output as stderr for strange reason
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error(error.message)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = scan
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -97,6 +97,11 @@ program.command('genexample')
|
|
|
97
97
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
98
98
|
.action(require('./actions/genexample'))
|
|
99
99
|
|
|
100
|
+
// dotenvx scan
|
|
101
|
+
program.command('scan')
|
|
102
|
+
.description('scan for leaked secrets')
|
|
103
|
+
.action(require('./actions/scan'))
|
|
104
|
+
|
|
100
105
|
// dotenvx ls
|
|
101
106
|
program.command('ls')
|
|
102
107
|
.description('print all .env files in a tree structure')
|
|
@@ -104,6 +109,16 @@ program.command('ls')
|
|
|
104
109
|
.option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
|
|
105
110
|
.action(require('./actions/ls'))
|
|
106
111
|
|
|
112
|
+
// dotenvx get
|
|
113
|
+
program.command('get')
|
|
114
|
+
.description('Return environment variable(s)')
|
|
115
|
+
.argument('[key]', 'environment variable name')
|
|
116
|
+
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
117
|
+
.option('-o, --overload', 'override existing env variables')
|
|
118
|
+
.option('-a, --all', 'include all machine envs as well')
|
|
119
|
+
.option('-pp, --pretty-print', 'pretty print output')
|
|
120
|
+
.action(require('./actions/get'))
|
|
121
|
+
|
|
107
122
|
// dotenvx hub
|
|
108
123
|
program.addCommand(require('./commands/hub'))
|
|
109
124
|
|
package/src/lib/main.js
CHANGED
|
@@ -5,6 +5,7 @@ const dotenvExpand = require('dotenv-expand')
|
|
|
5
5
|
// services
|
|
6
6
|
const Encrypt = require('./services/encrypt')
|
|
7
7
|
const Ls = require('./services/ls')
|
|
8
|
+
const Get = require('./services/get')
|
|
8
9
|
|
|
9
10
|
const config = function (options) {
|
|
10
11
|
return dotenv.config(options)
|
|
@@ -114,6 +115,10 @@ const ls = function (directory, envFile) {
|
|
|
114
115
|
return new Ls(directory, envFile).run()
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
const get = function (key, envFile, overload, all) {
|
|
119
|
+
return new Get(key, envFile, overload, all).run()
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
module.exports = {
|
|
118
123
|
config,
|
|
119
124
|
configDotenv,
|
|
@@ -121,6 +126,7 @@ module.exports = {
|
|
|
121
126
|
parse,
|
|
122
127
|
parseExpand,
|
|
123
128
|
inject,
|
|
129
|
+
encrypt,
|
|
124
130
|
ls,
|
|
125
|
-
|
|
131
|
+
get
|
|
126
132
|
}
|
|
@@ -6,11 +6,12 @@ const DotenvKeys = require('./../helpers/dotenvKeys')
|
|
|
6
6
|
const DotenvVault = require('./../helpers/dotenvVault')
|
|
7
7
|
|
|
8
8
|
const ENCODING = 'utf8'
|
|
9
|
+
const RESERVED_ENV_FILES = ['.env.vault', '.env.project', '.env.keys', '.env.me', '.env.x']
|
|
9
10
|
|
|
10
11
|
class Encrypt {
|
|
11
|
-
constructor (directory = '.', envFile
|
|
12
|
+
constructor (directory = '.', envFile) {
|
|
12
13
|
this.directory = directory
|
|
13
|
-
this.envFile = envFile
|
|
14
|
+
this.envFile = envFile || this._findEnvFiles()
|
|
14
15
|
// calculated
|
|
15
16
|
this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
|
|
16
17
|
this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
|
|
@@ -83,7 +84,8 @@ class Encrypt {
|
|
|
83
84
|
dotenvVaultFile,
|
|
84
85
|
addedVaults,
|
|
85
86
|
existingVaults,
|
|
86
|
-
addedDotenvFilenames
|
|
87
|
+
addedDotenvFilenames,
|
|
88
|
+
envFile: this.envFile
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
@@ -110,6 +112,17 @@ class Encrypt {
|
|
|
110
112
|
|
|
111
113
|
return dotenv.configDotenv(options).parsed
|
|
112
114
|
}
|
|
115
|
+
|
|
116
|
+
_findEnvFiles () {
|
|
117
|
+
const files = fs.readdirSync(this.directory)
|
|
118
|
+
const envFiles = files.filter(file =>
|
|
119
|
+
file.startsWith('.env') &&
|
|
120
|
+
!file.endsWith('.previous') &&
|
|
121
|
+
!RESERVED_ENV_FILES.includes(file)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return envFiles
|
|
125
|
+
}
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
module.exports = Encrypt
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const dotenv = require('dotenv')
|
|
2
|
+
const dotenvExpand = require('dotenv-expand')
|
|
3
|
+
|
|
4
|
+
class Get {
|
|
5
|
+
constructor (key, envFile = '.env', overload = false, all = false) {
|
|
6
|
+
this.key = key
|
|
7
|
+
this.envFile = envFile
|
|
8
|
+
this.overload = overload
|
|
9
|
+
this.all = all
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
run () {
|
|
13
|
+
const clonedEnv = { ...process.env }
|
|
14
|
+
const options = {
|
|
15
|
+
processEnv: clonedEnv,
|
|
16
|
+
path: this.envFile,
|
|
17
|
+
override: this.overload
|
|
18
|
+
}
|
|
19
|
+
const parsed = dotenv.config(options).parsed
|
|
20
|
+
|
|
21
|
+
const expandedEnv = { ...clonedEnv }
|
|
22
|
+
const expandOptions = {
|
|
23
|
+
processEnv: expandedEnv,
|
|
24
|
+
parsed
|
|
25
|
+
}
|
|
26
|
+
dotenvExpand.expand(expandOptions)
|
|
27
|
+
|
|
28
|
+
if (!this.key) {
|
|
29
|
+
// if user wants to return ALL envs (even prior set on machine)
|
|
30
|
+
if (this.all) {
|
|
31
|
+
return expandedEnv
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// typical scenario - return only envs that were identified in the .env file
|
|
35
|
+
const result = {}
|
|
36
|
+
for (const key of Object.keys(parsed)) {
|
|
37
|
+
result[key] = expandedEnv[key]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return result
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return expandedEnv[this.key]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = Get
|