paramsync 0.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 +7 -0
- data/LICENSE +12 -0
- data/README.md +334 -0
- data/bin/paramsync +8 -0
- data/lib/paramsync.rb +73 -0
- data/lib/paramsync/cli.rb +148 -0
- data/lib/paramsync/cli/check_command.rb +28 -0
- data/lib/paramsync/cli/config_command.rb +45 -0
- data/lib/paramsync/cli/encrypt_command.rb +22 -0
- data/lib/paramsync/cli/pull_command.rb +141 -0
- data/lib/paramsync/cli/push_command.rb +85 -0
- data/lib/paramsync/cli/targets_command.rb +21 -0
- data/lib/paramsync/config.rb +172 -0
- data/lib/paramsync/diff.rb +217 -0
- data/lib/paramsync/sync_target.rb +191 -0
- data/lib/paramsync/version.rb +5 -0
- data/paramsync.gemspec +31 -0
- metadata +103 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
# This software is public domain. No rights are reserved. See LICENSE for more information.
|
2
|
+
|
3
|
+
class Paramsync
|
4
|
+
class Diff
|
5
|
+
def initialize(target:, local:, remote:, mode:)
|
6
|
+
@target = target
|
7
|
+
@local = local
|
8
|
+
@remote = remote
|
9
|
+
@mode = mode
|
10
|
+
|
11
|
+
@all_keys = (@local.keys + @remote.keys).sort.uniq
|
12
|
+
|
13
|
+
@diff =
|
14
|
+
@all_keys.collect do |key|
|
15
|
+
excluded = false
|
16
|
+
op = :noop
|
17
|
+
if @remote.has_key?(key) and not @local.has_key?(key)
|
18
|
+
case @mode
|
19
|
+
when :push
|
20
|
+
op = @target.delete? ? :delete : :ignore
|
21
|
+
when :pull
|
22
|
+
op = :create
|
23
|
+
end
|
24
|
+
elsif @local.has_key?(key) and not @remote.has_key?(key)
|
25
|
+
case @mode
|
26
|
+
when :push
|
27
|
+
op = :create
|
28
|
+
when :pull
|
29
|
+
op = @target.delete? ? :delete : :ignore
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if @remote[key] == @local[key]
|
33
|
+
op = :noop
|
34
|
+
else
|
35
|
+
op = :update
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ssm_key = [@target.prefix, key].compact.join("/").gsub('/.', '/').squeeze("/")
|
40
|
+
|
41
|
+
if @target.exclude.include?(key) or @target.exclude.include?(ssm_key)
|
42
|
+
op = :ignore
|
43
|
+
excluded = true
|
44
|
+
end
|
45
|
+
|
46
|
+
filename =
|
47
|
+
case @target.type
|
48
|
+
when :dir then File.join(@target.base_path, key)
|
49
|
+
when :file then @target.base_path
|
50
|
+
end
|
51
|
+
|
52
|
+
display_filename =
|
53
|
+
case @target.type
|
54
|
+
when :dir then File.join(@target.base_path, key).trim_path
|
55
|
+
when :file then "#{@target.base_path.trim_path}#{':'.gray}#{key.cyan}"
|
56
|
+
end
|
57
|
+
|
58
|
+
OpenStruct.new(
|
59
|
+
op: op,
|
60
|
+
excluded: excluded,
|
61
|
+
relative_path: key,
|
62
|
+
filename: filename,
|
63
|
+
display_filename: display_filename,
|
64
|
+
ssm_key: ssm_key,
|
65
|
+
local_content: @local[key],
|
66
|
+
remote_content: @remote[key],
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def items_to_delete
|
72
|
+
@diff.select { |d| d.op == :delete }
|
73
|
+
end
|
74
|
+
|
75
|
+
def items_to_update
|
76
|
+
@diff.select { |d| d.op == :update }
|
77
|
+
end
|
78
|
+
|
79
|
+
def items_to_create
|
80
|
+
@diff.select { |d| d.op == :create }
|
81
|
+
end
|
82
|
+
|
83
|
+
def items_to_ignore
|
84
|
+
@diff.select { |d| d.op == :ignore }
|
85
|
+
end
|
86
|
+
|
87
|
+
def items_to_exclude
|
88
|
+
@diff.select { |d| d.op == :ignore and d.excluded == true }
|
89
|
+
end
|
90
|
+
|
91
|
+
def items_to_noop
|
92
|
+
@diff.select { |d| d.op == :noop }
|
93
|
+
end
|
94
|
+
|
95
|
+
def items_to_change
|
96
|
+
@diff.select { |d| [:delete, :update, :create].include?(d.op) }
|
97
|
+
end
|
98
|
+
|
99
|
+
def final_items
|
100
|
+
case @mode
|
101
|
+
when :push then @local
|
102
|
+
when :pull then @remote
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def any_changes?
|
107
|
+
self.items_to_change.count > 0
|
108
|
+
end
|
109
|
+
|
110
|
+
def print_report
|
111
|
+
puts '='*85
|
112
|
+
puts @target.description(@mode)
|
113
|
+
|
114
|
+
puts " Keys scanned: #{@diff.count}"
|
115
|
+
if Paramsync.config.verbose?
|
116
|
+
puts " Keys ignored: #{self.items_to_ignore.count}"
|
117
|
+
puts " Keys in sync: #{self.items_to_noop.count}"
|
118
|
+
end
|
119
|
+
|
120
|
+
puts if self.any_changes?
|
121
|
+
|
122
|
+
from_content_key, to_content_key, to_path_key, to_type_display_name =
|
123
|
+
case @mode
|
124
|
+
when :push then [:local_content, :remote_content, :ssm_key, "Keys"]
|
125
|
+
when :pull
|
126
|
+
case @target.type
|
127
|
+
when :dir then [:remote_content, :local_content, :display_filename, "Files"]
|
128
|
+
when :file then [:remote_content, :local_content, :display_filename, "File entries"]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
@diff.each do |item|
|
133
|
+
case item.op
|
134
|
+
when :create
|
135
|
+
puts "CREATE".bold.green + " #{item[to_path_key]}"
|
136
|
+
if(item[from_content_key][1])
|
137
|
+
puts '[SECURE]'
|
138
|
+
end
|
139
|
+
puts '-'*85
|
140
|
+
# simulate diff but without complaints about line endings
|
141
|
+
item[from_content_key][0].each_line do |line|
|
142
|
+
puts "+#{line.chomp}".green
|
143
|
+
end
|
144
|
+
puts '-'*85
|
145
|
+
|
146
|
+
when :update
|
147
|
+
puts "UPDATE".bold + " #{item[to_path_key]}"
|
148
|
+
if item[to_content_key][1] != item[from_content_key][1]
|
149
|
+
puts "#{item[to_content_key][1] ? '[SECURE]' : 'insecure'} => #{item[from_content_key][1] ? '[SECURE]' : 'insecure'}"
|
150
|
+
end
|
151
|
+
puts '-'*85
|
152
|
+
if item[to_content_key][0] != item[from_content_key][0]
|
153
|
+
puts Diffy::Diff.new(item[to_content_key][0], item[from_content_key][0]).to_s(:color)
|
154
|
+
end
|
155
|
+
puts '-'*85
|
156
|
+
|
157
|
+
when :delete
|
158
|
+
if @target.delete?
|
159
|
+
puts "DELETE".bold.red + " #{item[to_path_key]}"
|
160
|
+
puts '-'*85
|
161
|
+
# simulate diff but without complaints about line endings
|
162
|
+
item[to_content_key][0].each_line do |line|
|
163
|
+
puts "-#{line.chomp}".red
|
164
|
+
end
|
165
|
+
puts '-'*85
|
166
|
+
else
|
167
|
+
if Paramsync.config.verbose?
|
168
|
+
puts "IGNORE".bold + " #{item[to_path_key]}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
when :ignore
|
173
|
+
if Paramsync.config.verbose?
|
174
|
+
puts "IGNORE".bold + " #{item[to_path_key]}"
|
175
|
+
end
|
176
|
+
|
177
|
+
when :noop
|
178
|
+
if Paramsync.config.verbose?
|
179
|
+
puts "NO-OP!".bold + " #{item[to_path_key]}"
|
180
|
+
end
|
181
|
+
|
182
|
+
else
|
183
|
+
if Paramsync.config.verbose?
|
184
|
+
STDERR.puts "WARNING: unexpected operation '#{item.op}' for #{item[to_path_key]}"
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if self.items_to_create.count > 0
|
191
|
+
puts
|
192
|
+
puts "#{to_type_display_name} to create: #{self.items_to_create.count}".bold
|
193
|
+
self.items_to_create.each do |item|
|
194
|
+
puts "+ #{item[to_path_key]}".green
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
if self.items_to_update.count > 0
|
199
|
+
puts
|
200
|
+
puts "#{to_type_display_name} to update: #{self.items_to_update.count}".bold
|
201
|
+
self.items_to_update.each do |item|
|
202
|
+
puts "~ #{item[to_path_key]}".blue
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
if @target.delete?
|
207
|
+
if self.items_to_delete.count > 0
|
208
|
+
puts
|
209
|
+
puts "#{to_type_display_name} to delete: #{self.items_to_delete.count}".bold
|
210
|
+
self.items_to_delete.each do |item|
|
211
|
+
puts "- #{item[to_path_key]}".red
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# This software is public domain. No rights are reserved. See LICENSE for more information.
|
2
|
+
|
3
|
+
class Paramsync
|
4
|
+
class SyncTarget
|
5
|
+
VALID_CONFIG_KEYS = %w( name type region prefix path exclude chomp delete erb_enabled account )
|
6
|
+
attr_accessor :name, :type, :region, :prefix, :path, :exclude, :erb_enabled, :account, :ssm
|
7
|
+
|
8
|
+
REQUIRED_CONFIG_KEYS = %w( prefix region account )
|
9
|
+
VALID_TYPES = [ :dir, :file ]
|
10
|
+
DEFAULT_TYPE = :dir
|
11
|
+
|
12
|
+
def initialize(config:, account:, base_dir:)
|
13
|
+
if not config.is_a? Hash
|
14
|
+
raise Paramsync::ConfigFileInvalid.new("Sync target entries must be specified as hashes")
|
15
|
+
end
|
16
|
+
|
17
|
+
if (config.keys - Paramsync::SyncTarget::VALID_CONFIG_KEYS) != []
|
18
|
+
raise Paramsync::ConfigFileInvalid.new("Only the following keys are valid in a sync target entry: #{Paramsync::SyncTarget::VALID_CONFIG_KEYS.join(", ")}")
|
19
|
+
end
|
20
|
+
|
21
|
+
if (Paramsync::SyncTarget::REQUIRED_CONFIG_KEYS - config.keys) != []
|
22
|
+
raise Paramsync::ConfigFileInvalid.new("The following keys are required in a sync target entry: #{Paramsync::SyncTarget::REQUIRED_CONFIG_KEYS.join(", ")}")
|
23
|
+
end
|
24
|
+
|
25
|
+
@base_dir = base_dir
|
26
|
+
self.region = config['region']
|
27
|
+
self.prefix = config['prefix']
|
28
|
+
self.account = config['account']
|
29
|
+
self.path = config['path'] || config['prefix']
|
30
|
+
self.name = config['name']
|
31
|
+
self.type = (config['type'] || Paramsync::SyncTarget::DEFAULT_TYPE).to_sym
|
32
|
+
unless Paramsync::SyncTarget::VALID_TYPES.include?(self.type)
|
33
|
+
raise Paramsync::ConfigFileInvalid.new("Sync target '#{self.name || self.path}' has type '#{self.type}'. But only the following types are valid: #{Paramsync::SyncTarget::VALID_TYPES.collect(&:to_s).join(", ")}")
|
34
|
+
end
|
35
|
+
|
36
|
+
if self.type == :file and File.directory?(self.base_path)
|
37
|
+
raise Paramsync::ConfigFileInvalid.new("Sync target '#{self.name || self.path}' has type 'file', but path '#{self.path}' is a directory.")
|
38
|
+
end
|
39
|
+
|
40
|
+
self.exclude = config['exclude'] || []
|
41
|
+
if config.has_key?('chomp')
|
42
|
+
@do_chomp = config['chomp'] ? true : false
|
43
|
+
end
|
44
|
+
if config.has_key?('delete')
|
45
|
+
@do_delete = config['delete'] ? true : false
|
46
|
+
else
|
47
|
+
@do_delete = false
|
48
|
+
end
|
49
|
+
|
50
|
+
self.ssm = Aws::SSM::Client.new(
|
51
|
+
region: region,
|
52
|
+
credentials: Aws::AssumeRoleCredentials.new(
|
53
|
+
client: Aws::STS::Client.new(region: region),
|
54
|
+
role_arn: account,
|
55
|
+
role_session_name: "paramsync"
|
56
|
+
),
|
57
|
+
)
|
58
|
+
self.erb_enabled = config['erb_enabled']
|
59
|
+
end
|
60
|
+
|
61
|
+
def erb_enabled?
|
62
|
+
@erb_enabled
|
63
|
+
end
|
64
|
+
|
65
|
+
def chomp?
|
66
|
+
@do_chomp
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete?
|
70
|
+
@do_delete
|
71
|
+
end
|
72
|
+
|
73
|
+
def description(mode = :push)
|
74
|
+
if mode == :pull
|
75
|
+
"#{self.name.nil? ? '' : self.name.bold + "\n"}#{'ssm'.cyan}:#{self.region.green}:#{self.prefix} => #{'local'.blue}:#{self.path}"
|
76
|
+
else
|
77
|
+
"#{self.name.nil? ? '' : self.name.bold + "\n"}#{'local'.blue}:#{self.path} => #{'ssm'.cyan}:#{self.region.green}:#{self.prefix}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear_cache
|
82
|
+
@base_path = nil
|
83
|
+
@local_files = nil
|
84
|
+
@local_items = nil
|
85
|
+
@remote_items = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def base_path
|
89
|
+
@base_path ||= File.join(@base_dir, self.path)
|
90
|
+
end
|
91
|
+
|
92
|
+
def local_files
|
93
|
+
# see https://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
|
94
|
+
@local_files ||= Dir["#{self.base_path}/**{,/*/**}/*"].select { |f| File.file?(f) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def local_items
|
98
|
+
return @local_items if not @local_items.nil?
|
99
|
+
@local_items = {}
|
100
|
+
|
101
|
+
case self.type
|
102
|
+
when :dir
|
103
|
+
self.local_files.each do |local_file|
|
104
|
+
@local_items[local_file.sub(%r{^#{self.base_path}/?}, '')] =
|
105
|
+
load_local_file(local_file)
|
106
|
+
end
|
107
|
+
|
108
|
+
when :file
|
109
|
+
if File.exist?(self.base_path)
|
110
|
+
@local_items = local_items_from_file
|
111
|
+
end
|
112
|
+
end
|
113
|
+
@local_items.transform_values! do |val|
|
114
|
+
is_kms = val.bytes[0] == 0x01
|
115
|
+
if is_kms
|
116
|
+
val = Paramsync.config.kms_client.decrypt(
|
117
|
+
ciphertext_blob: val
|
118
|
+
).plaintext
|
119
|
+
end
|
120
|
+
[val, is_kms]
|
121
|
+
end
|
122
|
+
@local_items
|
123
|
+
end
|
124
|
+
|
125
|
+
def local_items_from_file
|
126
|
+
if erb_enabled?
|
127
|
+
loaded_file = YAML.load(ERB.new(File.read(self.base_path)).result)
|
128
|
+
else
|
129
|
+
loaded_file = YAML.load_file(self.base_path)
|
130
|
+
end
|
131
|
+
|
132
|
+
flatten_hash(nil, loaded_file)
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_local_file(local_file)
|
136
|
+
file = File.read(local_file)
|
137
|
+
|
138
|
+
if self.chomp?
|
139
|
+
encoded_file = file.chomp.force_encoding(Encoding::ASCII_8BIT)
|
140
|
+
else
|
141
|
+
encoded_file = file.force_encoding(Encoding::ASCII_8BIT)
|
142
|
+
end
|
143
|
+
|
144
|
+
return ERB.new(encoded_file).result if erb_enabled?
|
145
|
+
encoded_file
|
146
|
+
end
|
147
|
+
|
148
|
+
def remote_items
|
149
|
+
return @remote_items if not @remote_items.nil?
|
150
|
+
@remote_items = {}
|
151
|
+
|
152
|
+
resp = self.ssm.get_parameters_by_path(
|
153
|
+
path: self.prefix,
|
154
|
+
recursive: true,
|
155
|
+
with_decryption: true
|
156
|
+
)
|
157
|
+
|
158
|
+
return @remote_items if resp.values.nil?
|
159
|
+
resp.flat_map(&:parameters).each do |param|
|
160
|
+
@remote_items[param.name.gsub(self.prefix, '')] = [(param.value.nil? ? '' : param.value), param.type == 'SecureString']
|
161
|
+
end
|
162
|
+
|
163
|
+
@remote_items
|
164
|
+
end
|
165
|
+
|
166
|
+
def diff(mode)
|
167
|
+
Paramsync::Diff.new(target: self, local: self.local_items, remote: self.remote_items, mode: mode)
|
168
|
+
end
|
169
|
+
|
170
|
+
private def flatten_hash(prefix, hash)
|
171
|
+
new_hash = {}
|
172
|
+
|
173
|
+
hash.each do |k, v|
|
174
|
+
if k == '_' && !prefix.nil?
|
175
|
+
new_key = prefix
|
176
|
+
else
|
177
|
+
new_key = [prefix, k].compact.join('.').gsub('/.', '/')
|
178
|
+
end
|
179
|
+
|
180
|
+
case v
|
181
|
+
when Hash
|
182
|
+
new_hash.merge!(flatten_hash(new_key, v))
|
183
|
+
else
|
184
|
+
new_hash[new_key] = v.to_s
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
new_hash
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
data/paramsync.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'lib/paramsync/version.rb'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'paramsync'
|
5
|
+
s.version = Paramsync::VERSION
|
6
|
+
s.authors = ['David Adams', 'Jacob Burroughs']
|
7
|
+
s.email = 'maths22@gmail.com'
|
8
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
9
|
+
s.license = 'CC0'
|
10
|
+
s.homepage = 'https://github.com/maths22/paramsync'
|
11
|
+
s.required_ruby_version = '>=2.4.0'
|
12
|
+
|
13
|
+
s.summary = 'Simple filesystem-to-aws parameter store synchronization'
|
14
|
+
s.description =
|
15
|
+
'Syncs content from the filesystem to the aws parameter store. Derived from constancy for consul'
|
16
|
+
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
s.files = Dir["lib/**/*.rb"] + [
|
19
|
+
'bin/paramsync',
|
20
|
+
'README.md',
|
21
|
+
'LICENSE',
|
22
|
+
'paramsync.gemspec'
|
23
|
+
]
|
24
|
+
s.bindir = 'bin'
|
25
|
+
s.executables = ['paramsync']
|
26
|
+
|
27
|
+
s.add_dependency 'aws-sdk-ssm', '~> 1.89.0'
|
28
|
+
s.add_dependency 'aws-sdk-kms', '~> 1.37.0'
|
29
|
+
|
30
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paramsync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Adams
|
8
|
+
- Jacob Burroughs
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2021-02-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk-ssm
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.89.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.89.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: aws-sdk-kms
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.37.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.37.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
description: Syncs content from the filesystem to the aws parameter store. Derived
|
57
|
+
from constancy for consul
|
58
|
+
email: maths22@gmail.com
|
59
|
+
executables:
|
60
|
+
- paramsync
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- bin/paramsync
|
67
|
+
- lib/paramsync.rb
|
68
|
+
- lib/paramsync/cli.rb
|
69
|
+
- lib/paramsync/cli/check_command.rb
|
70
|
+
- lib/paramsync/cli/config_command.rb
|
71
|
+
- lib/paramsync/cli/encrypt_command.rb
|
72
|
+
- lib/paramsync/cli/pull_command.rb
|
73
|
+
- lib/paramsync/cli/push_command.rb
|
74
|
+
- lib/paramsync/cli/targets_command.rb
|
75
|
+
- lib/paramsync/config.rb
|
76
|
+
- lib/paramsync/diff.rb
|
77
|
+
- lib/paramsync/sync_target.rb
|
78
|
+
- lib/paramsync/version.rb
|
79
|
+
- paramsync.gemspec
|
80
|
+
homepage: https://github.com/maths22/paramsync
|
81
|
+
licenses:
|
82
|
+
- CC0
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 2.4.0
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubygems_version: 3.1.4
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Simple filesystem-to-aws parameter store synchronization
|
103
|
+
test_files: []
|