chamber 1.0.3 → 2.0.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/README.md +288 -173
- data/bin/chamber +2 -288
- data/lib/chamber.rb +19 -67
- data/lib/chamber/binary/heroku.rb +59 -0
- data/lib/chamber/binary/runner.rb +94 -0
- data/lib/chamber/binary/travis.rb +23 -0
- data/lib/chamber/commands/base.rb +33 -0
- data/lib/chamber/commands/comparable.rb +37 -0
- data/lib/chamber/commands/compare.rb +46 -0
- data/lib/chamber/commands/context_resolver.rb +72 -0
- data/lib/chamber/commands/files.rb +12 -0
- data/lib/chamber/commands/heroku.rb +30 -0
- data/lib/chamber/commands/heroku/clear.rb +25 -0
- data/lib/chamber/commands/heroku/compare.rb +31 -0
- data/lib/chamber/commands/heroku/pull.rb +30 -0
- data/lib/chamber/commands/heroku/push.rb +25 -0
- data/lib/chamber/commands/initialize.rb +73 -0
- data/lib/chamber/commands/securable.rb +48 -0
- data/lib/chamber/commands/secure.rb +16 -0
- data/lib/chamber/commands/show.rb +23 -0
- data/lib/chamber/commands/travis.rb +14 -0
- data/lib/chamber/commands/travis/secure.rb +35 -0
- data/lib/chamber/configuration.rb +34 -0
- data/lib/chamber/environmentable.rb +23 -0
- data/lib/chamber/errors/undecryptable_value_error.rb +6 -0
- data/lib/chamber/file.rb +17 -5
- data/lib/chamber/file_set.rb +18 -12
- data/lib/chamber/filters/boolean_conversion_filter.rb +41 -0
- data/lib/chamber/filters/decryption_filter.rb +69 -0
- data/lib/chamber/filters/encryption_filter.rb +57 -0
- data/lib/chamber/filters/environment_filter.rb +75 -0
- data/lib/chamber/filters/namespace_filter.rb +37 -0
- data/lib/chamber/filters/secure_filter.rb +39 -0
- data/lib/chamber/instance.rb +40 -0
- data/lib/chamber/namespace_set.rb +55 -16
- data/lib/chamber/rails/railtie.rb +1 -1
- data/lib/chamber/settings.rb +103 -42
- data/lib/chamber/system_environment.rb +3 -93
- data/lib/chamber/version.rb +2 -2
- data/spec/fixtures/settings.yml +27 -0
- data/spec/lib/chamber/commands/context_resolver_spec.rb +106 -0
- data/spec/lib/chamber/commands/files_spec.rb +19 -0
- data/spec/lib/chamber/commands/heroku/clear_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/compare_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/pull_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/push_spec.rb +11 -0
- data/spec/lib/chamber/commands/secure_spec.rb +29 -0
- data/spec/lib/chamber/commands/show_spec.rb +43 -0
- data/spec/lib/chamber/file_set_spec.rb +1 -1
- data/spec/lib/chamber/file_spec.rb +32 -9
- data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +44 -0
- data/spec/lib/chamber/filters/decryption_filter_spec.rb +55 -0
- data/spec/lib/chamber/filters/encryption_filter_spec.rb +48 -0
- data/spec/lib/chamber/filters/environment_filter_spec.rb +35 -0
- data/spec/lib/chamber/filters/namespace_filter_spec.rb +73 -0
- data/spec/lib/chamber/filters/secure_filter_spec.rb +36 -0
- data/spec/lib/chamber/namespace_set_spec.rb +61 -18
- data/spec/lib/chamber/settings_spec.rb +99 -23
- data/spec/lib/chamber/system_environment_spec.rb +1 -71
- data/spec/lib/chamber_spec.rb +40 -26
- data/spec/rails-2-test/config.ru +0 -0
- data/spec/rails-2-test/config/application.rb +5 -0
- data/spec/rails-2-test/script/console +0 -0
- data/spec/rails-3-test/config.ru +0 -0
- data/spec/rails-3-test/config/application.rb +5 -0
- data/spec/rails-3-test/script/rails +0 -0
- data/spec/rails-4-test/bin/rails +0 -0
- data/spec/rails-4-test/config.ru +0 -0
- data/spec/rails-4-test/config/application.rb +5 -0
- data/spec/spec_key +27 -0
- data/spec/spec_key.pub +9 -0
- metadata +85 -4
data/lib/chamber/settings.rb
CHANGED
@@ -1,18 +1,34 @@
|
|
1
1
|
require 'hashie/mash'
|
2
2
|
require 'chamber/system_environment'
|
3
3
|
require 'chamber/namespace_set'
|
4
|
+
require 'chamber/filters/namespace_filter'
|
5
|
+
require 'chamber/filters/encryption_filter'
|
6
|
+
require 'chamber/filters/decryption_filter'
|
7
|
+
require 'chamber/filters/environment_filter'
|
8
|
+
require 'chamber/filters/boolean_conversion_filter'
|
9
|
+
require 'chamber/filters/secure_filter'
|
4
10
|
|
5
11
|
###
|
6
12
|
# Internal: Represents the base settings storage needed for Chamber.
|
7
13
|
#
|
8
|
-
|
14
|
+
module Chamber
|
9
15
|
class Settings
|
10
16
|
|
11
17
|
attr_reader :namespaces
|
12
18
|
|
13
19
|
def initialize(options = {})
|
14
|
-
self.namespaces
|
15
|
-
self.
|
20
|
+
self.namespaces = options[:namespaces] || []
|
21
|
+
self.raw_data = options[:settings] || {}
|
22
|
+
self.decryption_key = options[:decryption_key]
|
23
|
+
self.encryption_key = options[:encryption_key]
|
24
|
+
self.pre_filters = options[:pre_filters] || [
|
25
|
+
Filters::NamespaceFilter,
|
26
|
+
]
|
27
|
+
self.post_filters = options[:post_filters] || [
|
28
|
+
Filters::DecryptionFilter,
|
29
|
+
Filters::EnvironmentFilter,
|
30
|
+
Filters::BooleanConversionFilter,
|
31
|
+
]
|
16
32
|
end
|
17
33
|
|
18
34
|
###
|
@@ -68,14 +84,14 @@ class Settings
|
|
68
84
|
# Internal: Merges a Settings object with another Settings object or
|
69
85
|
# a hash-like object.
|
70
86
|
#
|
71
|
-
# Also, if merging Settings, it will merge
|
87
|
+
# Also, if merging Settings, it will merge all other Settings data as well.
|
72
88
|
#
|
73
89
|
# Example:
|
74
90
|
#
|
75
91
|
# settings = Settings.new settings: { my_setting: 'my value' }
|
76
92
|
# other_settings = Settings.new settings: { my_other_setting: 'my other value' }
|
77
93
|
#
|
78
|
-
# settings.merge
|
94
|
+
# settings.merge other_settings
|
79
95
|
#
|
80
96
|
# settings
|
81
97
|
# # => {
|
@@ -83,70 +99,115 @@ class Settings
|
|
83
99
|
# 'my_other_setting' => 'my other value',
|
84
100
|
# }
|
85
101
|
#
|
102
|
+
# Returns a new Settings object
|
103
|
+
#
|
104
|
+
def merge(other)
|
105
|
+
other_settings = if other.is_a? Settings
|
106
|
+
other
|
107
|
+
elsif other.is_a? Hash
|
108
|
+
Settings.new(settings: other)
|
109
|
+
end
|
110
|
+
|
111
|
+
Settings.new(
|
112
|
+
encryption_key: encryption_key || other_settings.encryption_key,
|
113
|
+
decryption_key: decryption_key || other_settings.decryption_key,
|
114
|
+
namespaces: (namespaces + other_settings.namespaces),
|
115
|
+
settings: raw_data.merge(other_settings.raw_data))
|
116
|
+
end
|
117
|
+
|
118
|
+
###
|
119
|
+
# Internal: Returns the Settings data as a Hash for easy manipulation.
|
120
|
+
# Changes made to the hash will *not* be reflected in the original Settings
|
121
|
+
# object.
|
122
|
+
#
|
86
123
|
# Returns a Hash
|
87
124
|
#
|
88
|
-
def
|
89
|
-
|
90
|
-
|
125
|
+
def to_hash
|
126
|
+
data.to_hash
|
127
|
+
end
|
128
|
+
|
129
|
+
###
|
130
|
+
# Internal: Determines whether a Settings is equal to another hash-like
|
131
|
+
# object.
|
132
|
+
#
|
133
|
+
# Returns a Boolean
|
134
|
+
#
|
135
|
+
def ==(other)
|
136
|
+
self.to_hash == other.to_hash
|
91
137
|
end
|
92
138
|
|
139
|
+
###
|
140
|
+
# Internal: Determines whether a Settings is equal to another Settings.
|
141
|
+
#
|
142
|
+
# Returns a Boolean
|
143
|
+
#
|
93
144
|
def eql?(other)
|
94
145
|
other.is_a?( Chamber::Settings) &&
|
95
146
|
self.data == other.data &&
|
96
147
|
self.namespaces == other.namespaces
|
97
148
|
end
|
98
149
|
|
99
|
-
def
|
100
|
-
|
150
|
+
def secured
|
151
|
+
Settings.new( metadata.merge(
|
152
|
+
settings: raw_data,
|
153
|
+
pre_filters: [Filters::SecureFilter]))
|
101
154
|
end
|
102
155
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
def respond_to_missing?(name, include_private = false)
|
110
|
-
data.respond_to?(name, include_private)
|
156
|
+
def secure
|
157
|
+
Settings.new( metadata.merge(
|
158
|
+
settings: @raw_data,
|
159
|
+
pre_filters: [Filters::EncryptionFilter],
|
160
|
+
post_filters: [] ))
|
111
161
|
end
|
112
162
|
|
113
163
|
protected
|
114
164
|
|
115
|
-
|
116
|
-
|
165
|
+
attr_accessor :pre_filters,
|
166
|
+
:post_filters,
|
167
|
+
:encryption_key,
|
168
|
+
:decryption_key,
|
169
|
+
:raw_data
|
117
170
|
|
118
|
-
def
|
119
|
-
@
|
171
|
+
def raw_data=(new_raw_data)
|
172
|
+
@raw_data = Hashie::Mash.new(new_raw_data)
|
120
173
|
end
|
121
174
|
|
122
|
-
def
|
123
|
-
@
|
124
|
-
|
125
|
-
namespace_checked_data = if data_is_namespaced?
|
126
|
-
namespace_filtered_data
|
127
|
-
else
|
128
|
-
self.raw_data
|
129
|
-
end
|
175
|
+
def namespaces=(raw_namespaces)
|
176
|
+
@namespaces = NamespaceSet.new(raw_namespaces)
|
177
|
+
end
|
130
178
|
|
131
|
-
|
179
|
+
def raw_data
|
180
|
+
@filtered_raw_data ||= pre_filters.reduce(@raw_data) do |filtered_data, filter|
|
181
|
+
filter.execute({data: filtered_data}.
|
182
|
+
merge(metadata))
|
183
|
+
end
|
132
184
|
end
|
133
185
|
|
134
|
-
|
186
|
+
def data
|
187
|
+
@data ||= post_filters.reduce(raw_data) do |filtered_data, filter|
|
188
|
+
filter.execute({data: filtered_data}.
|
189
|
+
merge(metadata))
|
190
|
+
end
|
191
|
+
end
|
135
192
|
|
136
|
-
def
|
137
|
-
|
193
|
+
def metadata
|
194
|
+
{
|
195
|
+
namespaces: self.namespaces,
|
196
|
+
decryption_key: self.decryption_key,
|
197
|
+
encryption_key: self.encryption_key,
|
198
|
+
}
|
138
199
|
end
|
139
200
|
|
140
|
-
|
141
|
-
|
142
|
-
|
201
|
+
public
|
202
|
+
|
203
|
+
def method_missing(name, *args)
|
204
|
+
return data.public_send(name, *args) if data.respond_to?(name)
|
143
205
|
|
144
|
-
|
145
|
-
|
146
|
-
end
|
206
|
+
super
|
207
|
+
end
|
147
208
|
|
148
|
-
|
149
|
-
|
209
|
+
def respond_to_missing?(name, include_private = false)
|
210
|
+
data.respond_to?(name, include_private)
|
150
211
|
end
|
151
212
|
end
|
152
213
|
end
|
@@ -1,65 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'chamber/environmentable'
|
2
2
|
|
3
3
|
###
|
4
4
|
# Internal: Gives access to the existing environment for importing/exporting
|
5
5
|
# values.
|
6
6
|
#
|
7
|
-
|
7
|
+
module Chamber
|
8
8
|
module SystemEnvironment
|
9
|
-
|
10
|
-
###
|
11
|
-
# Internal: Allows the existing environment to be injected into the passed in
|
12
|
-
# hash. The hash that is passed in is *not* modified, instead a new hash is
|
13
|
-
# returned.
|
14
|
-
#
|
15
|
-
# Examples:
|
16
|
-
#
|
17
|
-
# ###
|
18
|
-
# # Injects the current environment variables
|
19
|
-
# #
|
20
|
-
# ENV['LEVEL_ONE_1_LEVEL_TWO_1'] = 'env value 1'
|
21
|
-
# ENV['LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1'] = 'env value 2'
|
22
|
-
#
|
23
|
-
# SystemEnvironment.inject_into(
|
24
|
-
# level_one_1: {
|
25
|
-
# level_two_1: 'value 1',
|
26
|
-
# level_two_2: {
|
27
|
-
# level_three_1: 'value 2' } } )
|
28
|
-
#
|
29
|
-
# # => {
|
30
|
-
# 'level_one_1' => {
|
31
|
-
# 'level_two_1' => 'env value 1',
|
32
|
-
# 'level_two_2' => {
|
33
|
-
# 'level_three_1' => 'env value 2',
|
34
|
-
# }
|
35
|
-
#
|
36
|
-
# ###
|
37
|
-
# # Can inject environment variables if said variables are prefixed
|
38
|
-
# #
|
39
|
-
# ENV['PREFIX_LEVEL_TWO_1'] = 'env value 1'
|
40
|
-
# ENV['PREFIX_LEVEL_TWO_2'] = 'env value 2'
|
41
|
-
#
|
42
|
-
# SystemEnvironment.inject_into({
|
43
|
-
# level_two_1: 'value 1',
|
44
|
-
# level_two_2: 'value 2'
|
45
|
-
# },
|
46
|
-
# ['prefix'])
|
47
|
-
#
|
48
|
-
# # => {
|
49
|
-
# 'level_two_1' => 'env value 1',
|
50
|
-
# 'level_two_2' => 'env value 2',
|
51
|
-
# }
|
52
|
-
#
|
53
|
-
#
|
54
|
-
def self.inject_into(settings = {}, parent_keys = [])
|
55
|
-
with_environment(settings, parent_keys,
|
56
|
-
->(key, value, environment_keys) do
|
57
|
-
{ key => inject_into(value, environment_keys) }
|
58
|
-
end,
|
59
|
-
->(key, value, environment_key) do
|
60
|
-
{ key => convert_value(ENV[environment_key] || value) }
|
61
|
-
end)
|
62
|
-
end
|
9
|
+
extend Environmentable
|
63
10
|
|
64
11
|
###
|
65
12
|
# Internal: Allows the environment variable-compatible variables to be
|
@@ -104,42 +51,5 @@ module SystemEnvironment
|
|
104
51
|
{ environment_key => value.to_s }
|
105
52
|
end)
|
106
53
|
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def self.with_environment(settings, parent_keys, hash_block, value_block)
|
111
|
-
environment_hash = Hashie::Mash.new
|
112
|
-
|
113
|
-
settings.each_pair do |key, value|
|
114
|
-
environment_keys = parent_keys.dup.push(key)
|
115
|
-
|
116
|
-
if value.respond_to? :each_pair
|
117
|
-
environment_hash.merge!(hash_block.call(key, value, environment_keys))
|
118
|
-
else
|
119
|
-
environment_key = environment_keys.join('_').upcase
|
120
|
-
|
121
|
-
environment_hash.merge!(value_block.call(key, value, environment_key))
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
environment_hash
|
126
|
-
end
|
127
|
-
|
128
|
-
def self.convert_value(value)
|
129
|
-
return nil if value.nil?
|
130
|
-
|
131
|
-
if value.is_a? String
|
132
|
-
case value
|
133
|
-
when 'false', 'f', 'no'
|
134
|
-
false
|
135
|
-
when 'true', 't', 'yes'
|
136
|
-
true
|
137
|
-
else
|
138
|
-
value
|
139
|
-
end
|
140
|
-
else
|
141
|
-
value
|
142
|
-
end
|
143
|
-
end
|
144
54
|
end
|
145
55
|
end
|
data/lib/chamber/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '
|
1
|
+
module Chamber
|
2
|
+
VERSION = '2.0.0'
|
3
3
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
development:
|
3
|
+
my_setting: my_dev_value
|
4
|
+
my_boolean: 'false'
|
5
|
+
my_dynamic_setting: 3
|
6
|
+
another_level:
|
7
|
+
setting_one: 1
|
8
|
+
setting_two: 4
|
9
|
+
level_three:
|
10
|
+
an_array:
|
11
|
+
- item 8
|
12
|
+
- item 2
|
13
|
+
- item 0
|
14
|
+
a_scalar: hello
|
15
|
+
test:
|
16
|
+
my_setting: my_value
|
17
|
+
my_boolean: 'false'
|
18
|
+
my_dynamic_setting: 2
|
19
|
+
another_level:
|
20
|
+
setting_one: 1
|
21
|
+
setting_two: 2
|
22
|
+
level_three:
|
23
|
+
an_array:
|
24
|
+
- item 1
|
25
|
+
- item 2
|
26
|
+
- item 3
|
27
|
+
a_scalar: hello
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'chamber/commands/context_resolver'
|
3
|
+
|
4
|
+
module Chamber
|
5
|
+
module Commands
|
6
|
+
describe ContextResolver do
|
7
|
+
let(:rails_2_path) { ::File.expand_path('../../../../rails-2-test', __FILE__) }
|
8
|
+
let(:rails_3_path) { ::File.expand_path('../../../../rails-3-test', __FILE__) }
|
9
|
+
let(:rails_4_path) { ::File.expand_path('../../../../rails-4-test', __FILE__) }
|
10
|
+
|
11
|
+
it 'does not attempt to do any resolution if all valid options are passed in' do
|
12
|
+
options = ContextResolver.resolve(basepath: 'my_path',
|
13
|
+
namespaces: 'ns')
|
14
|
+
|
15
|
+
expect(options[:basepath]).to eql 'my_path'
|
16
|
+
expect(options[:files]).to be_nil
|
17
|
+
expect(options[:namespaces]).to eql 'ns'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not attempt to do any resolution if files are passed in in place of a basepath' do
|
21
|
+
options = ContextResolver.resolve(files: 'my_files',
|
22
|
+
namespaces: 'ns')
|
23
|
+
|
24
|
+
expect(options[:files]).to eql 'my_files'
|
25
|
+
expect(options[:namespaces]).to eql 'ns'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'defaults the basepath to the rootpath if none is explicitly set' do
|
29
|
+
options = ContextResolver.resolve(rootpath: './app',
|
30
|
+
namespaces: 'ns')
|
31
|
+
|
32
|
+
expect(options[:basepath].to_s).to eql './app'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'sets the rootpath to the current working directory if none is passed in' do
|
36
|
+
allow(Pathname).to receive(:pwd).
|
37
|
+
and_return('my_dir')
|
38
|
+
|
39
|
+
options = ContextResolver.resolve
|
40
|
+
|
41
|
+
expect(options[:rootpath].to_s).to eql 'my_dir'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'sets the encryption key to the default if not passed in' do
|
45
|
+
options = ContextResolver.resolve(rootpath: rails_3_path)
|
46
|
+
|
47
|
+
expect(options[:encryption_key].to_s).to include 'rails-3-test/.chamber.pub.pem'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'sets the decryption key to the default if not passed in' do
|
51
|
+
options = ContextResolver.resolve(rootpath: rails_3_path)
|
52
|
+
|
53
|
+
expect(options[:decryption_key].to_s).to include 'rails-3-test/.chamber.pem'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'does not set the encryption key if the keyfile does not exist' do
|
57
|
+
options = ContextResolver.resolve(rootpath: './app')
|
58
|
+
|
59
|
+
expect(options[:encryption_key]).to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'does not set the decryption key if the keyfile does not exist' do
|
63
|
+
options = ContextResolver.resolve(rootpath: './app')
|
64
|
+
|
65
|
+
expect(options[:decryption_key]).to be_nil
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'sets the information to a Rails preset even if it is not pointing to a Rails app' do
|
69
|
+
options = ContextResolver.resolve(rootpath: './app',
|
70
|
+
preset: 'rails')
|
71
|
+
|
72
|
+
expect(options[:basepath].to_s).to include './app/config'
|
73
|
+
expect(options[:namespaces]).to eql []
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'sets the information to a Rails preset when the rootpath is a Rails app' do
|
77
|
+
options = ContextResolver.resolve(rootpath: rails_3_path,
|
78
|
+
preset: 'rails')
|
79
|
+
|
80
|
+
expect(options[:basepath].to_s).to include 'rails-3-test/config'
|
81
|
+
expect(options[:namespaces]).to eql ['development']
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'sets the basepath if inside a Rails 2 project' do
|
85
|
+
options = ContextResolver.resolve(rootpath: rails_2_path)
|
86
|
+
|
87
|
+
expect(options[:basepath].to_s).to include 'rails-2-test/config'
|
88
|
+
expect(options[:namespaces]).to eql ['development']
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'sets the basepath if inside a Rails 3 project' do
|
92
|
+
options = ContextResolver.resolve(rootpath: rails_3_path)
|
93
|
+
|
94
|
+
expect(options[:basepath].to_s).to include 'rails-3-test/config'
|
95
|
+
expect(options[:namespaces]).to eql ['development']
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'sets the basepath if inside a Rails 4 project' do
|
99
|
+
options = ContextResolver.resolve(rootpath: rails_4_path)
|
100
|
+
|
101
|
+
expect(options[:basepath].to_s).to include 'rails-4-test/config'
|
102
|
+
expect(options[:namespaces]).to eql ['development']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|