config_o_mat 0.3.0 → 0.4.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 +28 -0
- data/Gemfile.lock +16 -10
- data/config_o_mat.gemspec +1 -0
- data/lib/config_o_mat/configurator/op/refresh_all_profiles.rb +62 -36
- data/lib/config_o_mat/configurator/op/refresh_profile.rb +32 -0
- data/lib/config_o_mat/secrets_loader/memory.rb +1 -1
- data/lib/config_o_mat/shared/op/load_meta_config.rb +10 -0
- data/lib/config_o_mat/shared/types.rb +98 -9
- data/lib/config_o_mat/version.rb +1 -1
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26063499446b5cf904e29c5ef7e147c845bc6a2e72e8e37b32148c35f8b4eb96
|
4
|
+
data.tar.gz: 0cce14aaef3a7b479bce31bbe8dfeeb4f4159153c999de199227aa8dd49b9602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94869014f18070dfcb57e3907ad2c16b24d06f5693a353a7979f57e7b5333813ec9a4c458584173ec1622240c3fc6126dae0895ac53facef31fad11775879388
|
7
|
+
data.tar.gz: eff0f317b20431cb779765aa04af447a0402fc5ede9f46c69ddcbdc5403b11b85b79989e7315cb0f21262cad1818b06bb6bccf1b7e21a91638e91aba91db53ec
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
## 0.4.0
|
2
|
+
|
3
|
+
NEW FEATURES:
|
4
|
+
|
5
|
+
* Facter support. In your configomat config set a top level `facter` key to either truthy or a string. If truthy, facter data will be exposed in a profile named `facter` in all templates. If set to a string, facter data will be exposed in a profile with the given name in all templates.
|
6
|
+
* Attempting to access a configuration variable from a profile or secret that does not exist using `#[]` will now raise an exception indicating the key being incorrectly accessed. If you need to access optionally present configuration variables from profiles or secrets use `#fetch(key, nil)`.
|
7
|
+
|
8
|
+
## 0.3.0
|
9
|
+
|
10
|
+
NEW FEATURES:
|
11
|
+
|
12
|
+
* AWS Secrets Manager support, through an AWS AppConfig configuration. Values to pull from AWS Secrets Manager can be set using an "aws:secrets" key in any loaded AWS AppConfig JSON or YAML configuration. The value must be a dictionary. Keys in this dictionary will be exposed in the #secrets hash on the profile in templates. Values in this dictionary must be a dictionary containing at a minimum a `secret_id` key, which must be the id of an AWS Secrets Manager Secret. The dictionary may also contain a `version_id` or `version_stage` key to indicate which version of the secret to load, and a `content_type` key which may be one of `text/plan`, `application/json`, or `application/x-yaml` to indicate how the secret data should be parsed.
|
13
|
+
|
14
|
+
## 0.2.1
|
15
|
+
|
16
|
+
BUG FIXES:
|
17
|
+
|
18
|
+
* restart_all actually works.
|
19
|
+
|
20
|
+
## 0.2.0
|
21
|
+
|
22
|
+
NEW FEATURES:
|
23
|
+
|
24
|
+
* restart_all restart_mode for services to restart all running instances of an instantiated service.
|
25
|
+
|
26
|
+
## 0.1.0
|
27
|
+
|
28
|
+
Initial release
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
config_o_mat (0.
|
4
|
+
config_o_mat (0.4.0)
|
5
5
|
aws-sdk-appconfig (~> 1.18)
|
6
6
|
aws-sdk-secretsmanager (~> 1.57)
|
7
|
+
facter (~> 4.2, >= 4.2.8)
|
7
8
|
lifecycle_vm (~> 0.1.1)
|
8
9
|
logsformyfamily (~> 0.2)
|
9
10
|
ruby-dbus (~> 0.16.0)
|
@@ -13,25 +14,29 @@ GEM
|
|
13
14
|
remote: https://rubygems.org/
|
14
15
|
specs:
|
15
16
|
aws-eventstream (1.2.0)
|
16
|
-
aws-partitions (1.
|
17
|
-
aws-sdk-appconfig (1.
|
18
|
-
aws-sdk-core (~> 3, >= 3.
|
17
|
+
aws-partitions (1.574.0)
|
18
|
+
aws-sdk-appconfig (1.25.0)
|
19
|
+
aws-sdk-core (~> 3, >= 3.127.0)
|
19
20
|
aws-sigv4 (~> 1.1)
|
20
|
-
aws-sdk-core (3.
|
21
|
+
aws-sdk-core (3.130.0)
|
21
22
|
aws-eventstream (~> 1, >= 1.0.2)
|
22
23
|
aws-partitions (~> 1, >= 1.525.0)
|
23
24
|
aws-sigv4 (~> 1.1)
|
24
25
|
jmespath (~> 1.0)
|
25
|
-
aws-sdk-secretsmanager (1.
|
26
|
-
aws-sdk-core (~> 3, >= 3.
|
26
|
+
aws-sdk-secretsmanager (1.59.0)
|
27
|
+
aws-sdk-core (~> 3, >= 3.127.0)
|
27
28
|
aws-sigv4 (~> 1.1)
|
28
29
|
aws-sigv4 (1.4.0)
|
29
30
|
aws-eventstream (~> 1, >= 1.0.2)
|
30
31
|
diff-lcs (1.5.0)
|
31
32
|
docile (1.4.0)
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
facter (4.2.9)
|
34
|
+
hocon (~> 1.3)
|
35
|
+
thor (>= 1.0.1, < 2.0)
|
36
|
+
hocon (1.3.1)
|
37
|
+
jmespath (1.6.1)
|
38
|
+
lifecycle_vm (0.1.3)
|
39
|
+
logsformyfamily (0.3.0)
|
35
40
|
rspec (3.11.0)
|
36
41
|
rspec-core (~> 3.11.0)
|
37
42
|
rspec-expectations (~> 3.11.0)
|
@@ -53,6 +58,7 @@ GEM
|
|
53
58
|
simplecov_json_formatter (~> 0.1)
|
54
59
|
simplecov-html (0.12.3)
|
55
60
|
simplecov_json_formatter (0.1.4)
|
61
|
+
thor (1.2.1)
|
56
62
|
|
57
63
|
PLATFORMS
|
58
64
|
ruby
|
data/config_o_mat.gemspec
CHANGED
@@ -44,6 +44,7 @@ Gem::Specification.new do |spec|
|
|
44
44
|
spec.add_dependency('lifecycle_vm', '~> 0.1.1')
|
45
45
|
spec.add_dependency('ruby-dbus', '~> 0.16.0')
|
46
46
|
spec.add_dependency('sd_notify', '~> 0.1.1')
|
47
|
+
spec.add_dependency('facter', ['~> 4.2', '>= 4.2.8'])
|
47
48
|
|
48
49
|
spec.add_development_dependency('simplecov', '~> 0.21.2')
|
49
50
|
spec.add_development_dependency('rspec', '~> 3.10')
|
@@ -29,56 +29,82 @@ module ConfigOMat
|
|
29
29
|
self.last_refresh_time = Time.now.to_i
|
30
30
|
|
31
31
|
profile_defs.each do |(profile_name, definition)|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
if definition.kind_of?(ConfigOMat::Profile)
|
33
|
+
refresh_appconfig_profile(profile_name, definition)
|
34
|
+
elsif definition.kind_of?(ConfigOMat::FacterProfile)
|
35
|
+
refresh_facter_profile(profile_name)
|
36
|
+
else
|
37
|
+
error profile_name, "unknown profile type #{definition.class.name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def refresh_facter_profile(profile_name)
|
45
|
+
current_version = applied_profiles&.fetch(profile_name, nil)&.version
|
46
|
+
new_profile = ConfigOMat::LoadedFacterProfile.new(profile_name)
|
36
47
|
|
37
|
-
|
38
|
-
request[:client_configuration_version] = current_version if current_version
|
48
|
+
return if new_profile.version == current_version
|
39
49
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
error profile_name, e
|
45
|
-
nil
|
46
|
-
end
|
50
|
+
logger&.notice(
|
51
|
+
:updated_profile,
|
52
|
+
name: profile_name, previous_version: current_version, new_version: new_profile.version
|
53
|
+
)
|
47
54
|
|
48
|
-
|
55
|
+
profiles_to_apply << LoadedProfile.new(new_profile, nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
def refresh_appconfig_profile(profile_name, definition)
|
59
|
+
request = {
|
60
|
+
application: definition.application, environment: definition.environment,
|
61
|
+
configuration: definition.profile, client_id: client_id
|
62
|
+
}
|
63
|
+
|
64
|
+
current_version = applied_profiles&.fetch(profile_name, nil)&.version
|
65
|
+
request[:client_configuration_version] = current_version if current_version
|
66
|
+
|
67
|
+
response =
|
68
|
+
begin
|
69
|
+
appconfig_client.get_configuration(request)
|
70
|
+
rescue StandardError => e
|
71
|
+
error profile_name, e
|
72
|
+
nil
|
73
|
+
end
|
49
74
|
|
50
|
-
|
51
|
-
next if current_version && loaded_version == current_version
|
75
|
+
return if response.nil?
|
52
76
|
|
53
|
-
|
54
|
-
|
55
|
-
name: profile_name, previous_version: current_version, new_version: loaded_version
|
56
|
-
)
|
77
|
+
loaded_version = response.configuration_version
|
78
|
+
return if current_version && loaded_version == current_version
|
57
79
|
|
58
|
-
|
59
|
-
|
60
|
-
|
80
|
+
logger&.notice(
|
81
|
+
:updated_profile,
|
82
|
+
name: profile_name, previous_version: current_version, new_version: loaded_version
|
83
|
+
)
|
61
84
|
|
62
|
-
|
85
|
+
profile = LoadedAppconfigProfile.new(
|
86
|
+
profile_name, loaded_version, response.content.read, response.content_type
|
87
|
+
)
|
63
88
|
|
64
|
-
|
65
|
-
self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
|
66
|
-
secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
|
89
|
+
loaded_secrets = nil
|
67
90
|
|
68
|
-
|
91
|
+
if !profile.secret_defs.empty?
|
92
|
+
self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
|
93
|
+
secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
|
69
94
|
|
70
|
-
|
71
|
-
error :"#{profile_name}_secrets", vm.errors
|
72
|
-
next
|
73
|
-
end
|
95
|
+
vm = ConfigOMat::SecretsLoader::VM.new(secrets_loader_memory).call
|
74
96
|
|
75
|
-
|
76
|
-
|
77
|
-
|
97
|
+
if vm.errors?
|
98
|
+
error :"#{profile_name}_secrets", vm.errors
|
99
|
+
return
|
78
100
|
end
|
79
101
|
|
80
|
-
|
102
|
+
loaded_secrets = secrets_loader_memory.loaded_secrets.each_with_object({}) do |(key, value), hash|
|
103
|
+
hash[value.name] = value
|
104
|
+
end
|
81
105
|
end
|
106
|
+
|
107
|
+
profiles_to_apply << LoadedProfile.new(profile, loaded_secrets)
|
82
108
|
end
|
83
109
|
end
|
84
110
|
end
|
@@ -23,6 +23,38 @@ module ConfigOMat
|
|
23
23
|
writes :applying_profile, :secrets_loader_memory
|
24
24
|
|
25
25
|
def call
|
26
|
+
if applying_profile.loaded_profile_data.kind_of?(ConfigOMat::LoadedAppconfigProfile)
|
27
|
+
refresh_appconfig_profile
|
28
|
+
elsif applying_profile.loaded_profile_data.kind_of?(ConfigOMat::LoadedFacterProfile)
|
29
|
+
refresh_facter_profile
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def refresh_facter_profile
|
36
|
+
profile_name = applying_profile.name
|
37
|
+
profile_version = applying_profile.version
|
38
|
+
definition = profile_defs[profile_name]
|
39
|
+
new_profile = ConfigOMat::LoadedFacterProfile.new(profile_name)
|
40
|
+
|
41
|
+
if new_profile.version == profile_version
|
42
|
+
logger&.warning(
|
43
|
+
:no_update,
|
44
|
+
name: profile_name, version: profile_version
|
45
|
+
)
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
logger&.notice(
|
50
|
+
:updated_profile,
|
51
|
+
name: profile_name, previous_version: profile_version, new_version: new_profile.version
|
52
|
+
)
|
53
|
+
|
54
|
+
self.applying_profile = LoadedProfile.new(new_profile, nil)
|
55
|
+
end
|
56
|
+
|
57
|
+
def refresh_appconfig_profile
|
26
58
|
profile_name = applying_profile.name
|
27
59
|
profile_version = applying_profile.version
|
28
60
|
definition = profile_defs[profile_name]
|
@@ -126,6 +126,16 @@ module ConfigOMat
|
|
126
126
|
self.template_defs = instantiate.call(:templates, Template)
|
127
127
|
self.profile_defs = instantiate.call(:profiles, Profile)
|
128
128
|
|
129
|
+
facter = merged_config[:facter]
|
130
|
+
if facter
|
131
|
+
facter_key = facter.kind_of?(String) ? facter.to_sym : :facter
|
132
|
+
if profile_defs.key?(facter_key)
|
133
|
+
error :facter, "conflicts with profile #{facter_key}"
|
134
|
+
else
|
135
|
+
profile_defs[facter_key] = ConfigOMat::FacterProfile.new
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
129
139
|
self.logger = LogsForMyFamily::Logger.new if !logger
|
130
140
|
|
131
141
|
log_type = merged_config[:log_type]&.to_sym || :stdout
|
@@ -14,6 +14,8 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require 'facter'
|
18
|
+
|
17
19
|
require 'json'
|
18
20
|
require 'yaml'
|
19
21
|
require 'digest'
|
@@ -187,6 +189,83 @@ module ConfigOMat
|
|
187
189
|
end
|
188
190
|
end
|
189
191
|
|
192
|
+
class FacterProfile < ConfigItem
|
193
|
+
end
|
194
|
+
|
195
|
+
class LoadedFacterProfile < ConfigItem
|
196
|
+
CLEAR_FROM_FACTER = [
|
197
|
+
"memoryfree", "memoryfree_mb", "load_averages", "uptime", "system_uptime", "uptime_seconds", "uptime_hours", "uptime_days",
|
198
|
+
{"ec2_metadata" => ["identity-credentials"]},
|
199
|
+
{"memory" => [{"system" => ["capacity", "available_bytes", "used", "used_bytes", "available"] }] }
|
200
|
+
].freeze
|
201
|
+
|
202
|
+
attr_reader :name, :version, :contents
|
203
|
+
|
204
|
+
def initialize(name)
|
205
|
+
@name = name
|
206
|
+
load_from_facter
|
207
|
+
@version = contents.hash
|
208
|
+
end
|
209
|
+
|
210
|
+
def validate
|
211
|
+
error :name, 'must be present' if @name.nil? || @name.empty?
|
212
|
+
error :contents, 'must be present' if @contents.nil? || @contents.empty?
|
213
|
+
error :contents, 'must be a hash' if !@contents.kind_of?(Hash)
|
214
|
+
end
|
215
|
+
|
216
|
+
def hash
|
217
|
+
@name.hash ^ @version
|
218
|
+
end
|
219
|
+
|
220
|
+
def to_h
|
221
|
+
@contents
|
222
|
+
end
|
223
|
+
|
224
|
+
def eql?(other)
|
225
|
+
return false if !super(other)
|
226
|
+
return false if other.version != version || other.name != name
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def load_from_facter
|
233
|
+
Facter.clear
|
234
|
+
# This is to work around a bug in Facter wherein it fails to invalidate a second cache of the
|
235
|
+
# IMDSv2 token.
|
236
|
+
Facter::Resolvers::Ec2.instance_variable_set(:@v2_token, nil)
|
237
|
+
new_facts = Facter.to_hash
|
238
|
+
clear(new_facts, CLEAR_FROM_FACTER)
|
239
|
+
transform(new_facts)
|
240
|
+
@contents = new_facts
|
241
|
+
end
|
242
|
+
|
243
|
+
def clear(hash, diffs)
|
244
|
+
diffs.each do |diff|
|
245
|
+
if diff.kind_of?(Hash)
|
246
|
+
diff.each do |(key, values)|
|
247
|
+
clear(hash[key], values) if hash.key?(key)
|
248
|
+
end
|
249
|
+
elsif hash
|
250
|
+
hash.delete(diff)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def transform(hash)
|
256
|
+
return unless hash.kind_of?(Hash)
|
257
|
+
hash.transform_keys!(&:to_sym)
|
258
|
+
hash.default_proc = proc { |hash, key| raise KeyError.new("No key #{key.inspect} in profile #{name}", key: key, receiver: hash) }
|
259
|
+
hash.each_value do |value|
|
260
|
+
if value.kind_of?(Hash)
|
261
|
+
transform(value)
|
262
|
+
elsif value.kind_of?(Array)
|
263
|
+
value.each { |v| transform(v) }
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
190
269
|
class LoadedAppconfigProfile < ConfigItem
|
191
270
|
attr_reader :name, :version, :contents, :secret_defs
|
192
271
|
|
@@ -206,7 +285,12 @@ module ConfigOMat
|
|
206
285
|
if parser
|
207
286
|
begin
|
208
287
|
@contents = parser.call(contents)
|
209
|
-
|
288
|
+
if @contents.kind_of?(Hash)
|
289
|
+
parse_secrets
|
290
|
+
@contents.default_proc = proc do |hash, key|
|
291
|
+
raise KeyError.new("No key #{key.inspect} in profile #{name}", key: key, receiver: hash)
|
292
|
+
end
|
293
|
+
end
|
210
294
|
rescue StandardError => e
|
211
295
|
error :contents, e
|
212
296
|
end
|
@@ -239,7 +323,7 @@ module ConfigOMat
|
|
239
323
|
private
|
240
324
|
|
241
325
|
def parse_secrets
|
242
|
-
secret_entries = @contents
|
326
|
+
secret_entries = @contents.fetch(:"aws:secrets", nil)
|
243
327
|
return if secret_entries.nil?
|
244
328
|
|
245
329
|
error :contents_secrets, 'must be a dictionary' if !secret_entries.kind_of?(Hash)
|
@@ -305,6 +389,11 @@ module ConfigOMat
|
|
305
389
|
|
306
390
|
begin
|
307
391
|
@contents = LoadedAppconfigProfile::PARSERS[content_type].call(secret_string)
|
392
|
+
if @contents.kind_of?(Hash)
|
393
|
+
@contents.default_proc = proc do |hash, key|
|
394
|
+
raise KeyError.new("No key #{key.inspect} in secret #{name}", key: key, receiver: hash)
|
395
|
+
end
|
396
|
+
end
|
308
397
|
rescue StandardError => e
|
309
398
|
error :contents, e
|
310
399
|
end
|
@@ -335,22 +424,22 @@ module ConfigOMat
|
|
335
424
|
class LoadedProfile < ConfigItem
|
336
425
|
extend Forwardable
|
337
426
|
|
338
|
-
attr_reader :secrets, :
|
427
|
+
attr_reader :secrets, :loaded_profile_data
|
339
428
|
|
340
|
-
def_delegators :@
|
429
|
+
def_delegators :@loaded_profile_data, :name, :version, :contents
|
341
430
|
|
342
|
-
def initialize(
|
343
|
-
@
|
431
|
+
def initialize(loaded_profile_data, secrets)
|
432
|
+
@loaded_profile_data = loaded_profile_data
|
344
433
|
@secrets = secrets || {}
|
345
434
|
|
346
|
-
@errors = @
|
435
|
+
@errors = @loaded_profile_data.errors if @loaded_profile_data.errors?
|
347
436
|
end
|
348
437
|
|
349
438
|
def validate
|
350
439
|
end
|
351
440
|
|
352
441
|
def hash
|
353
|
-
@
|
442
|
+
@loaded_profile_data.hash ^ @secrets.hash
|
354
443
|
end
|
355
444
|
|
356
445
|
def to_h
|
@@ -359,7 +448,7 @@ module ConfigOMat
|
|
359
448
|
|
360
449
|
def eql?(other)
|
361
450
|
return false if !super(other)
|
362
|
-
return false if other.
|
451
|
+
return false if other.loaded_profile_data != @loaded_profile_data || other.secrets != @secrets
|
363
452
|
true
|
364
453
|
end
|
365
454
|
end
|
data/lib/config_o_mat/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: config_o_mat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Scarborough
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-appconfig
|
@@ -94,6 +94,26 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.1.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: facter
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.2'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 4.2.8
|
107
|
+
type: :runtime
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '4.2'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 4.2.8
|
97
117
|
- !ruby/object:Gem::Dependency
|
98
118
|
name: simplecov
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,6 +155,7 @@ files:
|
|
135
155
|
- ".rspec"
|
136
156
|
- ".ruby-gemset"
|
137
157
|
- ".ruby-version"
|
158
|
+
- CHANGELOG.md
|
138
159
|
- Gemfile
|
139
160
|
- Gemfile.lock
|
140
161
|
- LICENSE
|