ravioli 0.1.2 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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: