my-ruby-deployer 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/my-ruby-deployer.rb +502 -0
  3. metadata +187 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 011acb5e8ea3b1f472cdc7762bbe8c28c9af0d71d0e794c400941878d58c2dc0
4
+ data.tar.gz: 2b804b60e3f7112fa281444b3209b5c4bbcdf42e09d6267ecc15a8e2a325f841
5
+ SHA512:
6
+ metadata.gz: 46f6485783a33b8907d82b6973075b2b75d0bb53874a28c28f7608e7cff689ab27a1da72263525e20399aa8330b7f4cea434c7ca7758762a3ba80334a6313aaa
7
+ data.tar.gz: 50643d4b9906e09f1ffd7a8a578ed8482e787464224b4d7d7089309fd5a85980fa9becca7362305a902cb9dc0432f6ed04e7c48abf9806ff6a48822aaa022992
@@ -0,0 +1,502 @@
1
+ require 'blackstack-nodes'
2
+ require 'pg'
3
+ require 'sequel'
4
+ require 'colorize'
5
+ require 'simple_cloud_logging'
6
+
7
+ module BlackStack
8
+ # Deployer is a library that can be used to deploy a cluster of nodes.
9
+ module Deployer
10
+ @@nodes = []
11
+ @@routines = []
12
+
13
+ # get the array of nodes assigned to the module
14
+ def self.nodes
15
+ @@nodes
16
+ end # def self.nodes
17
+
18
+ # get the array of routines assigned to the module
19
+ def self.routines
20
+ @@routines
21
+ end # def self.routines
22
+
23
+ # inherit BlackStack::Infrastructure::NodeModule, including features of deployer.
24
+ module NodeModule
25
+ attr_accessor :deployment_routine, :parameters
26
+
27
+ include BlackStack::Infrastructure::NodeModule
28
+
29
+ def self.eth0_ip(insterface)
30
+ ret = nil
31
+ a = `ip addr show dev #{insterface}`.scan(/inet [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)
32
+ if a.size > 0
33
+ ret = a.last.to_s.gsub(/inet /, '')
34
+ else
35
+ raise "Cannot get ip address of the interface #{insterface}"
36
+ end
37
+ ret
38
+ end
39
+
40
+ # get the IP address for an interface using the ip addr command.
41
+ # this is a helper method for installing cockroachdb nodes.
42
+ def eth0_ip()
43
+ ret = nil
44
+ a = self.ssh.exec!("ip addr show dev #{parameters[:laninterface]}").scan(/inet [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)
45
+ if a.size > 0
46
+ ret = a.last.to_s.gsub(/inet /, '')
47
+ else
48
+ raise "Cannot get ip address of the interface #{parameters[:laninterface]}"
49
+ end
50
+ ret
51
+ end
52
+
53
+ def self.descriptor_errors(h)
54
+ errors = BlackStack::Infrastructure::NodeModule.descriptor_errors(h)
55
+
56
+ # validate: does not exist any other element in @@nodes with the same value for the parameter h[:name]
57
+ errors << "The parameter h[:name] is not unique" if BlackStack::Deployer.nodes.select{|n| n.name == h[:name]}.length > 0
58
+
59
+ # validate: h[:deployment_routine] is not nil
60
+ errors << "The parameter h[:deployment_routine] is required" if h[:deployment_routine].nil?
61
+
62
+ # validate: h[:deployment_routine] is a string
63
+ # This value is no longer mandatory
64
+ #errors << "The parameter h[:deployment_routine] is not a string" unless h[:deployment_routine].is_a?(String)
65
+
66
+ # return list of errors
67
+ errors.uniq
68
+ end
69
+
70
+ def initialize(h, i_logger=nil)
71
+ self.parameters = h
72
+ errors = BlackStack::Deployer::NodeModule.descriptor_errors(h)
73
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
74
+ super(h, i_logger)
75
+ self.deployment_routine = h[:deployment_routine]
76
+ end # def self.create(h)
77
+
78
+ def to_hash
79
+ h = super
80
+ h[:deployment_routine] = self.deployment_routine
81
+ h
82
+ end # def to_hash
83
+
84
+ def deploy(routine_name=nil, l=nil)
85
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
86
+ s = routine_name || self.deployment_routine
87
+ BlackStack::Deployer::run_routine(self.name, s, l);
88
+ end
89
+ end # module NodeModule
90
+
91
+ # define attributes and methods of a deployer routine
92
+ module RoutineModule
93
+ attr_accessor :name, :commands
94
+
95
+ def self.descriptor_errors(h)
96
+ errors = []
97
+
98
+ # validate: the parameter h is a hash
99
+ errors << "The parameter h is not a hash" unless h.is_a?(Hash)
100
+
101
+ # validate: the paramerer h has a key :name
102
+ errors << "The parameter h does not have a key :name" unless h.has_key?(:name)
103
+
104
+ # validate: the paramerer h has a key :command
105
+ errors << "The parameter h does not have a key :commands" unless h.has_key?(:commands)
106
+
107
+ # validate: the parameter h[:name] is a string or a symbol
108
+ errors << "The parameter h[:name] is not a string" unless h[:name].is_a?(String)
109
+
110
+ # validate: the parameter h[:name] is not 'reboot' because it is a reserved name
111
+ errors << "The parameter h[:name] is a reserved name (#{h[:name].to_s})" if h[:name] == 'reboot'
112
+
113
+ # validate: the parameter h[:commands] is required
114
+ errors << "The parameter h[:commands] is required" if h[:commands].nil?
115
+
116
+ # validate: the parametrer h[:commands] is an array
117
+ errors << "The parameter h[:commands] is not an array" unless h[:commands].is_a?(Array)
118
+
119
+ # validate: the parameter h[:commands] has at least one element
120
+ errors << "The parameter h[:commands] does not have at least one element" unless h[:commands].size > 0
121
+
122
+ # validate: each element of the array h[:commands] is a hash
123
+ h[:commands].each do |c|
124
+ errors += BlackStack::Deployer::CommandModule.descriptor_errors(c)
125
+ end # h[:commands].each do |c|
126
+
127
+ errors.uniq
128
+ end # def self.descriptor_error(h)
129
+
130
+ def initialize(h)
131
+ errors = BlackStack::Deployer::RoutineModule.descriptor_errors(h)
132
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
133
+ self.name = h[:name]
134
+ self.commands = []
135
+ h[:commands].each do |c|
136
+ self.commands << BlackStack::Deployer::Command.new(c)
137
+ end
138
+ end
139
+
140
+ def to_hash
141
+ h = {}
142
+ h[:name] = self.name
143
+ h[:commands] = []
144
+ self.commands.each do |c|
145
+ h[:commands] << c.to_hash
146
+ end
147
+ h
148
+ end
149
+
150
+ def run(node, l=nil)
151
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
152
+ l.logs "Running routine #{self.name.blue} on node #{node.name.blue}... "
153
+ i = 0
154
+ self.commands.each do |c|
155
+ i += 1
156
+ l.logs "Command #{i.to_s.blue}... "
157
+ c.run(node, l)
158
+ l.logf 'done'.green
159
+ end
160
+ l.logf 'done'.green
161
+ end # def run(node)
162
+ end # module RoutineModule
163
+
164
+ # define attributes and methods of a routine's command
165
+ module CommandModule
166
+ attr_accessor :command
167
+
168
+ def self.descriptor_errors(c)
169
+ errors = []
170
+
171
+ # validate: h is a hash
172
+ errors << "The command descriptor is not a hash" unless c.is_a?(Hash)
173
+
174
+ # validate: the hash c has a key :command
175
+ errors << "The command descriptor does not have a key :command" unless c.has_key?(:command)
176
+
177
+ # validate: the value of c[:command] is a string or symbol
178
+ errors << "The value of c[:command] is not a string and is not a symbol" unless c[:command].is_a?(String) || c[:command].is_a?(Symbol)
179
+
180
+ # if the parameter h[:name] is a symbol
181
+ if c[:command].is_a?(Symbol)
182
+ if c[:command] == :reboot
183
+ # :reboot is a reserved word, so it is fine to call :reboot
184
+ else
185
+ # validate: existis a routine with a the value c[:command].to_s on its :name key
186
+ errors << "The routine with the name #{c[:command].to_s} does not exist" unless BlackStack::Deployer::routines.select { |r| r.name == c[:command].to_s }.size > 0
187
+ end
188
+ else
189
+ c[:command].strip.split("\n").each { |l|
190
+ l.strip!
191
+ }
192
+ end
193
+
194
+ #
195
+ errors.uniq
196
+ end # def self.descriptor_error(h)
197
+
198
+ def initialize(h)
199
+ errors = BlackStack::Deployer::CommandModule.descriptor_errors(h)
200
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
201
+ self.command = h[:command]
202
+ end # def initialize(h)
203
+
204
+ def to_hash
205
+ h = {}
206
+ h[:command] = self.command
207
+ h
208
+ end # def to_hash
209
+
210
+ # running pre-defined commands: :root
211
+ # calling to other routines: :'change-name'
212
+ # calling
213
+ def run(n, l=nil)
214
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
215
+
216
+ # if self.command is a symbol
217
+ if self.command.is_a?(Symbol)
218
+
219
+ # if self.command is equel than :reboot
220
+ if self.command == :reboot
221
+ # call the n reboot method
222
+ n.reboot
223
+ else
224
+ # look for a routine with this name
225
+ r = BlackStack::Deployer.routines.select { |r| r.name == self.command.to_s }.first
226
+ if !r.nil?
227
+ r.run(n)
228
+ else
229
+ raise "The routine #{self.command.to_s} does not exist"
230
+ end
231
+ end
232
+
233
+ # if self.command is a string
234
+ elsif self.command.is_a?(String)
235
+ s = self.command.dup
236
+
237
+ l.logs "Replacing merge-tags... "
238
+ s.scan(/%[a-zA-Z0-9\_]+%/).uniq.each do |p|
239
+ l.logs "Replacing #{p.blue}... "
240
+ if p == '%timestamp%' # reserved parameter
241
+ # TODO: move this to a timestamp function on blackstack-core
242
+ s.gsub!(p, Time.now.to_s.gsub(/\D/, ''))
243
+ else
244
+ if n.parameters.has_key?(p.gsub(/%/, '').to_sym)
245
+ s.gsub!(p, n.parameters[p.gsub(/%/, '').to_sym].to_s)
246
+ else
247
+ raise "The parameter #{p} does not exist in the node descriptor #{n.parameters.to_s}"
248
+ end
249
+ end
250
+ l.logf 'done'.green
251
+ end
252
+ l.logf 'done'.green
253
+
254
+ l.logs "Running command... "
255
+ n.ssh.exec!(s)
256
+ l.logf 'done'.green
257
+ end # elsif code.is_a?(String)
258
+ end # def run(n)
259
+ end # module CommandModule
260
+
261
+ # TODO: declare these classes (stub and skeleton) using blackstack-rpc
262
+ #
263
+ # Stub Classes
264
+ # These classes represents a node, without using connection to the database.
265
+ # Use this class at the client side.
266
+ class Node
267
+ include BlackStack::Deployer::NodeModule
268
+ end # class Node
269
+
270
+ class Command
271
+ include BlackStack::Deployer::CommandModule
272
+ end # class Command
273
+
274
+ class Routine
275
+ include BlackStack::Deployer::RoutineModule
276
+ end # class Routine
277
+
278
+ # add a node to the list of nodes.
279
+ def self.add_node(h)
280
+ errors = BlackStack::Deployer::NodeModule.descriptor_errors(h)
281
+ raise errors.join(".\n") unless errors.empty?
282
+ @@nodes << BlackStack::Deployer::Node.new(h)
283
+ end # def
284
+
285
+ # add an array of nodes to the list of nodes.
286
+ def self.add_nodes(a)
287
+ # validate: the parameter a is an array
288
+ raise "The parameter a is not an array" unless a.is_a?(Array)
289
+ a.each { |h| BlackStack::Deployer.add_node(h) }
290
+ end # def
291
+
292
+ # remove all exisiting nodes in he list of nodes.
293
+ # then, add the nodes in the parameter a to the list of nodes.
294
+ def self.set_nodes(a)
295
+ @@nodes.clear
296
+ BlackStack::Deployer.add_nodes(a)
297
+ end # def
298
+
299
+ # add a routine to the list of routines.
300
+ def self.add_routine(h)
301
+ errors = BlackStack::Deployer::RoutineModule.descriptor_errors(h)
302
+ raise errors.join(".\n") unless errors.empty?
303
+ @@routines << BlackStack::Deployer::Routine.new(h)
304
+ end # def
305
+
306
+ # add an array of routines to the list of routines.
307
+ def self.add_routines(a)
308
+ # validate: the parameter a is an array
309
+ raise "The parameter a is not an array" unless a.is_a?(Array)
310
+ a.each { |h| BlackStack::Deployer.add_routine(h) }
311
+ end # def
312
+
313
+ # remove all exisiting routines in he list of routines.
314
+ # then, add the routines in the parameter a to the list of routines.
315
+ def self.set_routines(a)
316
+ @@routines.clear
317
+ BlackStack::Deployer.add_routines(a)
318
+ end # def
319
+
320
+ # running a routine on a node
321
+ def self.run_routine(node_name, routine_name, l=nil)
322
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
323
+ errors = []
324
+
325
+ # find the node with the value node_name in the key :name
326
+ n = @@nodes.select { |n| n.name == node_name }.first
327
+
328
+ # find the routine with the value routine_name in the key :name
329
+ r = @@routines.select { |r| r.name == routine_name }.first
330
+
331
+ # validate: the node n exists
332
+ errors << "Node #{node_name} not found" unless n
333
+
334
+ # validate: the routine r exists
335
+ errors << "Routine #{routine_name} not found" unless r
336
+
337
+ # raise exception if any error has been found
338
+ raise "The routine #{routine_name} cannot be run on the node #{node_name}: #{errors.uniq.join(".\n")}" if errors.length > 0
339
+
340
+ # connect the node
341
+ l.logs "Connecting to node #{n.name}... "
342
+ n.connect
343
+ l.done
344
+
345
+ # run the routine
346
+ l.logs "Running routine #{r.name}... "
347
+ r.run(n, l)
348
+ l.done
349
+
350
+ # disconnect the node
351
+ l.logs "Disconnecting from node #{n.name}... "
352
+ n.disconnect
353
+ l.done
354
+ end # def self.run_routine
355
+
356
+ module DB
357
+ LOCKFILE = './blackstack-deployer.lock'
358
+ @@checkpoint = nil
359
+ @@superhuser = nil
360
+ @@ndb = nil
361
+ @@folder = nil
362
+
363
+ def self.set_checkpoint(s)
364
+ @@checkpoint = s
365
+ end
366
+
367
+ def self.checkpoint
368
+ @@checkpoint
369
+ end
370
+
371
+ def self.save_checkpoint(lockfilename=BlackStack::Deployer::DB::LOCKFILE)
372
+ File.new(lockfilename, "w").write(@@checkpoint)
373
+ end
374
+
375
+ def self.load_checkpoint(lockfilename=BlackStack::Deployer::DB::LOCKFILE)
376
+ if File.exists?(lockfilename)
377
+ @@checkpoint = File.new(lockfilename, "r").read
378
+ else
379
+ @@checkpoint = nil
380
+ end
381
+ @@checkpoint
382
+ end
383
+
384
+ def self.connect(s)
385
+ @@db = Sequel::connect(s)
386
+ end # def
387
+
388
+ def self.set_folder(s)
389
+ @@folder = s
390
+ end # def
391
+
392
+
393
+ # Return `true` if the name of the file matches with `/\.transactions\./`, and it doesn't match with `/\.sentences\./`, and the matches with `/\.transactions\./` are no more than one.
394
+ # Otherwise, return `false`.
395
+ # This method should not be called directly by user code.
396
+ def self.is_transactions_file?(filename)
397
+ filename =~ /\.transactions\./ && filename !~ /\.sentences\./ && filename.scan(/\.transactions\./).size == 1
398
+ end
399
+
400
+ # Return `true` if the name of the file matches with `/\.sentences\./`, and it doesn't match with `/\.transactions\./`, and the matches with `/\.sentences\./` are no more than one.
401
+ # Otherwise({, return `false`.
402
+ # This method should not be called directly by user code.
403
+ def self.is_sentences_file?(filename)
404
+ filename =~ /\.sentences\./ && filename !~ /\.transactions\./ && filename.scan(/\.sentences\./).size == 1
405
+ end
406
+
407
+ # Method to process an `.sql` file with transactions code, separated by `BEGIN;` and `COMMIT;` statements.
408
+ # Reference: https://stackoverflow.com/questions/64066344/import-large-sql-files-with-ruby-sequel-gem
409
+ # This method is called by `BlackStack::Deployer::db_execute_file` if the filename matches with `/\.tsql\./`.
410
+ # This method should not be called directly by user code.
411
+ def self.execute_transactions(sql)
412
+ # TODO: Code Me!
413
+ end # def db_execute_tsql
414
+
415
+ # Method to process an `.sql` file with one sql sentence by line.
416
+ # Reference: https://stackoverflow.com/questions/64066344/import-large-sql-files-with-ruby-sequel-gem
417
+ # This method is called by `BlackStack::Deployer::db_execute_file` if the filename matches with `/\.sentences\./`.
418
+ # This method should not be called directly by user code.
419
+ def self.execute_sentences(sql, chunk_size=200, l=nil)
420
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
421
+
422
+ # Fix issue: Ruby `split': invalid byte sequence in UTF-8 (ArgumentError)
423
+ # Reference: https://stackoverflow.com/questions/11065962/ruby-split-invalid-byte-sequence-in-utf-8-argumenterror
424
+ #
425
+ # Fix issue: `PG::SyntaxError: ERROR: at or near "��truncate": syntax error`
426
+ #
427
+ sql.encode!('UTF-8', :invalid => :replace, :replace => '')
428
+
429
+ # Remove null bytes to avoid error: `String contains null byte`
430
+ # Reference: https://stackoverflow.com/questions/29320369/coping-with-string-contains-null-byte-sent-from-users
431
+ sql.gsub!("\u0000", "")
432
+
433
+ # Get the array of sentences
434
+ l.logs "Splitting the sql sentences... "
435
+ sentences = sql.split(/;/i)
436
+ l.logf "done (#{sentences.size})"
437
+
438
+ # Chunk the array into parts of chunk_size elements
439
+ # Reference: https://stackoverflow.com/questions/2699584/how-to-split-chunk-a-ruby-array-into-parts-of-x-elements
440
+ l.logs "Bunlding the array of sentences into chunks of #{chunk_size} each... "
441
+ chunks = sentences.each_slice(chunk_size).to_a
442
+ l.logf "done (#{chunks.size})"
443
+
444
+ chunk_number = -1
445
+ chunks.each { |chunk|
446
+ chunk_number += 1
447
+ statement = chunk.join(";\n").to_s.strip
448
+ l.logs "lines #{chunk_size*chunk_number+1} to #{chunk_size*chunk_number+chunk.size} of #{sentences.size}... "
449
+ begin
450
+ @@db.execute(statement) #if statement.to_s.strip.size > 0
451
+ l.done
452
+ rescue => e
453
+ l.logf e.to_s
454
+ raise "Error executing statement: #{statement}\n#{e.message}"
455
+ end
456
+ }
457
+ l.done
458
+ end # def db_execute_sql_sentences_file
459
+
460
+ # Run a series of `.sql` files with updates to the database.
461
+ #
462
+ def self.deploy(save_checkpoints=false, lockfilename=BlackStack::Deployer::DB::LOCKFILE, l=nil)
463
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
464
+ # get list of `.sql` files in the directory `sql_path`, with a name higher than `last_filename`, sorted by name.
465
+ Dir.entries(@@folder).select {
466
+ |filename| filename =~ /\.sql$/ && filename > @@checkpoint.to_s
467
+ }.uniq.sort.each { |filename|
468
+ fullfilename = "#{@@folder}/#{filename}"
469
+
470
+ l.logs "#{fullfilename}... "
471
+ BlackStack::Deployer::DB::execute_sentences( File.open(fullfilename).read )
472
+ l.done
473
+
474
+ l.logs "Updating checkpoint... "
475
+ BlackStack::Deployer::DB::set_checkpoint filename
476
+ l.done
477
+
478
+ l.logs 'Saving checkpoint... '
479
+ if save_checkpoints
480
+ BlackStack::Deployer::DB::save_checkpoint(lockfilename)
481
+ l.done
482
+ else
483
+ l.logf 'disabled'
484
+ end
485
+ }
486
+ end # def
487
+ end # module DB
488
+
489
+ # deploying all db-updates and run all routines on all nodes
490
+ def self.deploy(routine_name=nil, l=nil)
491
+ l = BlackStack::DummyLogger.new(nil) if l.nil?
492
+
493
+ @@nodes.each { |n|
494
+ l.logs "Node #{n.name}... "
495
+ n.deploy(routine_name, l)
496
+ l.done
497
+ }
498
+ end # def
499
+
500
+ end # module Deployer
501
+
502
+ end # module BlackStack
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: my-ruby-deployer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: blackstack-nodes
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.12
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.12
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.12
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.12
33
+ - !ruby/object:Gem::Dependency
34
+ name: pg
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.3.5
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.3.5
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 1.3.5
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.3.5
53
+ - !ruby/object:Gem::Dependency
54
+ name: sequel
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 5.56.0
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 5.56.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 5.56.0
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 5.56.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: websocket
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 1.2.8
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.8
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.2.8
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.2.8
93
+ - !ruby/object:Gem::Dependency
94
+ name: json
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 2.3.0
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 2.3.0
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 2.3.0
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 2.3.0
113
+ - !ruby/object:Gem::Dependency
114
+ name: colorize
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 0.8.1
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 0.8.1
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 0.8.1
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.8.1
133
+ - !ruby/object:Gem::Dependency
134
+ name: simple_cloud_logging
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 1.2.2
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 1.2.2
143
+ type: :runtime
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: 1.2.2
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 1.2.2
153
+ description: "Deployer automates what you already know how to do manually, but in
154
+ a repeatable, scalable fashion. There is no magic here!\nDeployer dutifully connects
155
+ to your server(s) via SSH and executes the steps necessary to deploy your project.
156
+ \nYou can define those steps yourself, or by using pre-built task libraries provided
157
+ by the Deployer community.\n "
158
+ email: leandro.sardi@expandedventure.com
159
+ executables: []
160
+ extensions: []
161
+ extra_rdoc_files: []
162
+ files:
163
+ - lib/my-ruby-deployer.rb
164
+ homepage: https://rubygems.org/gems/my-ruby-deployer
165
+ licenses:
166
+ - MIT
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubygems_version: 3.3.7
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Deployer is a deployment automation tool built on Ruby and SSH.
187
+ test_files: []