dotenv 2.8.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +158 -93
- data/lib/dotenv/autorestore.rb +29 -0
- data/lib/dotenv/cli.rb +13 -11
- data/lib/dotenv/diff.rb +56 -0
- data/lib/dotenv/environment.rb +12 -15
- data/lib/dotenv/load.rb +2 -1
- data/lib/dotenv/log_subscriber.rb +61 -0
- data/lib/dotenv/missing_keys.rb +1 -1
- data/lib/dotenv/parser.rb +14 -10
- data/lib/dotenv/rails-now.rb +10 -0
- data/lib/dotenv/rails.rb +114 -0
- data/lib/dotenv/replay_logger.rb +20 -0
- data/lib/dotenv/substitutions/command.rb +2 -2
- data/lib/dotenv/substitutions/variable.rb +3 -3
- data/lib/dotenv/template.rb +1 -1
- data/lib/dotenv/version.rb +1 -1
- data/lib/dotenv.rb +100 -47
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dc708aad5fcaf487b064451b8f47d5b0e402a7c250385f234306ab3c77fcf20
|
4
|
+
data.tar.gz: e939eca5e64f2aef9aeb784bd9765e20a3201057d4ed69c1ea46b1405625920e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c7a9b929e7b419a1df9c07bd2dfa7959deea186bc6de9e0e8bdfdc671b7d4c183104715206ac956e81d4689b76d14907dd0a644eb995f4cd48437047e307ad1
|
7
|
+
data.tar.gz: de3d64ad2bf4fbec40e7c253c71de40cf1cab82875293f168c73a5098e7419554362014e6eb0c6339d485c41c12cccad82e0f854fee30f7fef18e7cc4afc26ef
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# dotenv [![Gem Version](https://badge.fury.io/rb/dotenv.svg)](https://badge.fury.io/rb/dotenv)
|
1
|
+
# dotenv [![Gem Version](https://badge.fury.io/rb/dotenv.svg)](https://badge.fury.io/rb/dotenv)
|
2
2
|
|
3
3
|
Shim to load environment variables from `.env` into `ENV` in *development*.
|
4
4
|
|
@@ -8,52 +8,36 @@ But it is not always practical to set environment variables on development machi
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
Add this line to the top of your application's Gemfile:
|
11
|
+
Add this line to the top of your application's Gemfile and run `bundle install`:
|
14
12
|
|
15
13
|
```ruby
|
16
|
-
gem 'dotenv
|
14
|
+
gem 'dotenv', groups: [:development, :test]
|
17
15
|
```
|
18
16
|
|
19
|
-
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Add your application configuration to your `.env` file in the root of your project:
|
20
20
|
|
21
21
|
```shell
|
22
|
-
|
22
|
+
S3_BUCKET=YOURS3BUCKET
|
23
|
+
SECRET_KEY=YOURSECRETKEYGOESHERE
|
23
24
|
```
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
dotenv is initialized in your Rails app during the `before_configuration` callback, which is fired when the `Application` constant is defined in `config/application.rb` with `class Application < Rails::Application`. If you need it to be initialized sooner, you can manually call `Dotenv::Railtie.load`.
|
26
|
+
Whenever your application loads, these variables will be available in `ENV`:
|
28
27
|
|
29
28
|
```ruby
|
30
|
-
|
31
|
-
Bundler.require(*Rails.groups)
|
32
|
-
|
33
|
-
# Load dotenv only in development or test environment
|
34
|
-
if ['development', 'test'].include? ENV['RAILS_ENV']
|
35
|
-
Dotenv::Railtie.load
|
36
|
-
end
|
37
|
-
|
38
|
-
HOSTNAME = ENV['HOSTNAME']
|
29
|
+
config.fog_directory = ENV['S3_BUCKET']
|
39
30
|
```
|
40
31
|
|
41
|
-
|
32
|
+
See the [API Docs](https://rubydoc.info/github/bkeepers/dotenv/main) for more.
|
42
33
|
|
43
|
-
|
44
|
-
gem 'dotenv-rails', require: 'dotenv/rails-now'
|
45
|
-
gem 'gem-that-requires-env-variables'
|
46
|
-
```
|
47
|
-
|
48
|
-
### Sinatra or Plain ol' Ruby
|
34
|
+
### Rails
|
49
35
|
|
50
|
-
|
36
|
+
Dotenv will automatically load when your Rails app boots. See [Customizing Rails](#customizing-rails) to change which files are loaded and when.
|
51
37
|
|
52
|
-
|
53
|
-
$ gem install dotenv
|
54
|
-
```
|
38
|
+
### Sinatra / Ruby
|
55
39
|
|
56
|
-
|
40
|
+
Load Dotenv as early as possible in your application bootstrap process:
|
57
41
|
|
58
42
|
```ruby
|
59
43
|
require 'dotenv/load'
|
@@ -70,17 +54,17 @@ require 'dotenv'
|
|
70
54
|
Dotenv.load('file1.env', 'file2.env')
|
71
55
|
```
|
72
56
|
|
73
|
-
|
57
|
+
### Autorestore in tests
|
74
58
|
|
75
|
-
|
76
|
-
$ dotenv ./script.rb
|
77
|
-
```
|
59
|
+
Since 3.0, dotenv in a Rails app will automatically restore `ENV` after each test. This means you can modify `ENV` in your tests without fear of leaking state to other tests. It works with both `ActiveSupport::TestCase` and `Rspec`.
|
78
60
|
|
79
|
-
|
61
|
+
To disable this behavior, set `config.dotenv.autorestore = false` in `config/application.rb` or `config/environments/test.rb`. It is disabled by default if your app uses [climate_control](https://github.com/thoughtbot/climate_control) or [ice_age](https://github.com/dpep/ice_age_rb).
|
80
62
|
|
81
|
-
|
82
|
-
|
83
|
-
|
63
|
+
To use this behavior outside of a Rails app, just `require "dotenv/autorestore"` in your test suite.
|
64
|
+
|
65
|
+
See [`Dotenv.save`](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:save), [Dotenv.restore](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:restore), and [`Dotenv.modify(hash) { ... }`](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:modify) for manual usage.
|
66
|
+
|
67
|
+
### Rake
|
84
68
|
|
85
69
|
To ensure `.env` is loaded in rake, load the tasks:
|
86
70
|
|
@@ -88,41 +72,121 @@ To ensure `.env` is loaded in rake, load the tasks:
|
|
88
72
|
require 'dotenv/tasks'
|
89
73
|
|
90
74
|
task mytask: :dotenv do
|
91
|
-
|
75
|
+
# things that require .env
|
92
76
|
end
|
93
77
|
```
|
94
78
|
|
95
|
-
|
79
|
+
### CLI
|
96
80
|
|
97
|
-
|
81
|
+
You can use the `dotenv` executable load `.env` before launching your application:
|
98
82
|
|
99
|
-
```
|
100
|
-
|
101
|
-
SECRET_KEY=YOURSECRETKEYGOESHERE
|
83
|
+
```console
|
84
|
+
$ dotenv ./script.rb
|
102
85
|
```
|
103
86
|
|
104
|
-
|
87
|
+
The `dotenv` executable also accepts the flag `-f`. Its value should be a comma-separated list of configuration files, in the order of most important to least. All of the files must exist. There _must_ be a space between the flag and its value.
|
105
88
|
|
106
|
-
```
|
107
|
-
|
89
|
+
```console
|
90
|
+
$ dotenv -f ".env.local,.env" ./script.rb
|
108
91
|
```
|
109
92
|
|
110
|
-
|
93
|
+
The `dotenv` executable can optionally ignore missing files with the `-i` or `--ignore` flag. For example, if the `.env.local` file does not exist, the following will ignore the missing file and only load the `.env` file.
|
111
94
|
|
112
|
-
```
|
113
|
-
|
114
|
-
export SECRET_KEY=YOURSECRETKEYGOESHERE
|
95
|
+
```console
|
96
|
+
$ dotenv -i -f ".env.local,.env" ./script.rb
|
115
97
|
```
|
116
98
|
|
117
|
-
###
|
99
|
+
### Load Order
|
118
100
|
|
119
|
-
If you
|
101
|
+
If you use gems that require environment variables to be set before they are loaded, then list `dotenv` in the `Gemfile` before those other gems and require `dotenv/load`.
|
120
102
|
|
121
|
-
```
|
122
|
-
|
103
|
+
```ruby
|
104
|
+
gem 'dotenv', require: 'dotenv/load'
|
105
|
+
gem 'gem-that-requires-env-variables'
|
106
|
+
```
|
107
|
+
|
108
|
+
### Customizing Rails
|
109
|
+
|
110
|
+
Dotenv will load the following files depending on `RAILS_ENV`, with the first file having the highest precedence, and `.env` having the lowest precedence:
|
111
|
+
|
112
|
+
<table>
|
113
|
+
<thead>
|
114
|
+
<tr>
|
115
|
+
<th>Priority</th>
|
116
|
+
<th colspan="3">Environment</th>
|
117
|
+
<th><code>.gitignore</code>it?</th>
|
118
|
+
<th>Notes</th>
|
119
|
+
</tr>
|
120
|
+
<tr>
|
121
|
+
<th></th>
|
122
|
+
<th>development</th>
|
123
|
+
<th>test</th>
|
124
|
+
<th>production</th>
|
125
|
+
<th></th>
|
126
|
+
<th></th>
|
127
|
+
</tr>
|
128
|
+
</thead>
|
129
|
+
<tr>
|
130
|
+
<td>highest</td>
|
131
|
+
<td><code>.env.development.local</code></td>
|
132
|
+
<td><code>.env.test.local</code></td>
|
133
|
+
<td><code>.env.production.local</code></td>
|
134
|
+
<td>Yes</td>
|
135
|
+
<td>Environment-specific local overrides</td>
|
136
|
+
</tr>
|
137
|
+
<tr>
|
138
|
+
<td>2nd</td>
|
139
|
+
<td><code>.env.local</code></td>
|
140
|
+
<td><strong>N/A</strong></td>
|
141
|
+
<td><code>.env.local</code></td>
|
142
|
+
<td>Yes</td>
|
143
|
+
<td>Local overrides</td>
|
144
|
+
</tr>
|
145
|
+
<tr>
|
146
|
+
<td>3rd</td>
|
147
|
+
<td><code>.env.development</code></td>
|
148
|
+
<td><code>.env.test</code></td>
|
149
|
+
<td><code>.env.production</code></td>
|
150
|
+
<td>No</td>
|
151
|
+
<td>Shared environment-specific variables</td>
|
152
|
+
</tr>
|
153
|
+
<tr>
|
154
|
+
<td>last</td>
|
155
|
+
<td><code>.env</code></td>
|
156
|
+
<td><code>.env</code></td>
|
157
|
+
<td><code>.env</code></td>
|
158
|
+
<td><a href="#should-i-commit-my-env-file">Maybe</a></td>
|
159
|
+
<td>Shared for all environments</td>
|
160
|
+
</tr>
|
161
|
+
</table>
|
162
|
+
|
163
|
+
|
164
|
+
These files are loaded during the `before_configuration` callback, which is fired when the `Application` constant is defined in `config/application.rb` with `class Application < Rails::Application`. If you need it to be initialized sooner, or need to customize the loading process, you can do so at the top of `application.rb`
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# config/application.rb
|
168
|
+
Bundler.require(*Rails.groups)
|
169
|
+
|
170
|
+
# Load .env.local in test
|
171
|
+
Dotenv::Rails.files.unshift(".env.local") if ENV["RAILS_ENV"] == "test"
|
172
|
+
|
173
|
+
module YourApp
|
174
|
+
class Application < Rails::Application
|
175
|
+
# ...
|
176
|
+
end
|
177
|
+
end
|
123
178
|
```
|
124
179
|
|
125
|
-
|
180
|
+
Available options:
|
181
|
+
|
182
|
+
* `Dotenv::Rails.files` - list of files to be loaded, in order of precedence.
|
183
|
+
* `Dotenv::Rails.overwrite` - Overwrite exiting `ENV` variables with contents of `.env*` files
|
184
|
+
* `Dotenv::Rails.logger` - The logger to use for dotenv's logging. Defaults to `Rails.logger`
|
185
|
+
* `Dotenv::Rails.autorestore` - Enable or disable [autorestore](#autorestore-in-tests)
|
186
|
+
|
187
|
+
### Multi-line values
|
188
|
+
|
189
|
+
Multi-line values with line breaks must be surrounded with double quotes.
|
126
190
|
|
127
191
|
```shell
|
128
192
|
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
@@ -132,7 +196,12 @@ HkVN9...
|
|
132
196
|
-----END DSA PRIVATE KEY-----"
|
133
197
|
```
|
134
198
|
|
135
|
-
|
199
|
+
Prior to 3.0, dotenv would replace `\n` in quoted strings with a newline, but that behavior is deprecated. To use the old behavior, set `DOTENV_LINEBREAK_MODE=legacy` before any variables that include `\n`:
|
200
|
+
|
201
|
+
```shell
|
202
|
+
DOTENV_LINEBREAK_MODE=legacy
|
203
|
+
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nHkVN9...\n-----END DSA PRIVATE KEY-----\n"
|
204
|
+
```
|
136
205
|
|
137
206
|
### Command Substitution
|
138
207
|
|
@@ -166,6 +235,15 @@ SECRET_KEY=YOURSECRETKEYGOESHERE # comment
|
|
166
235
|
SECRET_HASH="something-with-a-#-hash"
|
167
236
|
```
|
168
237
|
|
238
|
+
### Exports
|
239
|
+
|
240
|
+
For compatability, you may also add `export` in front of each line so you can `source` the file in bash:
|
241
|
+
|
242
|
+
```shell
|
243
|
+
export S3_BUCKET=YOURS3BUCKET
|
244
|
+
export SECRET_KEY=YOURSECRETKEYGOESHERE
|
245
|
+
```
|
246
|
+
|
169
247
|
### Required Keys
|
170
248
|
|
171
249
|
If a particular configuration value is required but not set, it's appropriate to raise an error.
|
@@ -191,39 +269,11 @@ Dotenv.parse(".env.local", ".env")
|
|
191
269
|
|
192
270
|
This method returns a hash of the ENV var name/value pairs.
|
193
271
|
|
194
|
-
|
195
|
-
|
196
|
-
### Can I use dotenv in production?
|
197
|
-
|
198
|
-
dotenv was originally created to load configuration variables into `ENV` in *development*. There are typically better ways to manage configuration in production environments - such as `/etc/environment` managed by [Puppet](https://github.com/puppetlabs/puppet) or [Chef](https://github.com/chef/chef), `heroku config`, etc.
|
199
|
-
|
200
|
-
However, some find dotenv to be a convenient way to configure Rails applications in staging and production environments, and you can do that by defining environment-specific files like `.env.production` or `.env.test`.
|
201
|
-
|
202
|
-
If you use this gem to handle env vars for multiple Rails environments (development, test, production, etc.), please note that env vars that are general to all environments should be stored in `.env`. Then, environment specific env vars should be stored in `.env.<that environment's name>`.
|
203
|
-
|
204
|
-
### What other .env* files can I use?
|
205
|
-
|
206
|
-
`dotenv-rails` will override in the following order (highest defined variable overrides lower):
|
207
|
-
|
208
|
-
| Hierarchy Priority | Filename | Environment | Should I `.gitignore`it? | Notes |
|
209
|
-
| ------------------ | ------------------------ | -------------------- | --------------------------------------------------- | ------------------------------------------------------------ |
|
210
|
-
| 1st (highest) | `.env.development.local` | Development | Yes! | Local overrides of environment-specific settings. |
|
211
|
-
| 1st | `.env.test.local` | Test | Yes! | Local overrides of environment-specific settings. |
|
212
|
-
| 1st | `.env.production.local` | Production | Yes! | Local overrides of environment-specific settings. |
|
213
|
-
| 2nd | `.env.local` | Wherever the file is | Definitely. | Local overrides. This file is loaded for all environments _except_ `test`. |
|
214
|
-
| 3rd | `.env.development` | Development | No. | Shared environment-specific settings |
|
215
|
-
| 3rd | `.env.test` | Test | No. | Shared environment-specific settings |
|
216
|
-
| 3rd | `.env.production` | Production | No. | Shared environment-specific settings |
|
217
|
-
| Last | `.env` | All Environments | Depends (See [below](#should-i-commit-my-env-file)) | The Original® |
|
218
|
-
|
219
|
-
|
220
|
-
### Should I commit my .env file?
|
221
|
-
|
222
|
-
Credentials should only be accessible on the machines that need access to them. Never commit sensitive information to a repository that is not needed by every development machine and server.
|
223
|
-
|
272
|
+
### Templates
|
224
273
|
|
225
274
|
You can use the `-t` or `--template` flag on the dotenv cli to create a template of your `.env` file.
|
226
|
-
|
275
|
+
|
276
|
+
```console
|
227
277
|
$ dotenv -t .env
|
228
278
|
```
|
229
279
|
A template will be created in your working directory named `{FINAME}.template`. So in the above example, it would create a `.env.template` file.
|
@@ -244,14 +294,29 @@ S3_BUCKET=S3_BUCKET
|
|
244
294
|
SECRET_KEY=SECRET_KEY
|
245
295
|
```
|
246
296
|
|
297
|
+
## Frequently Answered Questions
|
298
|
+
|
299
|
+
### Can I use dotenv in production?
|
300
|
+
|
301
|
+
dotenv was originally created to load configuration variables into `ENV` in *development*. There are typically better ways to manage configuration in production environments - such as `/etc/environment` managed by [Puppet](https://github.com/puppetlabs/puppet) or [Chef](https://github.com/chef/chef), `heroku config`, etc.
|
302
|
+
|
303
|
+
However, some find dotenv to be a convenient way to configure Rails applications in staging and production environments, and you can do that by defining environment-specific files like `.env.production` or `.env.test`.
|
304
|
+
|
305
|
+
If you use this gem to handle env vars for multiple Rails environments (development, test, production, etc.), please note that env vars that are general to all environments should be stored in `.env`. Then, environment specific env vars should be stored in `.env.<that environment's name>`.
|
306
|
+
|
307
|
+
### Should I commit my .env file?
|
308
|
+
|
309
|
+
Credentials should only be accessible on the machines that need access to them. Never commit sensitive information to a repository that is not needed by every development machine and server.
|
310
|
+
|
247
311
|
Personally, I prefer to commit the `.env` file with development-only settings. This makes it easy for other developers to get started on the project without compromising credentials for other environments. If you follow this advice, make sure that all the credentials for your development environment are different from your other deployments and that the development credentials do not have access to any confidential data.
|
248
312
|
|
249
|
-
### Why is it not
|
313
|
+
### Why is it not overwriting existing `ENV` variables?
|
250
314
|
|
251
|
-
By default, it **won't** overwrite existing environment variables as dotenv assumes the deployment environment has more knowledge about configuration than the application does. To overwrite existing environment variables you can use `Dotenv.
|
315
|
+
By default, it **won't** overwrite existing environment variables as dotenv assumes the deployment environment has more knowledge about configuration than the application does. To overwrite existing environment variables you can use `Dotenv.load files, overwrite: true`.
|
252
316
|
|
253
|
-
You can also use the `-o` or `--
|
254
|
-
|
317
|
+
You can also use the `-o` or `--overwrite` flag on the dotenv cli to overwrite existing `ENV` variables.
|
318
|
+
|
319
|
+
```console
|
255
320
|
$ dotenv -o -f ".env.local,.env"
|
256
321
|
```
|
257
322
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Automatically restore `ENV` to its original state after
|
2
|
+
|
3
|
+
if defined?(RSpec.configure)
|
4
|
+
RSpec.configure do |config|
|
5
|
+
# Save ENV before the suite starts
|
6
|
+
config.before(:suite) { Dotenv.save }
|
7
|
+
|
8
|
+
# Restore ENV after each example
|
9
|
+
config.after { Dotenv.restore }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(ActiveSupport)
|
14
|
+
ActiveSupport.on_load(:active_support_test_case) do
|
15
|
+
ActiveSupport::TestCase.class_eval do
|
16
|
+
# Save ENV before each test
|
17
|
+
setup { Dotenv.save }
|
18
|
+
|
19
|
+
# Restore ENV after each test
|
20
|
+
teardown do
|
21
|
+
Dotenv.restore
|
22
|
+
rescue ThreadError => e
|
23
|
+
# Restore will fail if running tests in parallel.
|
24
|
+
warn e.message
|
25
|
+
warn "Set `config.dotenv.autorestore = false` in `config/initializers/test.rb`" if defined?(Dotenv::Rails)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/dotenv/cli.rb
CHANGED
@@ -4,26 +4,32 @@ require "dotenv/template"
|
|
4
4
|
require "optparse"
|
5
5
|
|
6
6
|
module Dotenv
|
7
|
-
# The command line interface
|
7
|
+
# The `dotenv` command line interface. Run `$ dotenv --help` to see usage.
|
8
8
|
class CLI < OptionParser
|
9
|
-
attr_reader :argv, :filenames, :
|
9
|
+
attr_reader :argv, :filenames, :overwrite
|
10
10
|
|
11
11
|
def initialize(argv = [])
|
12
12
|
@argv = argv.dup
|
13
13
|
@filenames = []
|
14
|
-
@
|
14
|
+
@ignore = false
|
15
|
+
@overwrite = false
|
15
16
|
|
16
|
-
super
|
17
|
+
super("Usage: dotenv [options]")
|
17
18
|
separator ""
|
18
19
|
|
19
20
|
on("-f FILES", Array, "List of env files to parse") do |list|
|
20
21
|
@filenames = list
|
21
22
|
end
|
22
23
|
|
23
|
-
on("-
|
24
|
-
@
|
24
|
+
on("-i", "--ignore", "ignore missing env files") do
|
25
|
+
@ignore = true
|
25
26
|
end
|
26
27
|
|
28
|
+
on("-o", "--overwrite", "overwrite existing ENV variables") do
|
29
|
+
@overwrite = true
|
30
|
+
end
|
31
|
+
on("--overload") { @overwrite = true }
|
32
|
+
|
27
33
|
on("-h", "--help", "Display help") do
|
28
34
|
puts self
|
29
35
|
exit
|
@@ -43,11 +49,7 @@ module Dotenv
|
|
43
49
|
end
|
44
50
|
|
45
51
|
def run
|
46
|
-
|
47
|
-
Dotenv.overload!(*@filenames)
|
48
|
-
else
|
49
|
-
Dotenv.load!(*@filenames)
|
50
|
-
end
|
52
|
+
Dotenv.load(*@filenames, overwrite: @overwrite, ignore: @ignore)
|
51
53
|
rescue Errno::ENOENT => e
|
52
54
|
abort e.message
|
53
55
|
else
|
data/lib/dotenv/diff.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Dotenv
|
2
|
+
# A diff between multiple states of ENV.
|
3
|
+
class Diff
|
4
|
+
# The initial state
|
5
|
+
attr_reader :a
|
6
|
+
|
7
|
+
# The final or current state
|
8
|
+
attr_reader :b
|
9
|
+
|
10
|
+
# Create a new diff. If given a block, the state of ENV after the block will be preserved as
|
11
|
+
# the final state for comparison. Otherwise, the current ENV will be the final state.
|
12
|
+
#
|
13
|
+
# @param a [Hash] the initial state, defaults to a snapshot of current ENV
|
14
|
+
# @param b [Hash] the final state, defaults to the current ENV
|
15
|
+
# @yield [diff] a block to execute before recording the final state
|
16
|
+
def initialize(a: snapshot, b: ENV, &block)
|
17
|
+
@a, @b = a, b
|
18
|
+
block&.call self
|
19
|
+
ensure
|
20
|
+
@b = snapshot if block
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return a Hash of keys added with their new values
|
24
|
+
def added
|
25
|
+
b.slice(*(b.keys - a.keys))
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a Hash of keys removed with their previous values
|
29
|
+
def removed
|
30
|
+
a.slice(*(a.keys - b.keys))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns of Hash of keys changed with an array of their previous and new values
|
34
|
+
def changed
|
35
|
+
(b.slice(*a.keys).to_a - a.to_a).map do |(k, v)|
|
36
|
+
[k, [a[k], v]]
|
37
|
+
end.to_h
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a Hash of all added, changed, and removed keys and their new values
|
41
|
+
def env
|
42
|
+
b.slice(*(added.keys + changed.keys)).merge(removed.transform_values { |v| nil })
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if any keys were added, removed, or changed
|
46
|
+
def any?
|
47
|
+
[added, removed, changed].any?(&:any?)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def snapshot
|
53
|
+
ENV.to_h.freeze
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/dotenv/environment.rb
CHANGED
@@ -1,28 +1,25 @@
|
|
1
1
|
module Dotenv
|
2
|
-
#
|
3
|
-
# Dotenv will load key value pairs from a file.
|
2
|
+
# A `.env` file that will be read and parsed into a Hash
|
4
3
|
class Environment < Hash
|
5
|
-
attr_reader :filename
|
4
|
+
attr_reader :filename, :overwrite
|
6
5
|
|
7
|
-
|
6
|
+
# Create a new Environment
|
7
|
+
#
|
8
|
+
# @param filename [String] the path to the file to read
|
9
|
+
# @param overwrite [Boolean] whether the parser should assume existing values will be overwritten
|
10
|
+
def initialize(filename, overwrite: false)
|
11
|
+
super()
|
8
12
|
@filename = filename
|
9
|
-
|
13
|
+
@overwrite = overwrite
|
14
|
+
load
|
10
15
|
end
|
11
16
|
|
12
|
-
def load
|
13
|
-
update Parser.call(read,
|
17
|
+
def load
|
18
|
+
update Parser.call(read, overwrite: overwrite)
|
14
19
|
end
|
15
20
|
|
16
21
|
def read
|
17
22
|
File.open(@filename, "rb:bom|utf-8", &:read)
|
18
23
|
end
|
19
|
-
|
20
|
-
def apply
|
21
|
-
each { |k, v| ENV[k] ||= v }
|
22
|
-
end
|
23
|
-
|
24
|
-
def apply!
|
25
|
-
each { |k, v| ENV[k] = v }
|
26
|
-
end
|
27
24
|
end
|
28
25
|
end
|
data/lib/dotenv/load.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
require "active_support/log_subscriber"
|
2
|
+
|
3
|
+
module Dotenv
|
4
|
+
# Logs instrumented events
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# require "active_support/notifications"
|
8
|
+
# require "dotenv/log_subscriber"
|
9
|
+
# Dotenv.instrumenter = ActiveSupport::Notifications
|
10
|
+
#
|
11
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
12
|
+
attach_to :dotenv
|
13
|
+
|
14
|
+
def logger
|
15
|
+
Dotenv::Rails.logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def load(event)
|
19
|
+
env = event.payload[:env]
|
20
|
+
|
21
|
+
info "Loaded #{color_filename(env.filename)}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(event)
|
25
|
+
diff = event.payload[:diff]
|
26
|
+
changed = diff.env.keys.map { |key| color_var(key) }
|
27
|
+
debug "Set #{changed.to_sentence}" if diff.any?
|
28
|
+
end
|
29
|
+
|
30
|
+
def save(event)
|
31
|
+
info "Saved a snapshot of #{color_env_constant}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def restore(event)
|
35
|
+
diff = event.payload[:diff]
|
36
|
+
|
37
|
+
removed = diff.removed.keys.map { |key| color(key, :RED) }
|
38
|
+
restored = (diff.changed.keys + diff.added.keys).map { |key| color_var(key) }
|
39
|
+
|
40
|
+
if removed.any? || restored.any?
|
41
|
+
info "Restored snapshot of #{color_env_constant}"
|
42
|
+
debug "Unset #{removed.to_sentence}" if removed.any?
|
43
|
+
debug "Restored #{restored.to_sentence}" if restored.any?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def color_filename(filename)
|
50
|
+
color(Pathname.new(filename).relative_path_from(Dotenv::Rails.root.to_s).to_s, :YELLOW)
|
51
|
+
end
|
52
|
+
|
53
|
+
def color_var(name)
|
54
|
+
color(name, :CYAN)
|
55
|
+
end
|
56
|
+
|
57
|
+
def color_env_constant
|
58
|
+
color("ENV", :GREEN)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/dotenv/missing_keys.rb
CHANGED
data/lib/dotenv/parser.rb
CHANGED
@@ -2,11 +2,11 @@ require "dotenv/substitutions/variable"
|
|
2
2
|
require "dotenv/substitutions/command" if RUBY_VERSION > "1.8.7"
|
3
3
|
|
4
4
|
module Dotenv
|
5
|
+
# Error raised when encountering a syntax error while parsing a .env file.
|
5
6
|
class FormatError < SyntaxError; end
|
6
7
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# exporting of variables.
|
8
|
+
# Parses the `.env` file format into key/value pairs.
|
9
|
+
# It allows for variable substitutions, command substitutions, and exporting of variables.
|
10
10
|
class Parser
|
11
11
|
@substitutions =
|
12
12
|
[Dotenv::Substitutions::Variable, Dotenv::Substitutions::Command]
|
@@ -32,15 +32,15 @@ module Dotenv
|
|
32
32
|
class << self
|
33
33
|
attr_reader :substitutions
|
34
34
|
|
35
|
-
def call(
|
36
|
-
new(
|
35
|
+
def call(...)
|
36
|
+
new(...).call
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def initialize(string,
|
40
|
+
def initialize(string, overwrite: false)
|
41
41
|
@string = string
|
42
42
|
@hash = {}
|
43
|
-
@
|
43
|
+
@overwrite = overwrite
|
44
44
|
end
|
45
45
|
|
46
46
|
def call
|
@@ -80,11 +80,15 @@ module Dotenv
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def expand_newlines(value)
|
83
|
-
|
83
|
+
if (@hash["DOTENV_LINEBREAK_MODE"] || ENV["DOTENV_LINEBREAK_MODE"]) == "legacy"
|
84
|
+
value.gsub('\n', "\n").gsub('\r', "\r")
|
85
|
+
else
|
86
|
+
value.gsub('\n', "\\\\\\n").gsub('\r', "\\\\\\r")
|
87
|
+
end
|
84
88
|
end
|
85
89
|
|
86
90
|
def variable_not_set?(line)
|
87
|
-
!line.split[1
|
91
|
+
!line.split[1..].all? { |var| @hash.member?(var) }
|
88
92
|
end
|
89
93
|
|
90
94
|
def unescape_value(value, maybe_quote)
|
@@ -100,7 +104,7 @@ module Dotenv
|
|
100
104
|
def perform_substitutions(value, maybe_quote)
|
101
105
|
if maybe_quote != "'"
|
102
106
|
self.class.substitutions.each do |proc|
|
103
|
-
value = proc.call(value, @hash, @
|
107
|
+
value = proc.call(value, @hash, overwrite: @overwrite)
|
104
108
|
end
|
105
109
|
end
|
106
110
|
value
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# If you use gems that require environment variables to be set before they are
|
2
|
+
# loaded, then list `dotenv` in the `Gemfile` before those other gems and
|
3
|
+
# require `dotenv/load`.
|
4
|
+
#
|
5
|
+
# gem "dotenv", require: "dotenv/load"
|
6
|
+
# gem "gem-that-requires-env-variables"
|
7
|
+
#
|
8
|
+
|
9
|
+
require "dotenv/load"
|
10
|
+
warn '[DEPRECATION] `require "dotenv/rails-now"` is deprecated. Use `require "dotenv/load"` instead.', caller(1..1).first
|
data/lib/dotenv/rails.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Since rubygems doesn't support optional dependencies, we have to manually check
|
2
|
+
unless Gem::Requirement.new(">= 6.1").satisfied_by?(Gem::Version.new(Rails.version))
|
3
|
+
warn "dotenv 3.0 only supports Rails 6.1 or later. Use dotenv ~> 2.0."
|
4
|
+
return
|
5
|
+
end
|
6
|
+
|
7
|
+
require "dotenv/replay_logger"
|
8
|
+
require "dotenv/log_subscriber"
|
9
|
+
|
10
|
+
Dotenv.instrumenter = ActiveSupport::Notifications
|
11
|
+
|
12
|
+
# Watch all loaded env files with Spring
|
13
|
+
begin
|
14
|
+
require "spring/commands"
|
15
|
+
ActiveSupport::Notifications.subscribe("load.dotenv") do |*args|
|
16
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
17
|
+
Spring.watch event.payload[:env].filename if Rails.application
|
18
|
+
end
|
19
|
+
rescue LoadError, ArgumentError
|
20
|
+
# Spring is not available
|
21
|
+
end
|
22
|
+
|
23
|
+
module Dotenv
|
24
|
+
# Rails integration for using Dotenv to load ENV variables from a file
|
25
|
+
class Rails < ::Rails::Railtie
|
26
|
+
delegate :files, :files=, :overwrite, :overwrite=, :autorestore, :autorestore=, :logger, to: "config.dotenv"
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
super()
|
30
|
+
config.dotenv = ActiveSupport::OrderedOptions.new.update(
|
31
|
+
# Rails.logger is not available yet, so we'll save log messages and replay them when it is
|
32
|
+
logger: Dotenv::ReplayLogger.new,
|
33
|
+
overwrite: false,
|
34
|
+
files: [
|
35
|
+
".env.#{env}.local",
|
36
|
+
(".env.local" unless env.test?),
|
37
|
+
".env.#{env}",
|
38
|
+
".env"
|
39
|
+
].compact,
|
40
|
+
autorestore: env.test? && !defined?(ClimateControl) && !defined?(IceAge)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Load dotenv
|
45
|
+
#
|
46
|
+
# This will get called during the `before_configuration` callback, but you
|
47
|
+
# can manually call `Dotenv::Rails.load` if you needed it sooner.
|
48
|
+
def load
|
49
|
+
Dotenv.load(*files.map { |file| root.join(file).to_s }, overwrite: overwrite)
|
50
|
+
end
|
51
|
+
|
52
|
+
def overload
|
53
|
+
deprecator.warn("Dotenv::Rails.overload is deprecated. Set `Dotenv::Rails.overwrite = true` and call Dotenv::Rails.load instead.")
|
54
|
+
Dotenv.load(*files.map { |file| root.join(file).to_s }, overwrite: true)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Internal: `Rails.root` is nil in Rails 4.1 before the application is
|
58
|
+
# initialized, so this falls back to the `RAILS_ROOT` environment variable,
|
59
|
+
# or the current working directory.
|
60
|
+
def root
|
61
|
+
::Rails.root || Pathname.new(ENV["RAILS_ROOT"] || Dir.pwd)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set a new logger and replay logs
|
65
|
+
def logger=(new_logger)
|
66
|
+
logger.replay new_logger if logger.is_a?(ReplayLogger)
|
67
|
+
config.dotenv.logger = new_logger
|
68
|
+
end
|
69
|
+
|
70
|
+
# The current environment that the app is running in.
|
71
|
+
#
|
72
|
+
# When running `rake`, the Rails application is initialized in development, so we have to
|
73
|
+
# check which rake tasks are being run to determine the environment.
|
74
|
+
#
|
75
|
+
# See https://github.com/bkeepers/dotenv/issues/219
|
76
|
+
def env
|
77
|
+
@env ||= if defined?(Rake.application) && Rake.application.top_level_tasks.grep(TEST_RAKE_TASKS).any?
|
78
|
+
env = Rake.application.options.show_tasks ? "development" : "test"
|
79
|
+
ActiveSupport::EnvironmentInquirer.new(env)
|
80
|
+
else
|
81
|
+
::Rails.env
|
82
|
+
end
|
83
|
+
end
|
84
|
+
TEST_RAKE_TASKS = /^(default$|test(:|$)|parallel:spec|spec(:|$))/
|
85
|
+
|
86
|
+
def deprecator # :nodoc:
|
87
|
+
@deprecator ||= ActiveSupport::Deprecation.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# Rails uses `#method_missing` to delegate all class methods to the
|
91
|
+
# instance, which means `Kernel#load` gets called here. We don't want that.
|
92
|
+
def self.load
|
93
|
+
instance.load
|
94
|
+
end
|
95
|
+
|
96
|
+
initializer "dotenv", after: :initialize_logger do |app|
|
97
|
+
if logger.is_a?(ReplayLogger)
|
98
|
+
self.logger = ActiveSupport::TaggedLogging.new(::Rails.logger).tagged("dotenv")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
initializer "dotenv.deprecator" do |app|
|
103
|
+
app.deprecators[:dotenv] = deprecator if app.respond_to?(:deprecators)
|
104
|
+
end
|
105
|
+
|
106
|
+
initializer "dotenv.autorestore" do |app|
|
107
|
+
require "dotenv/autorestore" if autorestore
|
108
|
+
end
|
109
|
+
|
110
|
+
config.before_configuration { load }
|
111
|
+
end
|
112
|
+
|
113
|
+
Railtie = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Dotenv::Railtie", "Dotenv::Rails", Dotenv::Rails.deprecator)
|
114
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Dotenv
|
2
|
+
# A logger that can be used before the apps real logger is initialized.
|
3
|
+
class ReplayLogger < Logger
|
4
|
+
def initialize
|
5
|
+
super(nil) # Doesn't matter what this is, it won't be used.
|
6
|
+
@logs = []
|
7
|
+
end
|
8
|
+
|
9
|
+
# Override the add method to store logs so we can replay them to a real logger later.
|
10
|
+
def add(*args, &block)
|
11
|
+
@logs.push([args, block])
|
12
|
+
end
|
13
|
+
|
14
|
+
# Replay the store logs to a real logger.
|
15
|
+
def replay(logger)
|
16
|
+
@logs.each { |args, block| logger.add(*args, &block) }
|
17
|
+
@logs.clear
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -20,7 +20,7 @@ module Dotenv
|
|
20
20
|
)
|
21
21
|
/x
|
22
22
|
|
23
|
-
def call(value, _env,
|
23
|
+
def call(value, _env, overwrite: false)
|
24
24
|
# Process interpolated shell commands
|
25
25
|
value.gsub(INTERPOLATED_SHELL_COMMAND) do |*|
|
26
26
|
# Eliminate opening and closing parentheses
|
@@ -28,7 +28,7 @@ module Dotenv
|
|
28
28
|
|
29
29
|
if $LAST_MATCH_INFO[:backslash]
|
30
30
|
# Command is escaped, don't replace it.
|
31
|
-
$LAST_MATCH_INFO[0][1
|
31
|
+
$LAST_MATCH_INFO[0][1..]
|
32
32
|
else
|
33
33
|
# Execute the command and return the value
|
34
34
|
`#{command}`.chomp
|
@@ -18,8 +18,8 @@ module Dotenv
|
|
18
18
|
\}? # closing brace
|
19
19
|
/xi
|
20
20
|
|
21
|
-
def call(value, env,
|
22
|
-
combined_env =
|
21
|
+
def call(value, env, overwrite: false)
|
22
|
+
combined_env = overwrite ? ENV.to_h.merge(env) : env.merge(ENV)
|
23
23
|
value.gsub(VARIABLE) do |variable|
|
24
24
|
match = $LAST_MATCH_INFO
|
25
25
|
substitute(match, variable, combined_env)
|
@@ -30,7 +30,7 @@ module Dotenv
|
|
30
30
|
|
31
31
|
def substitute(match, variable, env)
|
32
32
|
if match[1] == "\\"
|
33
|
-
variable[1
|
33
|
+
variable[1..]
|
34
34
|
elsif match[3]
|
35
35
|
env.fetch(match[3], "")
|
36
36
|
else
|
data/lib/dotenv/template.rb
CHANGED
@@ -20,7 +20,7 @@ module Dotenv
|
|
20
20
|
var, value = line.split("=")
|
21
21
|
template = var.gsub(EXPORT_COMMAND, "")
|
22
22
|
is_a_comment = var.strip[0].eql?("#")
|
23
|
-
value.nil? || is_a_comment ? line : "#{var}=#{template}"
|
23
|
+
(value.nil? || is_a_comment) ? line : "#{var}=#{template}"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/lib/dotenv/version.rb
CHANGED
data/lib/dotenv.rb
CHANGED
@@ -1,75 +1,121 @@
|
|
1
1
|
require "dotenv/parser"
|
2
2
|
require "dotenv/environment"
|
3
3
|
require "dotenv/missing_keys"
|
4
|
+
require "dotenv/diff"
|
4
5
|
|
5
|
-
#
|
6
|
+
# Shim to load environment variables from `.env files into `ENV`.
|
6
7
|
module Dotenv
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# An internal monitor to synchronize access to ENV in multi-threaded environments.
|
11
|
+
SEMAPHORE = Monitor.new
|
12
|
+
private_constant :SEMAPHORE
|
10
13
|
|
11
|
-
|
14
|
+
attr_accessor :instrumenter
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
# Loads environment variables from one or more `.env` files. See `#parse` for more details.
|
17
|
+
def load(*filenames, overwrite: false, ignore: true)
|
18
|
+
parse(*filenames, overwrite: overwrite, ignore: ignore) do |env|
|
19
|
+
instrument(:load, env: env) do |payload|
|
20
|
+
update(env, overwrite: overwrite)
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
#
|
25
|
+
# Same as `#load`, but raises Errno::ENOENT if any files don't exist
|
23
26
|
def load!(*filenames)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
load(*filenames, ignore: false)
|
28
|
+
end
|
29
|
+
|
30
|
+
# same as `#load`, but will overwrite existing values in `ENV`
|
31
|
+
def overwrite(*filenames)
|
32
|
+
load(*filenames, overwrite: true)
|
33
|
+
end
|
34
|
+
alias_method :overload, :overwrite
|
35
|
+
|
36
|
+
# same as `#overwrite`, but raises Errno::ENOENT if any files don't exist
|
37
|
+
def overwrite!(*filenames)
|
38
|
+
load(*filenames, overwrite: true, ignore: false)
|
28
39
|
end
|
40
|
+
alias_method :overload!, :overwrite!
|
29
41
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
42
|
+
# Parses the given files, yielding for each file if a block is given.
|
43
|
+
#
|
44
|
+
# @param filenames [String, Array<String>] Files to parse
|
45
|
+
# @param overwrite [Boolean] Overwrite existing `ENV` values
|
46
|
+
# @param ignore [Boolean] Ignore non-existent files
|
47
|
+
# @param block [Proc] Block to yield for each parsed `Dotenv::Environment`
|
48
|
+
# @return [Hash] parsed key/value pairs
|
49
|
+
def parse(*filenames, overwrite: false, ignore: true, &block)
|
50
|
+
filenames << ".env" if filenames.empty?
|
51
|
+
filenames = filenames.reverse if overwrite
|
52
|
+
|
53
|
+
filenames.reduce({}) do |hash, filename|
|
54
|
+
begin
|
55
|
+
env = Environment.new(File.expand_path(filename), overwrite: overwrite)
|
56
|
+
env = block.call(env) if block
|
57
|
+
rescue Errno::ENOENT
|
58
|
+
raise unless ignore
|
36
59
|
end
|
60
|
+
|
61
|
+
hash.merge! env || {}
|
37
62
|
end
|
38
63
|
end
|
39
64
|
|
40
|
-
#
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
instrument("dotenv.overload", env: env) { env.apply! }
|
65
|
+
# Save the current `ENV` to be restored later
|
66
|
+
def save
|
67
|
+
instrument(:save) do |payload|
|
68
|
+
@diff = payload[:diff] = Dotenv::Diff.new
|
45
69
|
end
|
46
70
|
end
|
47
71
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
72
|
+
# Restore `ENV` to a given state
|
73
|
+
#
|
74
|
+
# @param env [Hash] Hash of keys and values to restore, defaults to the last saved state
|
75
|
+
# @param safe [Boolean] Is it safe to modify `ENV`? Defaults to `true` in the main thread, otherwise raises an error.
|
76
|
+
def restore(env = @diff&.a, safe: Thread.current == Thread.main)
|
77
|
+
diff = Dotenv::Diff.new(b: env)
|
78
|
+
return unless diff.any?
|
79
|
+
|
80
|
+
unless safe
|
81
|
+
raise ThreadError, <<~EOE.tr("\n", " ")
|
82
|
+
Dotenv.restore is not thread safe. Use `Dotenv.modify { }` to update ENV for the duration
|
83
|
+
of the block in a thread safe manner, or call `Dotenv.restore(safe: true)` to ignore
|
84
|
+
this error.
|
85
|
+
EOE
|
54
86
|
end
|
87
|
+
instrument(:restore, diff: diff) { ENV.replace(env) }
|
55
88
|
end
|
56
89
|
|
57
|
-
#
|
90
|
+
# Update `ENV` with the given hash of keys and values
|
58
91
|
#
|
59
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
92
|
+
# @param env [Hash] Hash of keys and values to set in `ENV`
|
93
|
+
# @param overwrite [Boolean] Overwrite existing `ENV` values
|
94
|
+
def update(env = {}, overwrite: false)
|
95
|
+
instrument(:update) do |payload|
|
96
|
+
diff = payload[:diff] = Dotenv::Diff.new do
|
97
|
+
ENV.update(env.transform_keys(&:to_s)) do |key, old_value, new_value|
|
98
|
+
# This block is called when a key exists. Return the new value if overwrite is true.
|
99
|
+
overwrite ? new_value : old_value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
diff.env
|
65
103
|
end
|
66
104
|
end
|
67
105
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
106
|
+
# Modify `ENV` for the block and restore it to its previous state afterwards.
|
107
|
+
#
|
108
|
+
# Note that the block is synchronized to prevent concurrent modifications to `ENV`,
|
109
|
+
# so multiple threads will be executed serially.
|
110
|
+
#
|
111
|
+
# @param env [Hash] Hash of keys and values to set in `ENV`
|
112
|
+
def modify(env = {}, &block)
|
113
|
+
SEMAPHORE.synchronize do
|
114
|
+
diff = Dotenv::Diff.new
|
115
|
+
update(env, overwrite: true)
|
116
|
+
block.call
|
117
|
+
ensure
|
118
|
+
restore(diff.a, safe: true)
|
73
119
|
end
|
74
120
|
end
|
75
121
|
|
@@ -79,8 +125,15 @@ module Dotenv
|
|
79
125
|
raise MissingKeys, missing_keys
|
80
126
|
end
|
81
127
|
|
82
|
-
|
83
|
-
|
84
|
-
|
128
|
+
private
|
129
|
+
|
130
|
+
def instrument(name, payload = {}, &block)
|
131
|
+
if instrumenter
|
132
|
+
instrumenter.instrument("#{name}.dotenv", payload, &block)
|
133
|
+
else
|
134
|
+
block&.call payload
|
135
|
+
end
|
85
136
|
end
|
86
137
|
end
|
138
|
+
|
139
|
+
require "dotenv/rails" if defined?(Rails::Railtie)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dotenv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Keepers
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -64,11 +64,17 @@ files:
|
|
64
64
|
- README.md
|
65
65
|
- bin/dotenv
|
66
66
|
- lib/dotenv.rb
|
67
|
+
- lib/dotenv/autorestore.rb
|
67
68
|
- lib/dotenv/cli.rb
|
69
|
+
- lib/dotenv/diff.rb
|
68
70
|
- lib/dotenv/environment.rb
|
69
71
|
- lib/dotenv/load.rb
|
72
|
+
- lib/dotenv/log_subscriber.rb
|
70
73
|
- lib/dotenv/missing_keys.rb
|
71
74
|
- lib/dotenv/parser.rb
|
75
|
+
- lib/dotenv/rails-now.rb
|
76
|
+
- lib/dotenv/rails.rb
|
77
|
+
- lib/dotenv/replay_logger.rb
|
72
78
|
- lib/dotenv/substitutions/command.rb
|
73
79
|
- lib/dotenv/substitutions/variable.rb
|
74
80
|
- lib/dotenv/tasks.rb
|
@@ -78,7 +84,7 @@ homepage: https://github.com/bkeepers/dotenv
|
|
78
84
|
licenses:
|
79
85
|
- MIT
|
80
86
|
metadata: {}
|
81
|
-
post_install_message:
|
87
|
+
post_install_message:
|
82
88
|
rdoc_options: []
|
83
89
|
require_paths:
|
84
90
|
- lib
|
@@ -86,15 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
92
|
requirements:
|
87
93
|
- - ">="
|
88
94
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
95
|
+
version: '3.0'
|
90
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
97
|
requirements:
|
92
98
|
- - ">="
|
93
99
|
- !ruby/object:Gem::Version
|
94
100
|
version: '0'
|
95
101
|
requirements: []
|
96
|
-
rubygems_version: 3.
|
97
|
-
signing_key:
|
102
|
+
rubygems_version: 3.5.3
|
103
|
+
signing_key:
|
98
104
|
specification_version: 4
|
99
105
|
summary: Loads environment variables from `.env`.
|
100
106
|
test_files: []
|