Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include Rails integration in dotenv gem and refactor #468

Merged
merged 8 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 83 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,34 @@ But it is not always practical to set environment variables on development machi

## Installation

### Rails

Add this line to the top of your application's Gemfile:
Add this line to the top of your application's Gemfile and run `bundle install`:

```ruby
gem 'dotenv-rails', groups: [:development, :test]
gem 'dotenv', groups: [:development, :test]
```

And then execute:

```console
$ bundle
```

#### Note on load order

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`.

```ruby
# config/application.rb
Bundler.require(*Rails.groups)
## Usage

# Load dotenv only in development or test environment
if ['development', 'test'].include? ENV['RAILS_ENV']
Dotenv::Railtie.load
end
Add your application configuration to your `.env` file in the root of your project:

HOSTNAME = ENV['HOSTNAME']
```shell
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
```

If you use gems that require environment variables to be set before they are loaded, then list `dotenv-rails` in the `Gemfile` before those other gems and require `dotenv/rails-now`.
Whenever your application loads, these variables will be available in `ENV`:

```ruby
gem 'dotenv-rails', require: 'dotenv/rails-now'
gem 'gem-that-requires-env-variables'
config.fog_directory = ENV['S3_BUCKET']
```

### Sinatra or Plain ol' Ruby
### Rails

