my-ruby-deployer 1.3.1

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.
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: []