runger_config 4.0.0 → 5.1.0
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/bin/release +27 -0
- data/lib/generators/runger/app_config/app_config_generator.rb +6 -10
- data/lib/generators/runger/config/config_generator.rb +44 -41
- data/lib/generators/runger/install/install_generator.rb +35 -37
- data/lib/runger/auto_cast.rb +3 -3
- data/lib/runger/config.rb +114 -94
- data/lib/runger/dynamic_config.rb +21 -23
- data/lib/runger/ejson_parser.rb +24 -24
- data/lib/runger/env.rb +50 -52
- data/lib/runger/ext/deep_dup.rb +33 -36
- data/lib/runger/ext/deep_freeze.rb +28 -32
- data/lib/runger/ext/flatten_names.rb +23 -27
- data/lib/runger/ext/hash.rb +26 -29
- data/lib/runger/ext/string_constantize.rb +12 -15
- data/lib/runger/loaders/base.rb +11 -15
- data/lib/runger/loaders/doppler.rb +38 -42
- data/lib/runger/loaders/ejson.rb +65 -63
- data/lib/runger/loaders/env.rb +6 -10
- data/lib/runger/loaders/yaml.rb +69 -66
- data/lib/runger/loaders.rb +69 -71
- data/lib/runger/option_parser_builder.rb +16 -18
- data/lib/runger/optparse_config.rb +11 -10
- data/lib/runger/rails/autoload.rb +24 -26
- data/lib/runger/rails/config.rb +13 -17
- data/lib/runger/rails/loaders/credentials.rb +53 -57
- data/lib/runger/rails/loaders/secrets.rb +21 -25
- data/lib/runger/rails/loaders/yaml.rb +1 -6
- data/lib/runger/rails/loaders.rb +3 -3
- data/lib/runger/rails/settings.rb +49 -49
- data/lib/runger/rails.rb +9 -11
- data/lib/runger/railtie.rb +3 -2
- data/lib/runger/rbs.rb +29 -29
- data/lib/runger/settings.rb +82 -84
- data/lib/runger/testing/helpers.rb +26 -28
- data/lib/runger/testing.rb +2 -2
- data/lib/runger/tracing.rb +143 -136
- data/lib/runger/type_casting.rb +16 -11
- data/lib/runger/utils/which.rb +10 -12
- data/lib/runger/version.rb +1 -1
- data/lib/runger.rb +1 -1
- data/lib/runger_config.rb +34 -27
- metadata +20 -19
data/lib/runger/ext/deep_dup.rb
CHANGED
@@ -1,48 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
value
|
15
|
-
end
|
3
|
+
# Extend Object through refinements
|
4
|
+
module Runger::Ext::DeepDup
|
5
|
+
refine ::Hash do
|
6
|
+
# Based on ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_dup
|
7
|
+
def deep_dup
|
8
|
+
each_with_object(dup) do |(key, value), hash|
|
9
|
+
hash[key] =
|
10
|
+
if value.is_a?(::Hash) || value.is_a?(::Array)
|
11
|
+
value.deep_dup
|
12
|
+
else
|
13
|
+
value
|
16
14
|
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
refine ::Array do
|
21
|
-
# From ActiveSupport http://api.rubyonrails.org/classes/Array.html#method-i-deep_dup
|
22
|
-
def deep_dup
|
23
|
-
map do |value|
|
24
|
-
if value.is_a?(::Hash) || value.is_a?(::Array)
|
25
|
-
value.deep_dup
|
26
|
-
else
|
27
|
-
value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
15
|
end
|
16
|
+
end
|
17
|
+
end
|
32
18
|
|
33
|
-
|
34
|
-
|
35
|
-
|
19
|
+
refine ::Array do
|
20
|
+
# From ActiveSupport http://api.rubyonrails.org/classes/Array.html#method-i-deep_dup
|
21
|
+
def deep_dup
|
22
|
+
map do |value|
|
23
|
+
if value.is_a?(::Hash) || value.is_a?(::Array)
|
24
|
+
value.deep_dup
|
25
|
+
else
|
26
|
+
value
|
36
27
|
end
|
37
28
|
end
|
29
|
+
end
|
30
|
+
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
refine ::Object do
|
33
|
+
def deep_dup
|
34
|
+
dup
|
35
|
+
end
|
36
|
+
end
|
44
37
|
|
45
|
-
|
38
|
+
refine ::Module do
|
39
|
+
def deep_dup
|
40
|
+
self
|
46
41
|
end
|
47
42
|
end
|
43
|
+
|
44
|
+
using self
|
48
45
|
end
|
@@ -1,44 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
each_value do |value|
|
11
|
-
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
12
|
-
end
|
13
|
-
end
|
3
|
+
# Add #deep_freeze to hashes and arrays
|
4
|
+
module Runger::Ext::DeepFreeze
|
5
|
+
refine ::Hash do
|
6
|
+
def deep_freeze
|
7
|
+
freeze
|
8
|
+
each_value do |value|
|
9
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
14
10
|
end
|
11
|
+
end
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
14
|
+
refine ::Array do
|
15
|
+
def deep_freeze
|
16
|
+
freeze
|
17
|
+
each do |value|
|
18
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
23
19
|
end
|
20
|
+
end
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
begin
|
24
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
25
|
+
rescue LoadError
|
26
|
+
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
28
|
+
if defined?(::ActiveSupport::HashWithIndifferentAccess)
|
29
|
+
refine ::ActiveSupport::HashWithIndifferentAccess do
|
30
|
+
def deep_freeze
|
31
|
+
freeze
|
32
|
+
each_value do |value|
|
33
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
38
34
|
end
|
39
35
|
end
|
40
|
-
|
41
|
-
using self
|
42
36
|
end
|
43
37
|
end
|
38
|
+
|
39
|
+
using self
|
44
40
|
end
|
@@ -1,37 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
buf << :"#{prefix}"
|
12
|
-
return buf
|
13
|
-
end
|
14
|
-
|
15
|
-
each_with_object(buf) do |name, acc|
|
16
|
-
if name.is_a?(::Symbol)
|
17
|
-
acc << :"#{prefix}.#{name}"
|
18
|
-
else
|
19
|
-
name.flatten_names(prefix, acc)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
3
|
+
# Convert Hash with mixed array and hash values to an
|
4
|
+
# array of paths.
|
5
|
+
module Runger::Ext::FlattenNames
|
6
|
+
refine ::Array do
|
7
|
+
def flatten_names(prefix, buf)
|
8
|
+
if empty?
|
9
|
+
buf << :"#{prefix}"
|
10
|
+
return buf
|
23
11
|
end
|
24
12
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
13
|
+
each_with_object(buf) do |name, acc|
|
14
|
+
if name.is_a?(::Symbol)
|
15
|
+
acc << :"#{prefix}.#{name}"
|
16
|
+
else
|
17
|
+
name.flatten_names(prefix, acc)
|
31
18
|
end
|
32
19
|
end
|
20
|
+
end
|
21
|
+
end
|
33
22
|
|
34
|
-
|
23
|
+
refine ::Hash do
|
24
|
+
def flatten_names(prefix = nil, buf = [])
|
25
|
+
each_with_object(buf) do |(k, v), acc|
|
26
|
+
parent = prefix ? "#{prefix}.#{k}" : k
|
27
|
+
v.flatten_names(parent, acc)
|
28
|
+
end
|
35
29
|
end
|
36
30
|
end
|
31
|
+
|
32
|
+
using self
|
37
33
|
end
|
data/lib/runger/ext/hash.rb
CHANGED
@@ -1,40 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
self[key.to_s] = value
|
12
|
-
end
|
13
|
-
|
14
|
-
self
|
15
|
-
end
|
3
|
+
# Extend Hash through refinements
|
4
|
+
module Runger::Ext::Hash
|
5
|
+
refine ::Hash do
|
6
|
+
def stringify_keys!
|
7
|
+
keys.each do |key|
|
8
|
+
value = delete(key)
|
9
|
+
self[key.to_s] = value
|
10
|
+
end
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
raise ArgumentError, "Path cannot contain nil" if path.compact.size != path.size
|
12
|
+
self
|
13
|
+
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
hash[k]
|
25
|
-
end
|
15
|
+
def bury(val, *path)
|
16
|
+
raise(ArgumentError, 'No path specified') if path.empty?
|
17
|
+
raise(ArgumentError, 'Path cannot contain nil') if path.compact.size != path.size
|
26
18
|
|
27
|
-
|
19
|
+
last_key = path.pop
|
20
|
+
hash =
|
21
|
+
path.reduce(self) do |hash, k|
|
22
|
+
hash[k] = {} unless hash.key?(k) && hash[k].is_a?(::Hash)
|
23
|
+
hash[k]
|
28
24
|
end
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
result[yield(key)] = value.is_a?(::Hash) ? value.deep_transform_keys(&block) : value
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
26
|
+
hash[last_key] = val
|
27
|
+
end
|
36
28
|
|
37
|
-
|
29
|
+
def deep_transform_keys(&block)
|
30
|
+
each_with_object({}) do |(key, value), result|
|
31
|
+
result[yield(key)] = value.is_a?(::Hash) ? value.deep_transform_keys(&block) : value
|
32
|
+
end
|
38
33
|
end
|
39
34
|
end
|
35
|
+
|
36
|
+
using self
|
40
37
|
end
|
@@ -1,23 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def safe_constantize
|
9
|
-
names = split("::")
|
3
|
+
# Add simple safe_constantize method to String
|
4
|
+
module Runger::Ext::StringConstantize
|
5
|
+
refine String do
|
6
|
+
def safe_constantize
|
7
|
+
names = split('::')
|
10
8
|
|
11
|
-
|
9
|
+
return nil if names.empty?
|
12
10
|
|
13
|
-
|
14
|
-
|
11
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
12
|
+
names.shift if names.size > 1 && names.first.empty?
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
14
|
+
names.inject(Object) do |constant, name|
|
15
|
+
break if constant.nil?
|
16
|
+
|
17
|
+
constant.const_get(name, false) if constant.const_defined?(name, false)
|
21
18
|
end
|
22
19
|
end
|
23
20
|
end
|
data/lib/runger/loaders/base.rb
CHANGED
@@ -1,21 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Base
|
6
|
-
include Tracing
|
3
|
+
class Runger::Loaders::Base
|
4
|
+
include ::Runger::Tracing
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(local:)
|
15
|
-
@local = local
|
16
|
-
end
|
17
|
-
|
18
|
-
def use_local? = @local == true
|
6
|
+
class << self
|
7
|
+
def call(local: Runger::Settings.use_local_files, **options)
|
8
|
+
new(local:).call(**options)
|
19
9
|
end
|
20
10
|
end
|
11
|
+
|
12
|
+
def initialize(local:)
|
13
|
+
@local = local
|
14
|
+
end
|
15
|
+
|
16
|
+
def use_local? = @local == true
|
21
17
|
end
|
@@ -1,61 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
class Doppler < Base
|
10
|
-
class RequestError < StandardError; end
|
7
|
+
class Runger::Loaders::Doppler < Runger::Loaders::Base
|
8
|
+
class RequestError < StandardError; end
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
class << self
|
11
|
+
attr_accessor :download_url
|
12
|
+
attr_writer :token
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def token
|
15
|
+
@token || ENV.fetch('DOPPLER_TOKEN', nil)
|
16
|
+
end
|
17
|
+
end
|
20
18
|
|
21
|
-
|
19
|
+
self.download_url = 'https://api.doppler.com/v3/configs/config/secrets/download'
|
22
20
|
|
23
|
-
|
24
|
-
|
21
|
+
def call(env_prefix:, **_options)
|
22
|
+
env_payload = parse_doppler_response(url: Runger::Loaders::Doppler.download_url, token: Runger::Loaders::Doppler.token)
|
25
23
|
|
26
|
-
|
24
|
+
env = ::Runger::Env.new(type_cast: ::Runger::NoCast, env_container: env_payload)
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
27
|
+
::Runger::Tracing.current_trace&.merge!(trace)
|
28
|
+
conf
|
29
|
+
end
|
30
|
+
end
|
33
31
|
|
34
|
-
|
32
|
+
private
|
35
33
|
|
36
|
-
|
37
|
-
|
34
|
+
def parse_doppler_response(url:, token:)
|
35
|
+
response = fetch_doppler_config(url, token)
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
unless response.is_a?(Net::HTTPSuccess)
|
38
|
+
raise(RequestError, "#{response.code} #{response.message}")
|
39
|
+
end
|
42
40
|
|
43
|
-
|
44
|
-
|
41
|
+
JSON.parse(response.read_body)
|
42
|
+
end
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
def fetch_doppler_config(url, token)
|
45
|
+
uri = URI.parse(url)
|
46
|
+
raise('Doppler token is required to load configuration from Doppler') if token.nil?
|
49
47
|
|
50
|
-
|
51
|
-
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
http.use_ssl = true if uri.scheme == 'https'
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
request = Net::HTTP::Get.new(uri)
|
52
|
+
request['Accept'] = 'application/json'
|
53
|
+
request['Authorization'] = "Bearer #{token}"
|
56
54
|
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
55
|
+
http.request(request)
|
60
56
|
end
|
61
57
|
end
|
data/lib/runger/loaders/ejson.rb
CHANGED
@@ -1,89 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'runger/ejson_parser'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
attr_accessor :bin_path
|
10
|
-
end
|
11
|
-
|
12
|
-
self.bin_path = "ejson"
|
13
|
-
|
14
|
-
def call(name:, ejson_namespace: name, ejson_parser: Runger::EJSONParser.new(EJSON.bin_path), **_options)
|
15
|
-
configs = []
|
5
|
+
class Runger::Loaders::EJSON < Runger::Loaders::Base
|
6
|
+
class << self
|
7
|
+
attr_accessor :bin_path
|
8
|
+
end
|
16
9
|
|
17
|
-
|
18
|
-
secrets_hash, rel_path =
|
19
|
-
extract_hash_from_rel_config_path(
|
20
|
-
ejson_parser: ejson_parser,
|
21
|
-
rel_config_path: rel_config_path
|
22
|
-
)
|
10
|
+
self.bin_path = 'ejson'
|
23
11
|
|
24
|
-
|
12
|
+
def call(
|
13
|
+
name:,
|
14
|
+
ejson_namespace: name,
|
15
|
+
ejson_parser: Runger::EJSONParser.new(Runger::Loaders::EJSON.bin_path),
|
16
|
+
**_options
|
17
|
+
)
|
18
|
+
configs = []
|
25
19
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
rel_config_paths.each do |rel_config_path|
|
21
|
+
secrets_hash, rel_path =
|
22
|
+
extract_hash_from_rel_config_path(
|
23
|
+
ejson_parser:,
|
24
|
+
rel_config_path:,
|
25
|
+
)
|
31
26
|
|
32
|
-
|
27
|
+
next unless secrets_hash
|
33
28
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
config_hash =
|
30
|
+
if ejson_namespace
|
31
|
+
secrets_hash[ejson_namespace]
|
32
|
+
else
|
33
|
+
secrets_hash.except('_public_key')
|
38
34
|
end
|
39
35
|
|
40
|
-
|
36
|
+
next unless config_hash.is_a?(Hash)
|
41
37
|
|
42
|
-
|
43
|
-
|
38
|
+
configs <<
|
39
|
+
trace!(:ejson, path: rel_path) do
|
40
|
+
config_hash
|
44
41
|
end
|
45
|
-
|
42
|
+
end
|
46
43
|
|
47
|
-
|
44
|
+
return {} if configs.empty?
|
48
45
|
|
49
|
-
|
50
|
-
|
46
|
+
configs.inject do |result_config, next_config|
|
47
|
+
::Runger::Utils.deep_merge!(result_config, next_config)
|
48
|
+
end
|
49
|
+
end
|
51
50
|
|
52
|
-
|
51
|
+
private
|
53
52
|
|
54
|
-
|
55
|
-
|
53
|
+
def rel_config_paths
|
54
|
+
chain = [environmental_rel_config_path]
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
"#{Settings.current_environment}/secrets.ejson",
|
62
|
-
default_rel_config_path
|
63
|
-
]
|
64
|
-
else
|
65
|
-
default_rel_config_path
|
66
|
-
end
|
67
|
-
end
|
56
|
+
chain << 'secrets.local.ejson' if use_local?
|
57
|
+
|
58
|
+
chain
|
59
|
+
end
|
68
60
|
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
def environmental_rel_config_path
|
62
|
+
if ::Runger::Settings.current_environment
|
63
|
+
# if environment file is absent, then take data from the default one
|
64
|
+
[
|
65
|
+
"#{::Runger::Settings.current_environment}/secrets.ejson",
|
66
|
+
default_rel_config_path,
|
67
|
+
]
|
68
|
+
else
|
69
|
+
default_rel_config_path
|
70
|
+
end
|
71
|
+
end
|
72
72
|
|
73
|
-
|
74
|
-
|
73
|
+
def default_rel_config_path
|
74
|
+
'secrets.ejson'
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
abs_path = "#{Settings.app_root}/#{rel_path}"
|
77
|
+
def extract_hash_from_rel_config_path(ejson_parser:, rel_config_path:)
|
78
|
+
rel_config_path = Array(rel_config_path)
|
79
79
|
|
80
|
-
|
80
|
+
rel_config_path.each do |rel_conf_path|
|
81
|
+
rel_path = "config/#{rel_conf_path}"
|
82
|
+
abs_path = "#{::Runger::Settings.app_root}/#{rel_path}"
|
81
83
|
|
82
|
-
|
83
|
-
end
|
84
|
+
result = ejson_parser.call(abs_path)
|
84
85
|
|
85
|
-
|
86
|
-
end
|
86
|
+
return [result, rel_path] if result
|
87
87
|
end
|
88
|
+
|
89
|
+
nil
|
88
90
|
end
|
89
91
|
end
|
data/lib/runger/loaders/env.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def call(env_prefix:, **_options)
|
7
|
-
env = ::Runger::Env.new(type_cast: ::Runger::NoCast)
|
3
|
+
class Runger::Loaders::Env < Runger::Loaders::Base
|
4
|
+
def call(env_prefix:, **_options)
|
5
|
+
env = ::Runger::Env.new(type_cast: ::Runger::NoCast)
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
7
|
+
env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
8
|
+
::Runger::Tracing.current_trace&.merge!(trace)
|
9
|
+
conf
|
14
10
|
end
|
15
11
|
end
|
16
12
|
end
|