Install the gem:
Dotenv will automatically load when your Rails app boots. See [Customizing Rails](#customizing-rails) to change which files are loaded and when.

```console
$ gem install dotenv
```
### Sinatra / Ruby

As early as possible in your application bootstrap process, load `.env`:
Load Dotenv as early as possible in your application bootstrap process:

```ruby
require 'dotenv/load'
Expand All @@ -70,7 +52,21 @@ require 'dotenv'
Dotenv.load('file1.env', 'file2.env')
```

Alternatively, you can use the `dotenv` executable to launch your application:
### Rake

To ensure `.env` is loaded in rake, load the tasks:

```ruby
require 'dotenv/tasks'

task mytask: :dotenv do
# things that require .env
end
```

### CLI

You can use the `dotenv` executable load `.env` before launching your application:

```console
$ dotenv ./script.rb
Expand All @@ -88,47 +84,47 @@ The `dotenv` executable can optionally ignore missing files with the `-i` or `--
$ dotenv -i -f ".env.local,.env" ./script.rb
```

To ensure `.env` is loaded in rake, load the tasks:
### Load Order

```ruby
require 'dotenv/tasks'
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`.

task mytask: :dotenv do
# things that require .env
end
```ruby
gem 'dotenv', require: 'dotenv/load'
gem 'gem-that-requires-env-variables'
```

## Usage
### Customizing Rails

Add your application configuration to your `.env` file in the root of your project:
Dotenv will load the following files depending on `RAILS_ENV`, with the last file listed having the highest precedence:

```shell
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
```
* **development**: `.env`, `.env.development`, `.env.local`, `.env.development.local`
* **test**: `.env`, `.env.test`, `.env.test.local` - Note that it will **not** load `.env.local`.
* **development**: `.env`, `.env.production`, `.env.local`, `.env.production.local`

Whenever your application loads, these variables will be available in `ENV`:
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`

```ruby
config.fog_directory = ENV['S3_BUCKET']
```
# config/application.rb
Bundler.require(*Rails.groups)

You may also add `export` in front of each line so you can `source` the file in bash:
# Load .env.local in test
Dotenv::Rails.files.unshift(".env.local") if ENV["RAILS_ENV"] == "test"

```shell
export S3_BUCKET=YOURS3BUCKET
export SECRET_KEY=YOURSECRETKEYGOESHERE
module YourApp
class Application < Rails::Application
# ...
end
end
```

### Multi-line values
Available options:

If you need multiline variables, for example private keys, you can double quote strings and use the `\n` character for newlines:
* `Dotenv::Rails.files` - list of files to be loaded, in order of precedence.
* `Dotenv::Rails.overwrite` - Overwrite exiting `ENV` variables with contents of `.env*` files

```shell
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nHkVN9...\n-----END DSA PRIVATE KEY-----\n"
```
### Multi-line values

Alternatively, multi-line values with line breaks are now supported for quoted values.
Multi-line values with line breaks must be surrounded with double quotes.

```shell
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
Expand All @@ -138,7 +134,12 @@ HkVN9...
-----END DSA PRIVATE KEY-----"
```

This is particularly helpful when using the Heroku command line plugin [`heroku-config`](https://github.com/xavdid/heroku-config) to pull configuration variables down that may have line breaks.
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`:

```shell
DOTENV_LINEBREAK_MODE=legacy
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nHkVN9...\n-----END DSA PRIVATE KEY-----\n"
```

### Command Substitution

Expand Down Expand Up @@ -172,6 +173,15 @@ SECRET_KEY=YOURSECRETKEYGOESHERE # comment
SECRET_HASH="something-with-a-#-hash"
```

### Exports

For compatability, you may also add `export` in front of each line so you can `source` the file in bash:

```shell
export S3_BUCKET=YOURS3BUCKET
export SECRET_KEY=YOURSECRETKEYGOESHERE
```

### Required Keys

If a particular configuration value is required but not set, it's appropriate to raise an error.
Expand All @@ -197,36 +207,7 @@ Dotenv.parse(".env.local", ".env")

This method returns a hash of the ENV var name/value pairs.

## Frequently Answered Questions

### Can I use dotenv in production?

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.

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`.

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>`.

### What other .env* files can I use?

`dotenv-rails` will override in the following order (highest defined variable overrides lower):

| Hierarchy Priority | Filename | Environment | Should I `.gitignore`it? | Notes |
| ------------------ | ------------------------ | -------------------- | --------------------------------------------------- | ------------------------------------------------------------ |
| 1st (highest) | `.env.development.local` | Development | Yes! | Local overrides of environment-specific settings. |
| 1st | `.env.test.local` | Test | Yes! | Local overrides of environment-specific settings. |
| 1st | `.env.production.local` | Production | Yes! | Local overrides of environment-specific settings. |
| 2nd | `.env.local` | Wherever the file is | Definitely. | Local overrides. This file is loaded for all environments _except_ `test`. |
| 3rd | `.env.development` | Development | No. | Shared environment-specific settings |
| 3rd | `.env.test` | Test | No. | Shared environment-specific settings |
| 3rd | `.env.production` | Production | No. | Shared environment-specific settings |
| Last | `.env` | All Environments | Depends (See [below](#should-i-commit-my-env-file)) | The Original® |


### Should I commit my .env file?

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.

### Templates

You can use the `-t` or `--template` flag on the dotenv cli to create a template of your `.env` file.

Expand All @@ -251,6 +232,20 @@ S3_BUCKET=S3_BUCKET
SECRET_KEY=SECRET_KEY
```

## Frequently Answered Questions

### Can I use dotenv in production?

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.

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`.

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>`.

### Should I commit my .env file?

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.

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.

### Why is it not overriding existing `ENV` variables?
Expand Down
4 changes: 1 addition & 3 deletions dotenv-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ Gem::Specification.new "dotenv-rails", Dotenv::VERSION do |gem|
gem.description = gem.summary = "Autoload dotenv in Rails."
gem.homepage = "https://github.com/bkeepers/dotenv"
gem.license = "MIT"
gem.files = `git ls-files lib | grep rails`.split(
$OUTPUT_RECORD_SEPARATOR
) + ["README.md", "LICENSE"]
gem.files = `git ls-files lib | grep dotenv-rails.rb`.split("\n") + ["README.md", "LICENSE"]

gem.add_dependency "dotenv", Dotenv::VERSION
gem.add_dependency "railties", ">= 3.2"
Expand Down
3 changes: 1 addition & 2 deletions dotenv.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ Gem::Specification.new "dotenv", Dotenv::VERSION do |gem|
gem.homepage = "https://github.com/bkeepers/dotenv"
gem.license = "MIT"

gem_files = `git ls-files README.md LICENSE lib bin | grep -v rails`
gem.files = gem_files.split($OUTPUT_RECORD_SEPARATOR)
gem.files = `git ls-files README.md LICENSE lib bin | grep -v dotenv-rails.rb`.split("\n")
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }

gem.add_development_dependency "rake"
Expand Down
2 changes: 2 additions & 0 deletions lib/dotenv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ def require_keys(*keys)
raise MissingKeys, missing_keys
end
end

require "dotenv/rails" if defined?(Rails::Railtie)
3 changes: 2 additions & 1 deletion lib/dotenv/load.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
require "dotenv"
Dotenv.load

defined?(Dotenv::Rails) ? Dotenv::Rails.load : Dotenv.load
10 changes: 5 additions & 5 deletions lib/dotenv/rails-now.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# If you use gems that require environment variables to be set before they are
# loaded, then list `dotenv-rails` in the `Gemfile` before those other gems and
# require `dotenv/rails-now`.
# loaded, then list `dotenv` in the `Gemfile` before those other gems and
# require `dotenv/load`.
#
# gem "dotenv-rails", require: "dotenv/rails-now"
# gem "dotenv", require: "dotenv/load"
# gem "gem-that-requires-env-variables"
#

require "dotenv/rails"
Dotenv::Railtie.load
require "dotenv/load"
warn '[DEPRECATION] `require "dotenv/rails-now"` is deprecated. Use `require "dotenv/load"` instead.', caller(1..1).first
Loading