@dotenvx/dotenvx 0.2.23
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 +274 -0
- package/package.json +45 -0
- package/src/cli/dotenvx.js +116 -0
- package/src/cli/helpers.js +28 -0
- package/src/lib/main.js +28 -0
- package/src/shared/logger.js +26 -0
- package/src/shared/packageJson.js +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
*a better dotenv*–from the creator of [`dotenv`](https://github.com/motdotla/dotenv).
|
|
4
|
+
|
|
5
|
+
* run anywhere (cross-platform)
|
|
6
|
+
* multi-environment
|
|
7
|
+
* encrypted envs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Quickstart
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
brew install dotenvx/brew/dotenvx
|
|
16
|
+
```
|
|
17
|
+
> * [other ways to install](#other-ways-to-install)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Run Anywhere
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
$ echo "HELLO=World" > .env && echo "console.log('Hello ' + process.env.HELLO)" > index.js
|
|
25
|
+
|
|
26
|
+
$ node index.js
|
|
27
|
+
Hello undefined
|
|
28
|
+
|
|
29
|
+
$ dotenv run -- node index.js
|
|
30
|
+
Hello World
|
|
31
|
+
> :-D
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
More examples
|
|
35
|
+
|
|
36
|
+
* <details><summary>Python 🐍</summary><br>
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
$ echo 'import os;print("Hello " + os.getenv("HELLO", ""))' > index.py
|
|
40
|
+
|
|
41
|
+
$ dotenv run -- python3 index.py
|
|
42
|
+
Hello World
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
</details>
|
|
46
|
+
* <details><summary>PHP 🐘</summary><br>
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
$ echo '<?php echo "Hello {$_SERVER["HELLO"]}\n";' > index.php
|
|
50
|
+
|
|
51
|
+
$ dotenv run -- php index.php
|
|
52
|
+
Hello World
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
* <details><summary>Ruby 💎</summary><br>
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
$ echo 'puts "Hello #{ENV["HELLO"]}"' > index.rb
|
|
60
|
+
|
|
61
|
+
$ dotenv run -- ruby index.rb
|
|
62
|
+
Hello World
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
</details>
|
|
66
|
+
* <details><summary>Rust 🦀</summary><br>
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
$ echo 'fn main() {let hello = std::env::var("HELLO").unwrap_or("".to_string());println!("Hello {hello}");}' > src/main.rs
|
|
70
|
+
|
|
71
|
+
$ dotenv run -- cargo run
|
|
72
|
+
Hello World
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
</details>
|
|
76
|
+
* <details><summary>Frameworks ▲</summary><br>
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
$ dotenv run -- next dev
|
|
80
|
+
$ dotenv run -- npm start
|
|
81
|
+
$ dotenv run -- bin/rails s
|
|
82
|
+
$ dotenv run -- php artisan serve
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
</details>
|
|
86
|
+
* <details><summary>Docker 🐳</summary><br>
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
# run as a command-line tool
|
|
90
|
+
docker run -it --rm -v $(pwd):/app dotenv/dotenv run -- node index.js
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
# include in a Dockerfile
|
|
95
|
+
# example coming soon
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</details>
|
|
99
|
+
|
|
100
|
+
* <details><summary>CI/CDs 🐙</summary><br>
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
examples coming soon
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
</details>
|
|
107
|
+
* <details><summary>Platforms</summary><br>
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
examples coming soon
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
</details>
|
|
114
|
+
* <details><summary>npx</summary><br>
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
# alternatively use npx
|
|
118
|
+
$ npx @dotenv/dotenv run -- node index.js
|
|
119
|
+
$ npx @dotenv/dotenv run -- next dev
|
|
120
|
+
$ npx @dotenv/dotenv run -- npm start
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
</details>
|
|
124
|
+
* <details><summary>Git</summary><br>
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
# use as a git submodule
|
|
128
|
+
$ git dotenv run -- node index.js
|
|
129
|
+
$ git dotenv run -- next dev
|
|
130
|
+
$ git dotenv run -- npm start
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
</details>
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## Multiple Environments
|
|
138
|
+
|
|
139
|
+
Pass the `--env-file` flag (shorthand `-f`) to run any environment from a `.env.environment` file.
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
$ dotenv run --env-file=.env.production -- node index.js
|
|
143
|
+
[dotenv][INFO] Injecting 12 production environment variables into your application process
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Combine multiple `.env` files if you like.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
$ dotenv run --env-file=.env.local --env-file=.env -- node index.js
|
|
150
|
+
[dotenv][INFO] Injecting 12 local, 1 development environment variables into your application process
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
## Encrypt Your Env Files
|
|
156
|
+
|
|
157
|
+
WIP
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Usage
|
|
162
|
+
|
|
163
|
+
### Guide
|
|
164
|
+
|
|
165
|
+
Begin by creating a simple 'hello world' program.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
// index.js
|
|
169
|
+
console.log(`Hello ${process.env.HELLO}`)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Run it.
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
$ node index.js
|
|
176
|
+
Hello undefined
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Run it with `dotenv`.
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
$ dotenv run -- node index.js
|
|
183
|
+
[dotenv@x.x.x][WARN] ENOENT: no such file or directory, open '/../../.env'
|
|
184
|
+
Hello undefined
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
It warns you when there is no `.env` file (pass the `--quiet` flag to suppress these warnings).
|
|
188
|
+
|
|
189
|
+
Create the `.env` file.
|
|
190
|
+
|
|
191
|
+
```ini
|
|
192
|
+
# env
|
|
193
|
+
JELLO="World"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Run it again.
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
$ dotenv run -- node index.js
|
|
200
|
+
[dotenv@x.x.x][INFO] Injecting 0 environment variables into your application process
|
|
201
|
+
Hello undefined
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Hrm, still undefined. Pass the `--debug` flag to debug the issue. I'll give you a hint: 🍮
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
$ dotenv run --debug -- node index.js
|
|
208
|
+
[dotenv@x.x.x][VERBOSE] Loading env from /../../.env
|
|
209
|
+
[dotenv@x.x.x][DEBUG] Reading env from /../../.env
|
|
210
|
+
[dotenv@x.x.x][DEBUG] Parsing env from /../../.env
|
|
211
|
+
[dotenv@x.x.x][DEBUG] {"JELLO":"World"}
|
|
212
|
+
|
|
213
|
+
# Oops, HELLO not JELLO ^^
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Fix your `.env` file.
|
|
217
|
+
|
|
218
|
+
```ini
|
|
219
|
+
# .env
|
|
220
|
+
HELLO="World"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
One last time. [Le tired](https://youtu.be/kCpjgl2baLs?t=45).
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
$ dotenv run -- node index.js
|
|
227
|
+
[dotenv@x.x.x][INFO] Injecting 0 environment variables into your application process
|
|
228
|
+
Hello undefined
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
## Install
|
|
235
|
+
|
|
236
|
+
Installing with [`brew`](https://brew.sh) is most straight forward:
|
|
237
|
+
|
|
238
|
+
```sh
|
|
239
|
+
brew install dotenvx/brew/dotenvx
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Other Ways to Install
|
|
243
|
+
|
|
244
|
+
1. After `brew`, installing globally using [`npm`](https://www.npmjs.com/package/@dotenvx/dotenvx) is easiest:
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
npm install @dotenvx/dotenvx --global
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
2. Or use with [`npx`](https://www.npmjs.com/package/npx):
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
npx @dotenvx/dotenvx help
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
3. dotenvx is a standalone binary, so (if you want) you can just download it directly:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
# download it to `./dotenvx`
|
|
260
|
+
curl -Lo ./dotenvx --compressed -f --proto '=https' https://github.com/dotenvx/dotenvx/releases/latest/download/dotenvx-$(uname)-$(uname -m).tar.gz
|
|
261
|
+
|
|
262
|
+
# install it to `/usr/local/bin/dotenvx`
|
|
263
|
+
sudo install -m 755 dotenvx /usr/local/bin
|
|
264
|
+
|
|
265
|
+
# check it works
|
|
266
|
+
dotenvx --help
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Contributing
|
|
270
|
+
|
|
271
|
+
If you have questions or feedback:
|
|
272
|
+
|
|
273
|
+
* [github.com/dotenvx/dotenvx](https://github.com/dotenvx/dotenvx) - bugs and discussions
|
|
274
|
+
* [@dotenvx 𝕏](https://x.com/dotenvx) (DMs are open)
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.23",
|
|
3
|
+
"name": "@dotenvx/dotenvx",
|
|
4
|
+
"description": "a better dotenv–from the creator of `dotenv`",
|
|
5
|
+
"author": "@motdotla",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"dotenv",
|
|
8
|
+
"env"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/dotenvx/dotenvx",
|
|
11
|
+
"repository": "dotenvx/dotenvx",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"files": [
|
|
14
|
+
"src/**/*"
|
|
15
|
+
],
|
|
16
|
+
"main": "src/lib/main.js",
|
|
17
|
+
"bin": {
|
|
18
|
+
"dotenvx": "./src/cli/dotenvx.js",
|
|
19
|
+
"git-dotenvx": "./src/cli/dotenvx.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"standard": "standard",
|
|
23
|
+
"standard:fix": "standard --fix",
|
|
24
|
+
"test": "jest"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "^11.1.0",
|
|
28
|
+
"dotenv": "^16.3.1",
|
|
29
|
+
"winston": "^3.11.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"jest": "^29.7.0",
|
|
33
|
+
"jest-mock-process": "^2.0.0",
|
|
34
|
+
"pkg": "^5.8.1",
|
|
35
|
+
"standard": "^17.1.0"
|
|
36
|
+
},
|
|
37
|
+
"standard": {
|
|
38
|
+
"env": [
|
|
39
|
+
"jest"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const { Command } = require('commander')
|
|
5
|
+
const program = new Command()
|
|
6
|
+
|
|
7
|
+
// constants
|
|
8
|
+
const ENCODING = 'utf8'
|
|
9
|
+
|
|
10
|
+
const logger = require('./../shared/logger')
|
|
11
|
+
const helpers = require('./helpers')
|
|
12
|
+
const packageJson = require('./../shared/packageJson')
|
|
13
|
+
const main = require('./../lib/main')
|
|
14
|
+
|
|
15
|
+
// global log levels
|
|
16
|
+
program
|
|
17
|
+
.option('-l, --log-level <level>', 'set log level', 'info')
|
|
18
|
+
.option('-q, --quiet', 'sets log level to error')
|
|
19
|
+
.option('-v, --verbose', 'sets log level to verbose')
|
|
20
|
+
.option('-d, --debug', 'sets log level to debug')
|
|
21
|
+
.hook('preAction', (thisCommand, actionCommand) => {
|
|
22
|
+
const options = thisCommand.opts()
|
|
23
|
+
|
|
24
|
+
if (options.logLevel) {
|
|
25
|
+
logger.level = options.logLevel
|
|
26
|
+
logger.debug(`Setting log level to ${options.logLevel}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// --quiet overides --log-level. only errors will be shown
|
|
30
|
+
if (options.quiet) {
|
|
31
|
+
logger.level = 'error'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// --verbose overrides --quiet
|
|
35
|
+
if (options.verbose) {
|
|
36
|
+
logger.level = 'verbose'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --debug overrides --verbose
|
|
40
|
+
if (options.debug) {
|
|
41
|
+
logger.level = 'debug'
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// cli
|
|
46
|
+
program
|
|
47
|
+
.name(packageJson.name)
|
|
48
|
+
.description(packageJson.description)
|
|
49
|
+
.version(packageJson.version)
|
|
50
|
+
|
|
51
|
+
// commands
|
|
52
|
+
program.command('encrypt')
|
|
53
|
+
.description('encrypt something')
|
|
54
|
+
.action((_str, _options) => {
|
|
55
|
+
console.log('encrypted!')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
program.command('decrypt')
|
|
59
|
+
.description('decrypt something')
|
|
60
|
+
.action((_str, _options) => {
|
|
61
|
+
console.log('decrypted!')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
program.command('run')
|
|
65
|
+
.description('Inject env variables into your application process')
|
|
66
|
+
.option('-f, --env-file <paths...>', 'path to your env file', '.env')
|
|
67
|
+
.option('-o, --overload', 'override existing env variables')
|
|
68
|
+
.action(function () {
|
|
69
|
+
// injecting 1 environment variable from ${options.envFile}
|
|
70
|
+
const options = this.opts()
|
|
71
|
+
logger.debug('Configuring options')
|
|
72
|
+
logger.debug(options)
|
|
73
|
+
|
|
74
|
+
// convert to array if needed
|
|
75
|
+
let optionEnvFile = options.envFile
|
|
76
|
+
if (!Array.isArray(optionEnvFile)) {
|
|
77
|
+
optionEnvFile = [optionEnvFile]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const env = {}
|
|
81
|
+
|
|
82
|
+
for (const envFilepath of optionEnvFile) {
|
|
83
|
+
const filepath = helpers.resolvePath(envFilepath)
|
|
84
|
+
|
|
85
|
+
logger.verbose(`Loading env from ${filepath}`)
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
logger.debug(`Reading env from ${filepath}`)
|
|
89
|
+
const src = fs.readFileSync(filepath, { encoding: ENCODING })
|
|
90
|
+
|
|
91
|
+
logger.debug(`Parsing env from ${filepath}`)
|
|
92
|
+
const parsed = main.parse(src)
|
|
93
|
+
|
|
94
|
+
logger.debug(`Populating env from ${filepath}`)
|
|
95
|
+
main.populate(process.env, parsed, { debug: (logger.level === 'debug'), override: options.overload })
|
|
96
|
+
} catch (e) {
|
|
97
|
+
logger.warn(e)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.info('Injecting X environment variables into your application process')
|
|
102
|
+
|
|
103
|
+
// Extract command and arguments after '--'
|
|
104
|
+
const commandIndex = process.argv.indexOf('--')
|
|
105
|
+
if (commandIndex === -1 || commandIndex === process.argv.length - 1) {
|
|
106
|
+
logger.error('At least one argument is required after the run command, received 0.')
|
|
107
|
+
logger.error('Exiting')
|
|
108
|
+
process.exit(1)
|
|
109
|
+
} else {
|
|
110
|
+
const subCommand = process.argv.slice(commandIndex + 1)
|
|
111
|
+
|
|
112
|
+
helpers.executeCommand(subCommand, env)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
program.parse(process.argv)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { spawn } = require('child_process')
|
|
3
|
+
|
|
4
|
+
// resolve path based on current running process location
|
|
5
|
+
const resolvePath = function (filepath) {
|
|
6
|
+
return path.resolve(process.cwd(), filepath)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const executeCommand = function (subCommand, env) {
|
|
10
|
+
const subprocess = spawn(subCommand[0], subCommand.slice(1), {
|
|
11
|
+
stdio: 'inherit',
|
|
12
|
+
shell: true,
|
|
13
|
+
env: { ...process.env, ...env }
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
subprocess.on('close', (code) => {
|
|
17
|
+
process.exit(code)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
subprocess.on('error', (_err) => {
|
|
21
|
+
process.exit(1)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
resolvePath,
|
|
27
|
+
executeCommand
|
|
28
|
+
}
|
package/src/lib/main.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const logger = require('./../shared/logger')
|
|
2
|
+
const dotenv = require('dotenv')
|
|
3
|
+
|
|
4
|
+
const config = function (options) {
|
|
5
|
+
return dotenv.config(options)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const parse = function (src) {
|
|
9
|
+
const result = dotenv.parse(src)
|
|
10
|
+
|
|
11
|
+
logger.debug(result)
|
|
12
|
+
|
|
13
|
+
return result
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const populate = function (processEnv, parsed, options = {}) {
|
|
17
|
+
const result = dotenv.populate(processEnv, parsed, options = {})
|
|
18
|
+
|
|
19
|
+
logger.debug(process.env)
|
|
20
|
+
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
config,
|
|
26
|
+
parse,
|
|
27
|
+
populate
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const winston = require('winston')
|
|
2
|
+
|
|
3
|
+
const printf = winston.format.printf
|
|
4
|
+
const combine = winston.format.combine
|
|
5
|
+
const createLogger = winston.createLogger
|
|
6
|
+
const transports = winston.transports
|
|
7
|
+
|
|
8
|
+
const packageJson = require('./packageJson')
|
|
9
|
+
|
|
10
|
+
const dotenvxFormat = printf(({ level, message, label, timestamp }) => {
|
|
11
|
+
const formattedMessage = typeof message === 'object' ? JSON.stringify(message) : message
|
|
12
|
+
|
|
13
|
+
return `[dotenvx@${packageJson.version}][${level.toUpperCase()}] ${formattedMessage}`
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const logger = createLogger({
|
|
17
|
+
level: 'info',
|
|
18
|
+
format: combine(
|
|
19
|
+
dotenvxFormat
|
|
20
|
+
),
|
|
21
|
+
transports: [
|
|
22
|
+
new transports.Console()
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
module.exports = logger
|