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

Enable passthrough by specifying environment variable name in NVAR_PASSTHROUGH #13

Merged
merged 6 commits into from
Mar 22, 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
11 changes: 10 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
nvar (0.2.0)
nvar (0.3.0)
activesupport (>= 5.0.0, < 8.0)

GEM
Expand Down Expand Up @@ -148,6 +148,7 @@ GEM
parser (3.3.0.5)
ast (~> 2.4.1)
racc
prism (0.24.0)
psych (5.1.2)
stringio
racc (1.7.3)
Expand Down Expand Up @@ -225,6 +226,12 @@ GEM
rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-lsp (0.14.3)
language_server-protocol (~> 3.17.0)
prism (>= 0.22.0, < 0.25)
sorbet-runtime (>= 0.5.10782)
ruby-lsp-rspec (0.1.10)
ruby-lsp (~> 0.14.0)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
simplecov (0.22.0)
Expand All @@ -233,6 +240,7 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
sorbet-runtime (0.5.11276)
standard (1.34.0)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
Expand Down Expand Up @@ -277,6 +285,7 @@ DEPENDENCIES
rails
rake (~> 12.0)
rspec (~> 3.0)
ruby-lsp-rspec
simplecov
standardrb
tempfile
Expand Down
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ If your app relies on lots of environment secrets, onboarding's tough. New team
You can use `Nvar` in Ruby apps, with out-of-the-box support provided for Rails.
## Installation

Add the gem to your Gemfile and install it with `bundle add nvar`. If you're not on Rails, you'll need to make sure that `Nvar` is required with `require 'nvar'`, and then manually call `Nvar::EnvironmentVariable.load_all` as early as is appropriate.
Add the gem to your Gemfile and install it with `bundle add nvar`. If you're not on Rails, you'll need to make sure that `Nvar` is required with `require 'nvar'`, and then manually call `Nvar.load_all` as early as is appropriate.

It's recommended to do this by adding the following to `config/application.rb`:

```rb
require "dotenv/load" # if using in tandem with dotenv
require "nvar"

Nvar.load_all
```

## Configuration

`Nvar` is configured by way of `config/environment_variables.yml`. If you're on Rails, this file will be created for you automatically. Each key corresponds to the name of a required environment variable, and houses its configuration, all of which is optional.
Expand Down Expand Up @@ -36,7 +46,7 @@ This is just a glimpse of `Nvar`'s greater aim - centralizing configuration for

### Passthrough

The final config option, `passthrough`, deserves some extra detail. By default, `Nvar` sets your environment constants to their actual values in development and production environments, and to their names in test environments.
The final config option, `passthrough`, deserves some extra detail. By default, `Nvar` sets your environment constants to their actual values in development and production environments, and to their names in test environments, in order to prevent your tests from depending on their values.

In production/development, or in test with passthrough active:

