ravioli 0.1.2 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92bc88993084463bb740d10f3b95d4282c083e492db56deb6326c37718cc6f94
4
- data.tar.gz: 077a5e8070664163c4f8508a9c2e1d7850d8241a4c96ad3b58a716ec9f0b98f2
3
+ metadata.gz: a0f1dc00885f0dfdf291b2e81d158f093808d5200c062ebec79a534332b88f9b
4
+ data.tar.gz: 6cf7a6e99487f216c3ca1ca0de08d57e7b2b0b2b7a2a5b69c7a23ee58912297b
5
5
  SHA512:
6
- metadata.gz: ba40d1bbfc6a08e842577f746a7ac21b17aaa94824dfe0dcd1b6aa86b23868f99bcad55c05a5bcb8dff1448249bf6ca797e84a54eec1f8004cc47be4c57a5019
7
- data.tar.gz: 6cb087130e095129930d2ec12d0b9505adae10e0d8838cd60d06db5b61ca6d3d3b164fcc1a2f8299b65be5083d2479b39693c9ceb9d5540b05a398d6826129c0
6
+ metadata.gz: 32c99eb0bd607e0ea8a700397edf49db62079b03c5e54684e4f53302abafdbbb368a0cfc014ccffd2e67f2a2beff2b6110c65cda437c620267be3af8e0389587
7
+ data.tar.gz: aa552b5b5f826d14fb483b9af2c56de0003fca8d7500dcc97ba842568c91be7b01dfe77bea53ef5775b278ca338913a855a08d0c5a84fb8b4f1a663ea9aa5ee9
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # Ravioli.rb 🍝
2
2
 
3
-
4
3
  **Grab a fork and twist your configuration spaghetti in a single, delicious dumpling!**
5
4
 
6
5
  Ravioli combines all of your app's runtime configuration into a unified, simple interface. **It combines YAML or JSON configuration files, encrypted Rails credentials, and ENV vars into one easy-to-consume interface** so you can focus on writing code and not on where configuration comes from.
@@ -217,7 +216,7 @@ Ravioli will then check for [encrypted credentials](https://guides.rubyonrails.o
217
216
  2. Then, it loads and applies `config/credentials/RAILS_ENV.yml.enc` over top of what it has already loaded
218
217
  3. Finally, IF `Rails.config.staging?` IS TRUE, it loads and applies `config/credentials/staging.yml.enc`
219
218
 
220
- This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of
219
+ This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of a "root" `config/credentials.yml.enc` file.
221
220
 
222
221
  ### All put together, it does this:
223
222
 
@@ -418,47 +417,47 @@ staging:
418
417
 
419
418
  ### Encryption keys in ENV
420
419
 
421
- Because Ravioli merges environment-specific credentials over top of the root credentials file, you'll need to provide encryption keys for two (or, if you have a staging setup, three) different files in ENV vars. As such, Ravioli looks for decryption keys in a fallback-specific way. Here's where it looks for each file:
422
-
423
- <table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody><tr><td>
424
-
425
- `config/credentials.yml.enc`
426
-
427
- </td><td>
428
-
429
- `ENV["RAILS_BASE_KEY"]`
420
+ Here are a few facts about credentials in Rails and how they're deployed:
430
421
 
431
- </td><td>
422
+ 1. Rails assumes you want to use the file that matches your environment, if it exists (e.g. `RAILS_ENV=production` will look for `config/credentials/production.yml.enc`)
423
+ 2. Rails does _not_ support environment-specfic keys, but it _does_ now aggressively loads credentials at boot time.
432
424
 
433
- `ENV["RAILS_MASTER_KEY"]`
425
+ **This means `RAILS_MASTER_KEY` MUST be the decryption key for your environment-specific credential file, if one exists.**
434
426
 
435
- </td></tr><tr><td>
427
+ But, because Ravioli merges environment-specific credentials over top of the root credentials file, you'll need to provide encryption keys for two (or, if you have a staging setup, three) different files in ENV vars. As such, Ravioli looks for decryption keys in a way that mirrors Rails' assumptions, but allows progressive layering of credentials.
436
428
 
437
- `config/credentials/production.yml.enc`
429
+ Here are a few examples
438
430
 
439
- </td><td>
431
+ <table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody>
440
432
 
441
- `ENV["RAILS_PRODUCTION_KEY"]`
433
+ <tr>
434
+ <td><code>config/credentials.yml.enc</code></td>
435
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
436
+ <td><code>ENV["RAILS_ROOT_KEY"]</code></td>
437
+ </tr>
442
438
 
443
- </td><td>
439
+ <tr>
440
+ <td><code>config/credentials/#{RAILS_ENV}.yml.enc</code></td>
441
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
442
+ <td><code>ENV["RAILS_#{RAILS_ENV}_KEY"]</code></td>
443
+ </tr>
444
444
 
445
- `ENV["RAILS_MASTER_KEY"]`
446
445
 
447
- </td></tr><tr><td>
446
+ <tr>
447
+ <td><code>config/credentials/staging.yml.enc</code></td>
448
+ <td><code>ENV["RAILS_MASTER_KEY"]</code></td>
449
+ <td><code>ENV["RAILS_STAGING_KEY"]</code></td>
450
+ </tr>
448
451
 
449
- `config/credentials/staging.yml.enc` (only if running on staging)
452
+ </tbody></table>
450
453
 
451
- </td><td>
452
-
453
- `ENV["RAILS_STAGING_KEY"]`
454
-
455
- </td><td>
456
-
457
- `ENV["RAILS_MASTER_KEY"]`
454
+ Credentials are loaded in that order, too, so that you can have a base setup on `config/credentials.yml.enc`, overlay that with production-specific stuff from `config/credentials/production.yml.enc`, and then short-circuit or redirect some stuff in `config/credentials/staging.yml.enc` for staging environments.
458
455
 
459
- </td></tr></tbody></table>
456
+ #### TLDR:
460
457
 
461
- Credentials are loaded in that order, too, so that you can have a base setup on `config/credentials.yml.enc`, overlay that with production-specific stuff from `config/credentials/production.yml.enc`, and then short-circuit or redirect some stuff in `config/credentials/staging.yml.enc` for staging environments.
458
+ 1. Set `RAILS_MASTER_KEY` to the key for your specific environment
459
+ 2. Set `RAILS_STAGING_KEY` to the key for your staging credentials (if deploying to staging AND you have staging-specific credentials)
460
+ 3. Set `RAILS_ROOT_KEY` to the key for your root credentials (if you have anything in `config/credentials.yml.enc`)
462
461
 
463
462
  ## License
464
463
 
data/lib/ravioli.rb CHANGED
@@ -40,4 +40,4 @@ module Ravioli
40
40
  end
41
41
  end
42
42
 
43
- require_relative "ravioli/engine" if defined?(Rails)
43
+ require_relative "ravioli/railtie" if defined?(Rails)
@@ -81,15 +81,6 @@ module Ravioli
81
81
  def add_staging_flag!(is_staging = Rails.env.production? && ENV["STAGING"].present?)
82
82
  is_staging = is_staging.present?
83
83
  configuration.staging = is_staging
84
- Rails.env.class_eval <<-EOC, __FILE__, __LINE__ + 1
85
- def staging?
86
- config = Rails.try(:config)
87
- return false unless config&.is_a?(Ravioli::Configuration)
88
-
89
- config.staging?
90
- end
91
- EOC
92
- is_staging
93
84
  end
94
85
 
95
86
  # Iterates through the config directory (including nested folders) and
@@ -104,14 +95,28 @@ module Ravioli
104
95
 
105
96
  # Loads Rails encrypted credentials that it can. Checks for corresponding private key files, or ENV vars based on the {Ravioli::Builder credentials preadmlogic}
106
97
  def auto_load_credentials!
107
- # Load the base config
108
- load_credentials(key_path: "config/master.key", env_name: "base")
109
-
110
- # Load any environment-specific configuration on top of it
111
- load_credentials("config/credentials/#{Rails.env}", key_path: "config/credentials/#{Rails.env}.key", env_name: "master")
98
+ # Load the root config (supports using the master key or `RAILS_ROOT_KEY`)
99
+ load_credentials(
100
+ key_path: "config/master.key",
101
+ env_names: %w[master root],
102
+ )
103
+
104
+ # Load any environment-specific configuration on top of it. Since Rails will try
105
+ # `RAILS_MASTER_KEY` from the environment, we assume the same
106
+ load_credentials(
107
+ "config/credentials/#{Rails.env}",
108
+ key_path: "config/credentials/#{Rails.env}.key",
109
+ env_names: ["master"],
110
+ )
112
111
 
113
112
  # Apply staging configuration on top of THAT, if need be
114
- load_credentials("config/credentials/staging", key_path: "config/credentials/staging.key") if configuration.staging?
113
+ if configuration.staging?
114
+ load_credentials(
115
+ "config/credentials/staging",
116
+ env_names: %w[staging master],
117
+ key_path: "config/credentials/staging.key",
118
+ )
119
+ end
115
120
  end
116
121
 
117
122
  # When the builder is done working, lock the configuration and return it
@@ -140,11 +145,30 @@ module Ravioli
140
145
  end
141
146
 
142
147
  # Load secure credentials using a key either from a file or the ENV
143
- def load_credentials(path = "credentials", key_path: path, env_name: path.split("/").last)
144
- credentials = parse_credentials(path, env_name: env_name, key_path: key_path)
145
- configuration.append(credentials) if credentials.present?
146
- rescue => error
147
- warn "Could not decrypt `#{path}.yml.enc' with key file `#{key_path}' or `ENV[\"#{env_name}\"]'", error, critical: false
148
+ def load_credentials(path = "credentials", key_path: path, env_names: path.split("/").last)
149
+ error = nil
150
+ env_names = Array(env_names).map { |env_name| parse_env_name(env_name) }
151
+ env_names.each do |env_name|
152
+ credentials = parse_credentials(path, env_name: env_name, key_path: key_path)
153
+ if credentials.present?
154
+ configuration.append(credentials)
155
+ return credentials
156
+ end
157
+ rescue => e
158
+ error = e
159
+ end
160
+
161
+ if error
162
+ attempted_names = ["key file `#{key_path}'"]
163
+ attempted_names.push(*env_names.map { |env_name| "`ENV[\"#{env_name}\"]'" })
164
+ attempted_names = attempted_names.to_sentence(two_words_connector: " or ", last_word_connector: ", or ")
165
+ warn(
166
+ "Could not decrypt `#{path}.yml.enc' with #{attempted_names}",
167
+ error,
168
+ critical: false,
169
+ )
170
+ end
171
+
148
172
  {}
149
173
  end
150
174
 
@@ -233,13 +257,17 @@ module Ravioli
233
257
  {name => config}
234
258
  end
235
259
 
260
+ def parse_env_name(env_name)
261
+ env_name = env_name.to_s
262
+ env_name.match?(/^RAILS_/) ? env_name : "RAILS_#{env_name.upcase}_KEY"
263
+ end
264
+
236
265
  def parse_credentials(path, key_path: path, env_name: path.split("/").last)
237
266
  @current_credentials = path
238
- env_name = env_name.to_s
239
- env_name = "RAILS_#{env_name.upcase}_KEY" unless env_name.upcase == env_name
267
+ env_name = parse_env_name(env_name)
240
268
  key_path = path_to_config_file_path(key_path, extnames: "key", quiet: true)
241
- options = {key_path: key_path}
242
- options[:env_key] = ENV[env_name].present? ? env_name : SecureRandom.hex(6)
269
+ options = {key_path: key_path.to_s}
270
+ options[:env_key] = ENV[env_name].present? ? env_name : "__RAVIOLI__#{SecureRandom.hex(6)}"
243
271
 
244
272
  path = path_to_config_file_path(path, extnames: "yml.enc")
245
273
  (Rails.application.encrypted(path, **options)&.config || {}).tap do
@@ -255,7 +283,7 @@ module Ravioli
255
283
  def parse_yaml_config_file(path)
256
284
  contents = File.read(path)
257
285
  erb = ERB.new(contents).tap { |renderer| renderer.filename = path.to_s }
258
- YAML.safe_load(erb.result, aliases: true)
286
+ YAML.safe_load(erb.result, [Symbol], aliases: true)
259
287
  end
260
288
 
261
289
  def path_to_config_file_path(path, extnames: EXTNAMES, quiet: false)
@@ -288,7 +316,7 @@ module Ravioli
288
316
  if @strict && critical
289
317
  raise BuildError.new(message, error)
290
318
  else
291
- Rails.logger.warn(message) if defined? Rails
319
+ Rails.logger.try(:warn, message) if defined? Rails
292
320
  $stderr.write message # rubocop:disable Rails/Output
293
321
  end
294
322
  end
@@ -1,15 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Ravioli
4
- class Engine < ::Rails::Engine
5
- # Bootstrap Ravioli onto the Rails app
6
- initializer "ravioli", before: :load_environment_config do |app|
7
- Rails.extend Ravioli::RailsConfig unless Rails.respond_to?(:config)
8
- end
9
- end
10
-
11
- private
3
+ require_relative "./staging_inquirer"
12
4
 
5
+ module Ravioli
13
6
  module RailsConfig
14
7
  def config
15
8
  Ravioli.default || Ravioli.build(namespace: Rails.application&.class&.module_parent, strict: Rails.env.production?) do |config|
@@ -19,4 +12,10 @@ module Ravioli
19
12
  end
20
13
  end
21
14
  end
15
+
16
+ class Railtie < ::Rails::Railtie
17
+ # Bootstrap Ravioli onto the Rails app
18
+ Rails.env.class.prepend Ravioli::StagingInquirer
19
+ Rails.extend Ravioli::RailsConfig unless Rails.respond_to?(:config)
20
+ end
22
21
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ravioli
4
+ # A module that we mix in to the `Rails.env` inquirer class to add some extra staging-related
5
+ # metadata
6
+ module StagingInquirer
7
+ # Add a `name` method to `Rails.env` that will return "staging" for staging environments, and
8
+ # otherwise the string's value
9
+ def name
10
+ staging? ? "staging" : to_s
11
+ end
12
+
13
+ # Add a `strict:` keyword to reduce `Rails.env.production && !Rails.env.staging` calls
14
+ def production?(strict: false)
15
+ is_production = super()
16
+ return is_production unless strict && is_production
17
+
18
+ is_production && !staging?
19
+ end
20
+
21
+ # Override staging inquiries to check against the current configuration
22
+ def staging?
23
+ Rails.try(:config)&.staging?
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ravioli
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.6"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravioli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flip Sasser
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -270,7 +270,8 @@ files:
270
270
  - lib/ravioli.rb
271
271
  - lib/ravioli/builder.rb
272
272
  - lib/ravioli/configuration.rb
273
- - lib/ravioli/engine.rb
273
+ - lib/ravioli/railtie.rb
274
+ - lib/ravioli/staging_inquirer.rb
274
275
  - lib/ravioli/version.rb
275
276
  homepage: https://github.com/flipsasser/ravioli
276
277
  licenses: