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 +4 -4
- data/README.md +29 -30
- data/lib/ravioli.rb +1 -1
- data/lib/ravioli/builder.rb +54 -26
- data/lib/ravioli/{engine.rb → railtie.rb} +8 -9
- data/lib/ravioli/staging_inquirer.rb +26 -0
- data/lib/ravioli/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0f1dc00885f0dfdf291b2e81d158f093808d5200c062ebec79a534332b88f9b
|
4
|
+
data.tar.gz: 6cf7a6e99487f216c3ca1ca0de08d57e7b2b0b2b7a2a5b69c7a23ee58912297b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
`
|
425
|
+
**This means `RAILS_MASTER_KEY` MUST be the decryption key for your environment-specific credential file, if one exists.**
|
434
426
|
|
435
|
-
|
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
|
-
|
429
|
+
Here are a few examples
|
438
430
|
|
439
|
-
</
|
431
|
+
<table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody>
|
440
432
|
|
441
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
452
|
+
</tbody></table>
|
450
453
|
|
451
|
-
|
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
|
-
|
456
|
+
#### TLDR:
|
460
457
|
|
461
|
-
|
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
data/lib/ravioli/builder.rb
CHANGED
@@ -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
|
108
|
-
load_credentials(
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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,
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
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
|
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
|
-
|
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
|
data/lib/ravioli/version.rb
CHANGED
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.
|
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-
|
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/
|
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:
|