Expand All @@ -52,17 +62,20 @@ irb(main):001:0> REQUIRED_ENV_VAR
=> "REQUIRED_ENV_VAR"
```

Your tests shouldn't be reliant on your environment, so generally, you want to have `passthrough` set to `true` as little as possible. What it *is* useful for, however, is recording VCR cassettes. Set `passthrough: true` on necessary environment variables before recording VCR cassettes, then remove it and run your tests again to make sure they're not reliant on your environment.
Your tests shouldn't be reliant on your environment, so setting `passthrough` to `true` in `config/environment_variables.yml` isn't recommended. Passthrough is, however, useful for recording VCR cassettes. Set NVAR_PASSTHROUGH to a comma-separated list of environment variable names while running your tests, then unset it and run your tests again to make sure they're not reliant on your environment. For example:

```
NVAR_PASSTHROUGH=GITHUB_APP_PRIVATE_KEY,GITHUB_APP_INSTALLATION_ID bundle exec rspec
```

## Usage

Now that you've been through and configured the environment variables that are necessary for your app, `Nvar` will write your environment variables to top-level constants, cast to any types you've specified, and raise an informative error if any are absent.
### .env files

`Nvar` works well with gems like `dotenv-rails` that source their config from a `.env` file. If an environment variable is unset when the app initializes and isn't present in `.env`, it will be added to that file. If a default value is specified in your `Nvar` config, that will be passed to `.env` too.
`Nvar` works well with gems like `dotenv` that source their config from a `.env` file. If an environment variable is unset when the app initializes and isn't present in `.env`, it will be added to that file. If a default value is specified in your `Nvar` config, that will be passed to `.env` too.

When using gems such as `dotenv-rails`, make sure you load those first so that the environment is ready for `Nvar` to check.
When using gems such as `dotenv`, make sure you load those first so that the environment is ready for `Nvar` to check.

### `rake nvar:verify_environment_file`

Expand Down
13 changes: 12 additions & 1 deletion lib/nvar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
require "nvar/environment_variable"
require "nvar/engine" if defined?(Rails)
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/hash/reverse_merge"
require "active_support/string_inquirer"

# Centralized configuration for required environment variables in your Ruby app.
module Nvar
mattr_accessor :config_file_path, default: File.expand_path("config/environment_variables.yml")
mattr_accessor :env_file_path, default: File.expand_path(".env")
mattr_accessor :env

# Comments in .env files must have a leading '#' symbol. This cannot be
# followed by a space.
Expand Down Expand Up @@ -38,12 +41,21 @@ def message
end

class << self
def env
if defined?(Rails)
Rails.env
else
ActiveSupport::StringInquirer.new(@@env&.to_s || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
end
end

def configure_for_rails(app)
self.config_file_path = app.root.join("config/environment_variables.yml")
self.env_file_path = app.root.join(".env")
[config_file_path, env_file_path].each do |path|
File.open(path, "w") {} unless path.exist? # rubocop:disable Lint/EmptyBlock
end
self.env = Rails.env
end

def load_all
Expand All @@ -62,7 +74,6 @@ def filter_from_vcr_cassettes(config)

def all
variables.map do |variable_name, config|
# TODO: Passthrough from environment behaviour might need to go here?
EnvironmentVariable.new(**(config || {}).merge(name: variable_name))
end.partition(&:set?)
end
Expand Down
6 changes: 3 additions & 3 deletions lib/nvar/environment_variable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def initialize(name:, type: "String", filter_from_requests: nil, **args)
@type = type
@required = args[:required].nil? ? true : args[:required]
@filter_from_requests = filter_from_requests.yield_self { |f| [true, false].include?(f) ? f : f&.to_sym }
@value = fetch_value(**args.slice(:passthrough, :default_value))
@value = fetch_value(**args.slice(:passthrough, :default_value).with_defaults(passthrough: ENV.fetch("NVAR_PASSTHROUGH", "").split(",").include?(name.to_s)))
@defined = true
rescue KeyError
@value = args[:default_value]
Expand All @@ -26,7 +26,7 @@ def initialize(name:, type: "String", filter_from_requests: nil, **args)
def to_const
raise Nvar::EnvironmentVariableNotPresentError, self unless defined

Object.const_set(name, typecast_value)
Object.const_set(name, typecast_value) unless Object.const_defined?(name)
end

def set?
Expand Down Expand Up @@ -76,7 +76,7 @@ def typecast_value
end

def fetch_value(passthrough: false, default_value: nil)
return default_value || name if ENV["RAILS_ENV"] == "test" && !passthrough
return default_value || name.to_s if Nvar.env.test? && !passthrough

required ? ENV.fetch(name.to_s) : ENV[name.to_s]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/nvar/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Nvar
VERSION = "0.2.0"
VERSION = "0.3.0"
end
1 change: 1 addition & 0 deletions nvar.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "byebug"
spec.add_development_dependency "climate_control"
spec.add_development_dependency "rails"
spec.add_development_dependency "ruby-lsp-rspec"
spec.add_development_dependency "simplecov"
spec.add_development_dependency "standardrb"
spec.add_development_dependency "tempfile"
Expand Down
Loading
Loading