opswalrus 1.0.39 → 1.0.40
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/Gemfile.lock +1 -3
- data/lib/opswalrus/app.rb +31 -24
- data/lib/opswalrus/bootstrap.sh +2 -1
- data/lib/opswalrus/cli.rb +11 -18
- data/lib/opswalrus/host.rb +59 -32
- data/lib/opswalrus/hosts_file.rb +3 -6
- data/lib/opswalrus/interaction_handlers.rb +0 -3
- data/lib/opswalrus/local_non_blocking_backend.rb +2 -2
- data/lib/opswalrus/local_pty_backend.rb +1 -1
- data/lib/opswalrus/operation_runner.rb +3 -5
- data/lib/opswalrus/ops_file.rb +2 -3
- data/lib/opswalrus/ops_file_script.rb +1 -11
- data/lib/opswalrus/ops_file_script_dsl.rb +67 -43
- data/lib/opswalrus/runtime_environment.rb +12 -19
- data/lib/opswalrus/sshkit_ext.rb +10 -4
- data/lib/opswalrus/version.rb +1 -1
- data/lib/opswalrus/walrus_lang.rb +0 -1
- data/opswalrus.gemspec +0 -1
- data/vms/web-ubuntu/Vagrantfile +13 -7
- metadata +2 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43969fb5bd89993b9f4fb4c127d8a63f7d074d79d08104609b3a43d4df3f6457
|
|
4
|
+
data.tar.gz: beca6e40dcabbe38815ef6ac2fe6581895ba8ea2dcb27ab314ad22d5ff2d47cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be45244077503ac67da072c8d41cad8800553cb9dc243d49904de29588c20a93317543b3d2d2d97a8c7a74ca0a1abc6c0d2fa5d4187b1fe34d863e1e3e558218
|
|
7
|
+
data.tar.gz: 3cd42b3c126aef7dcec443c7f14fe9ee308da1d153148430b2413fe4166c4a214c43b0e368b485a4b3dd6cddf9c3047f778e55dbd4b99dc79697d75eaf910c72
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
opswalrus (1.0.
|
|
5
|
-
amazing_print (~> 1.5)
|
|
4
|
+
opswalrus (1.0.40)
|
|
6
5
|
bcrypt_pbkdf (~> 1.1)
|
|
7
6
|
binding_of_caller (~> 1.0)
|
|
8
7
|
citrus (~> 3.0)
|
|
@@ -20,7 +19,6 @@ GEM
|
|
|
20
19
|
specs:
|
|
21
20
|
addressable (2.8.5)
|
|
22
21
|
public_suffix (>= 2.0.2, < 6.0)
|
|
23
|
-
amazing_print (1.5.0)
|
|
24
22
|
bcrypt_pbkdf (1.1.0)
|
|
25
23
|
binding_of_caller (1.0.0)
|
|
26
24
|
debug_inspector (>= 0.0.1)
|
data/lib/opswalrus/app.rb
CHANGED
|
@@ -49,7 +49,6 @@ module OpsWalrus
|
|
|
49
49
|
# @logger.debug Style.yellow("debug"), foo: "bar", baz: {qux: "quux"}
|
|
50
50
|
# @logger.trace Style.yellow("trace"), foo: "bar", baz: {qux: "quux"}
|
|
51
51
|
|
|
52
|
-
@verbose = false
|
|
53
52
|
@sudo_user = nil
|
|
54
53
|
@sudo_password = nil
|
|
55
54
|
@identity_file_paths = []
|
|
@@ -124,38 +123,53 @@ module OpsWalrus
|
|
|
124
123
|
@logger.level = log_level
|
|
125
124
|
end
|
|
126
125
|
|
|
127
|
-
def verbose?
|
|
128
|
-
[:info, :debug, :trace].include? @logger.level
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def debug?
|
|
132
|
-
[:debug, :trace].include? @logger.level
|
|
133
|
-
end
|
|
134
|
-
|
|
135
126
|
def fatal(*args)
|
|
136
127
|
@logger.fatal(*args)
|
|
137
128
|
end
|
|
129
|
+
def fatal?
|
|
130
|
+
@logger.fatal?
|
|
131
|
+
end
|
|
138
132
|
|
|
139
133
|
def error(*args)
|
|
140
134
|
@logger.error(*args)
|
|
141
135
|
end
|
|
136
|
+
def error?
|
|
137
|
+
@logger.error?
|
|
138
|
+
end
|
|
142
139
|
|
|
143
140
|
def warn(*args)
|
|
144
141
|
@logger.warn(*args)
|
|
145
142
|
end
|
|
143
|
+
alias_method :important, :warn # warn means important
|
|
144
|
+
def warn?
|
|
145
|
+
@logger.warn?
|
|
146
|
+
end
|
|
146
147
|
|
|
147
|
-
def
|
|
148
|
+
def info(*args)
|
|
148
149
|
@logger.info(*args)
|
|
149
150
|
end
|
|
150
|
-
alias_method :
|
|
151
|
+
alias_method :log, :info
|
|
152
|
+
def info?
|
|
153
|
+
@logger.info?
|
|
154
|
+
end
|
|
151
155
|
|
|
152
156
|
def debug(*args)
|
|
153
157
|
@logger.debug(*args)
|
|
154
158
|
end
|
|
159
|
+
def debug?
|
|
160
|
+
@logger.debug?
|
|
161
|
+
end
|
|
155
162
|
|
|
156
163
|
def trace(*args)
|
|
157
164
|
@logger.trace(*args)
|
|
158
165
|
end
|
|
166
|
+
def trace?
|
|
167
|
+
@logger.trace?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def verbose?
|
|
171
|
+
info? || debug? || trace?
|
|
172
|
+
end
|
|
159
173
|
|
|
160
174
|
def set_pwd(pwd)
|
|
161
175
|
@pwd = pwd.to_pathname
|
|
@@ -181,7 +195,6 @@ module OpsWalrus
|
|
|
181
195
|
def prompt_sudo_password
|
|
182
196
|
password = IO::console.getpass(LOCAL_SUDO_PASSWORD_PROMPT)
|
|
183
197
|
set_sudo_password(password)
|
|
184
|
-
# puts "sudo password = |#{password}|"
|
|
185
198
|
nil
|
|
186
199
|
end
|
|
187
200
|
|
|
@@ -223,25 +236,19 @@ module OpsWalrus
|
|
|
223
236
|
|
|
224
237
|
ops_file = load_entry_point_ops_file(ops_file_path, tmp_bundle_root_dir)
|
|
225
238
|
|
|
226
|
-
|
|
227
|
-
puts "Running: #{ops_file.ops_file_path}"
|
|
228
|
-
end
|
|
239
|
+
debug "Running: #{ops_file.ops_file_path}"
|
|
229
240
|
|
|
230
241
|
op = OperationRunner.new(self, ops_file)
|
|
231
242
|
result = op.run(operation_kv_args, params_json_hash: @params)
|
|
232
243
|
exit_status = result.exit_status
|
|
233
244
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
puts exit_status
|
|
245
|
+
debug "Op exit_status"
|
|
246
|
+
debug exit_status
|
|
237
247
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
end
|
|
248
|
+
debug "Op output"
|
|
249
|
+
debug JSON.pretty_generate(result.value)
|
|
241
250
|
|
|
242
|
-
|
|
243
|
-
puts JSON.pretty_generate(result.value)
|
|
244
|
-
end
|
|
251
|
+
puts JSON.pretty_generate(result.value)
|
|
245
252
|
|
|
246
253
|
exit_status
|
|
247
254
|
ensure
|
data/lib/opswalrus/bootstrap.sh
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
|
|
3
3
|
export PATH="$HOME/.local/share/rtx/bin:$PATH" # this is key for activating rtx without running `eval "$($RTX activate bash)"`
|
|
4
|
+
# eval "$(rtx activate bash)"
|
|
4
5
|
RTX="$HOME/.local/share/rtx/bin/rtx"
|
|
5
6
|
rtx_init() { eval "$($RTX activate bash)"; }
|
|
6
7
|
# RTX_RUBY="$HOME/.local/share/rtx/bin/rtx x ruby -- ruby"
|
|
@@ -27,7 +28,7 @@ if [ -x $RTX ]; then
|
|
|
27
28
|
# make sure the latest opswalrus gem is installed
|
|
28
29
|
# todo: figure out how to install this differently, so that test versions will work
|
|
29
30
|
# gem install opswalrus
|
|
30
|
-
$GEM_CMD install opswalrus
|
|
31
|
+
# $GEM_CMD install opswalrus
|
|
31
32
|
$RTX reshim
|
|
32
33
|
|
|
33
34
|
exit 0
|
data/lib/opswalrus/cli.rb
CHANGED
|
@@ -22,19 +22,17 @@ module OpsWalrus
|
|
|
22
22
|
on_error do |exception|
|
|
23
23
|
next(false) if exception.is_a? GLI::CustomExit
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
$app.fatal "catchall exception handler:"
|
|
26
|
+
$app.fatal exception.message
|
|
27
|
+
$app.fatal exception.backtrace.join("\n")
|
|
28
28
|
false # disable built-in exception handling
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
program_desc 'ops is an operation runner'
|
|
32
32
|
|
|
33
|
-
desc
|
|
34
|
-
switch
|
|
35
|
-
|
|
36
|
-
desc 'Turn on debug mode'
|
|
37
|
-
switch [:d, :debug]
|
|
33
|
+
switch [:v, :verbose], desc: "Verbose output"
|
|
34
|
+
switch :debug, desc: "Debug output"
|
|
35
|
+
switch :trace, desc: "Trace output"
|
|
38
36
|
|
|
39
37
|
switch :noop, desc: "Perform a dry run"
|
|
40
38
|
switch :dryrun, desc: "Perform a dry run"
|
|
@@ -63,8 +61,7 @@ module OpsWalrus
|
|
|
63
61
|
hosts = global_options[:hosts]
|
|
64
62
|
tags = global_options[:tags]
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
$app.set_log_level(log_level)
|
|
64
|
+
$app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
|
|
68
65
|
|
|
69
66
|
$app.report_inventory(hosts, tags: tags)
|
|
70
67
|
end
|
|
@@ -131,8 +128,7 @@ module OpsWalrus
|
|
|
131
128
|
c.switch :dry_run, desc: "Perform a dry run"
|
|
132
129
|
|
|
133
130
|
c.action do |global_options, options, args|
|
|
134
|
-
|
|
135
|
-
$app.set_log_level(log_level)
|
|
131
|
+
$app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
|
|
136
132
|
|
|
137
133
|
hosts = global_options[:hosts]
|
|
138
134
|
tags = global_options[:tags]
|
|
@@ -168,8 +164,7 @@ module OpsWalrus
|
|
|
168
164
|
c.switch :dry_run, desc: "Perform a dry run"
|
|
169
165
|
|
|
170
166
|
c.action do |global_options, options, args|
|
|
171
|
-
|
|
172
|
-
$app.set_log_level(log_level)
|
|
167
|
+
$app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
|
|
173
168
|
|
|
174
169
|
hosts = global_options[:hosts]
|
|
175
170
|
$app.set_inventory_hosts(hosts)
|
|
@@ -213,8 +208,7 @@ module OpsWalrus
|
|
|
213
208
|
long_desc 'Download and bundle the latest versions of dependencies for the current package'
|
|
214
209
|
c.command :update do |update|
|
|
215
210
|
update.action do |global_options, options, args|
|
|
216
|
-
|
|
217
|
-
$app.set_log_level(log_level)
|
|
211
|
+
$app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
|
|
218
212
|
|
|
219
213
|
$app.bundle_update
|
|
220
214
|
end
|
|
@@ -234,8 +228,7 @@ module OpsWalrus
|
|
|
234
228
|
unzip.flag [:o, :output], desc: "Specify the output directory"
|
|
235
229
|
|
|
236
230
|
unzip.action do |global_options, options, args|
|
|
237
|
-
|
|
238
|
-
$app.set_log_level(log_level)
|
|
231
|
+
$app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
|
|
239
232
|
|
|
240
233
|
output_dir = options[:output]
|
|
241
234
|
zip_file_path = args.first
|
data/lib/opswalrus/host.rb
CHANGED
|
@@ -51,7 +51,6 @@ module OpsWalrus
|
|
|
51
51
|
sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
|
|
52
52
|
sibling_symbol_table.each do |symbol_name|
|
|
53
53
|
unless methods_defined.include? symbol_name
|
|
54
|
-
# puts "2. defining: #{symbol_name}(...)"
|
|
55
54
|
klass.define_method(symbol_name) do |*args, **kwargs, &block|
|
|
56
55
|
App.instance.trace "resolving implicit import: #{symbol_name}"
|
|
57
56
|
namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(ops_file, symbol_name)
|
|
@@ -144,7 +143,7 @@ module OpsWalrus
|
|
|
144
143
|
end
|
|
145
144
|
|
|
146
145
|
# returns the tuple: [stdout, stderr, exit_status]
|
|
147
|
-
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
|
|
146
|
+
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil, log_level: nil)
|
|
148
147
|
# description = nil
|
|
149
148
|
|
|
150
149
|
return ["", "", 0] if !desc_or_cmd && !cmd && !block # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
|
|
@@ -170,57 +169,83 @@ module OpsWalrus
|
|
|
170
169
|
|
|
171
170
|
#cmd = Shellwords.escape(cmd)
|
|
172
171
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
172
|
+
cmd_id = Random.uuid.split('-').first
|
|
173
|
+
# if App.instance.report_mode?
|
|
174
|
+
puts Style.green("*" * 80)
|
|
175
|
+
print Style.blue(host)
|
|
176
|
+
print " (#{Style.blue(self.alias)})" if self.alias
|
|
177
|
+
print " | #{Style.magenta(description)}" if description
|
|
178
|
+
puts
|
|
179
|
+
print Style.yellow(cmd_id)
|
|
180
|
+
print Style.green.bold(" > ")
|
|
181
|
+
puts Style.yellow(cmd)
|
|
182
|
+
|
|
183
|
+
# puts Style.green("*" * 80)
|
|
184
|
+
# if self.alias
|
|
185
|
+
# print "[#{Style.blue(self.alias)} | #{Style.blue(host)}] "
|
|
186
|
+
# else
|
|
187
|
+
# print "[#{Style.blue(host)}] "
|
|
188
|
+
# end
|
|
189
|
+
# print "#{description}: " if description
|
|
190
|
+
# puts Style.yellow("[#{cmd_id}] #{cmd}")
|
|
191
|
+
# end
|
|
183
192
|
|
|
184
193
|
return unless cmd && !cmd.strip.empty?
|
|
185
194
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# puts "sudo_password: #{sudo_password}"
|
|
189
|
-
|
|
190
|
-
if App.instance.dry_run?
|
|
195
|
+
t1 = Time.now
|
|
196
|
+
out, err, exit_status = if App.instance.dry_run?
|
|
191
197
|
["", "", 0]
|
|
192
198
|
else
|
|
193
199
|
sshkit_cmd = execute_cmd(cmd, input: input)
|
|
194
200
|
[sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
|
|
195
201
|
end
|
|
196
|
-
|
|
202
|
+
t2 = Time.now
|
|
203
|
+
seconds = t2 - t1
|
|
204
|
+
|
|
205
|
+
if App.instance.info? || log_level == :info
|
|
206
|
+
puts Style.cyan(out)
|
|
207
|
+
puts Style.red(err)
|
|
208
|
+
elsif App.instance.debug? || log_level == :debug
|
|
209
|
+
puts Style.cyan(out)
|
|
210
|
+
puts Style.red(err)
|
|
211
|
+
elsif App.instance.trace? || log_level == :trace
|
|
212
|
+
puts Style.cyan(out)
|
|
213
|
+
puts Style.red(err)
|
|
214
|
+
end
|
|
215
|
+
print Style.yellow(cmd_id)
|
|
216
|
+
print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
|
217
|
+
if exit_status == 0
|
|
218
|
+
puts Style.green("#{exit_status} (#{exit_status == 0 ? 'success' : 'failure'})")
|
|
219
|
+
else
|
|
220
|
+
puts Style.red("#{exit_status} (#{exit_status == 0 ? 'success' : 'failure'})")
|
|
221
|
+
end
|
|
197
222
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
# end
|
|
223
|
+
[out, err, exit_status]
|
|
224
|
+
end
|
|
201
225
|
|
|
202
226
|
# runs the specified ops command with the specified command arguments
|
|
203
|
-
def run_ops(ops_command, ops_command_options = nil, command_arguments, in_bundle_root_dir: true
|
|
204
|
-
# e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops bundle unzip tmpops.zip
|
|
205
|
-
# e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops run echo.ops args:foo args:bar
|
|
206
|
-
|
|
207
|
-
# cmd = "/home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops"
|
|
227
|
+
def run_ops(ops_command, ops_command_options = nil, command_arguments, in_bundle_root_dir: true)
|
|
208
228
|
local_hostname_for_remote_host = if self.alias
|
|
209
|
-
"#{
|
|
229
|
+
"#{host} (#{self.alias})"
|
|
210
230
|
else
|
|
211
231
|
host
|
|
212
232
|
end
|
|
213
233
|
|
|
214
|
-
# cmd = "OPSWALRUS_LOCAL_HOSTNAME='#{local_hostname_for_remote_host}'; /home/linuxbrew/.linuxbrew/bin/gem exec --conservative -g opswalrus ops"
|
|
215
234
|
# cmd = "OPS_GEM=\"#{OPS_GEM}\" OPSWALRUS_LOCAL_HOSTNAME='#{local_hostname_for_remote_host}'; $OPS_GEM exec --conservative -g opswalrus ops"
|
|
216
235
|
cmd = "OPSWALRUS_LOCAL_HOSTNAME='#{local_hostname_for_remote_host}' eval #{OPS_CMD}"
|
|
217
|
-
|
|
236
|
+
if App.instance.info?
|
|
237
|
+
cmd << " --verbose"
|
|
238
|
+
elsif App.instance.debug?
|
|
239
|
+
cmd << " --debug"
|
|
240
|
+
elsif App.instance.trace?
|
|
241
|
+
cmd << " --trace"
|
|
242
|
+
end
|
|
218
243
|
cmd << " #{ops_command.to_s}"
|
|
219
244
|
cmd << " #{ops_command_options.to_s}" if ops_command_options
|
|
220
245
|
cmd << " #{@tmp_bundle_root_dir}" if in_bundle_root_dir
|
|
221
246
|
cmd << " #{command_arguments}" unless command_arguments.empty?
|
|
222
247
|
|
|
223
|
-
shell!(cmd)
|
|
248
|
+
shell!(cmd, log_level: :info)
|
|
224
249
|
end
|
|
225
250
|
|
|
226
251
|
def desc(msg)
|
|
@@ -391,13 +416,15 @@ module OpsWalrus
|
|
|
391
416
|
|
|
392
417
|
def execute(*args, input: nil)
|
|
393
418
|
@runtime_env.handle_input(input, ssh_password) do |interaction_handler|
|
|
394
|
-
@sshkit_backend.capture(*args, interaction_handler: interaction_handler, verbosity:
|
|
419
|
+
# @sshkit_backend.capture(*args, interaction_handler: interaction_handler, verbosity: SSHKit.config.output_verbosity)
|
|
420
|
+
@sshkit_backend.capture(*args, interaction_handler: interaction_handler)
|
|
395
421
|
end
|
|
396
422
|
end
|
|
397
423
|
|
|
398
424
|
def execute_cmd(*args, input: nil)
|
|
399
425
|
@runtime_env.handle_input(input, ssh_password) do |interaction_handler|
|
|
400
|
-
@sshkit_backend.execute_cmd(*args, interaction_handler: interaction_handler, verbosity:
|
|
426
|
+
# @sshkit_backend.execute_cmd(*args, interaction_handler: interaction_handler, verbosity: SSHKit.config.output_verbosity)
|
|
427
|
+
@sshkit_backend.execute_cmd(*args, interaction_handler: interaction_handler)
|
|
401
428
|
end
|
|
402
429
|
end
|
|
403
430
|
|
data/lib/opswalrus/hosts_file.rb
CHANGED
|
@@ -168,20 +168,18 @@ module OpsWalrus
|
|
|
168
168
|
|
|
169
169
|
def decrypt(decrypted_file_path = nil)
|
|
170
170
|
decrypted_file_path ||= @hosts_file_path
|
|
171
|
-
|
|
171
|
+
App.instance.debug "Decrypting #{@hosts_file_path} -> #{decrypted_file_path}."
|
|
172
172
|
raise("Path to age identity not specified") if App.instance.identity_file_paths.empty?
|
|
173
173
|
decrypt_secrets!
|
|
174
174
|
File.write(decrypted_file_path, to_yaml)
|
|
175
|
-
# puts to_yaml
|
|
176
175
|
end
|
|
177
176
|
|
|
178
177
|
def encrypt(encrypted_file_path = nil)
|
|
179
178
|
encrypted_file_path ||= @hosts_file_path
|
|
180
|
-
|
|
179
|
+
App.instance.debug "Encrypting #{@hosts_file_path} -> #{encrypted_file_path}."
|
|
181
180
|
raise("Path to age identity not specified") if App.instance.identity_file_paths.empty?
|
|
182
181
|
encrypt_secrets!
|
|
183
182
|
File.write(encrypted_file_path, to_yaml)
|
|
184
|
-
# puts to_yaml
|
|
185
183
|
end
|
|
186
184
|
end
|
|
187
185
|
|
|
@@ -231,7 +229,7 @@ module OpsWalrus
|
|
|
231
229
|
when Array
|
|
232
230
|
ref.map {|audience_id_reference| dereference(audience_id_reference) }.flatten.compact.uniq
|
|
233
231
|
when Nil
|
|
234
|
-
|
|
232
|
+
App.instance.warn "ID #{audience_id_reference} does not appear in the list of known public key identifiers"
|
|
235
233
|
nil
|
|
236
234
|
else
|
|
237
235
|
raise "ID reference #{audience_id_reference} corresponds to an unknown type of public key or transitive ID reference: #{ref.inspect}"
|
|
@@ -372,7 +370,6 @@ module OpsWalrus
|
|
|
372
370
|
# coder.represent_scalar(nil, @public_key)
|
|
373
371
|
|
|
374
372
|
# coder.scalar = @public_key
|
|
375
|
-
# puts @ids.inspect
|
|
376
373
|
single_line_ids = @ids.join(", ")
|
|
377
374
|
if single_line_ids.size <= 80
|
|
378
375
|
coder['ids'] = single_line_ids
|
|
@@ -39,7 +39,7 @@ module SSHKit
|
|
|
39
39
|
handle_data_for_stdout(output, cmd, buffer, stdin, true)
|
|
40
40
|
stdout.close
|
|
41
41
|
rescue => e
|
|
42
|
-
|
|
42
|
+
App.instance.error "closing PTY due to unexpected error: #{e.message}"
|
|
43
43
|
handle_data_for_stdout(output, cmd, buffer, stdin, true)
|
|
44
44
|
stdout.close
|
|
45
45
|
# puts e.message
|
|
@@ -73,7 +73,7 @@ module SSHKit
|
|
|
73
73
|
handle_data_for_stderr(output, cmd, buffer, stdin, true)
|
|
74
74
|
stderr.close
|
|
75
75
|
rescue => e
|
|
76
|
-
|
|
76
|
+
App.instance.error "closing PTY due to unexpected error: #{e.message}"
|
|
77
77
|
handle_data_for_stderr(output, cmd, buffer, stdin, true)
|
|
78
78
|
stderr.close
|
|
79
79
|
# puts e.message
|
|
@@ -44,7 +44,7 @@ module SSHKit
|
|
|
44
44
|
handle_data_for_stdout(output, cmd, buffer, stdin, true)
|
|
45
45
|
stdout.close
|
|
46
46
|
rescue => e
|
|
47
|
-
|
|
47
|
+
App.instance.error "closing PTY due to unexpected error: #{e.message}"
|
|
48
48
|
handle_data_for_stdout(output, cmd, buffer, stdin, true)
|
|
49
49
|
stdout.close
|
|
50
50
|
# puts e.message
|
|
@@ -83,10 +83,8 @@ module OpsWalrus
|
|
|
83
83
|
def run(runtime_kv_args, params_json_hash: nil)
|
|
84
84
|
params_hash = build_params_hash(runtime_kv_args, params_json_hash: params_json_hash)
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
App.instance.trace @entry_point_ops_file.script
|
|
89
|
-
end
|
|
86
|
+
App.instance.trace "Script:"
|
|
87
|
+
App.instance.trace @entry_point_ops_file.script
|
|
90
88
|
|
|
91
89
|
result = begin
|
|
92
90
|
# update the bundle for the package
|
|
@@ -112,7 +110,7 @@ module OpsWalrus
|
|
|
112
110
|
Invocation::Error.new(e)
|
|
113
111
|
end
|
|
114
112
|
|
|
115
|
-
if
|
|
113
|
+
if result.failure?
|
|
116
114
|
App.instance.debug "Ops script error details:"
|
|
117
115
|
App.instance.debug "Error: #{result.value}"
|
|
118
116
|
App.instance.debug "Status code: #{result.exit_status}"
|
data/lib/opswalrus/ops_file.rb
CHANGED
|
@@ -47,8 +47,7 @@ module OpsWalrus
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def lazy_load_file
|
|
50
|
-
|
|
51
|
-
# puts caller
|
|
50
|
+
App.instance.trace "OpsFile#lazy_load_file for #{@ops_file_path}"
|
|
52
51
|
yaml, ruby_script = if @ops_file_path.exist?
|
|
53
52
|
parse(File.read(@ops_file_path))
|
|
54
53
|
end || ["", ""]
|
|
@@ -166,7 +165,7 @@ module OpsWalrus
|
|
|
166
165
|
package_uri = import_str
|
|
167
166
|
if Git.repo?(package_uri) # ops file has imported an ad-hoc git repo
|
|
168
167
|
destination_package_path = app.bundler.dynamic_package_path_for_git_package(package_uri)
|
|
169
|
-
|
|
168
|
+
App.instance.trace "DynamicPackageImportReference: #{local_name} -> #{destination_package_path}"
|
|
170
169
|
return DynamicPackageImportReference.new(local_name, DynamicPackageReference.new(local_name, package_uri, nil))
|
|
171
170
|
end
|
|
172
171
|
|
|
@@ -110,7 +110,7 @@ module OpsWalrus
|
|
|
110
110
|
sibling_symbol_table_names = Set.new
|
|
111
111
|
sibling_symbol_table_names |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
|
|
112
112
|
sibling_symbol_table_names |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
|
|
113
|
-
#
|
|
113
|
+
# App.instance.trace "sibling_symbol_table_names=#{sibling_symbol_table_names}"
|
|
114
114
|
App.instance.trace "methods_defined=#{methods_defined}"
|
|
115
115
|
sibling_symbol_table_names.each do |symbol_name|
|
|
116
116
|
unless methods_defined.include? symbol_name
|
|
@@ -134,8 +134,6 @@ module OpsWalrus
|
|
|
134
134
|
# - @params
|
|
135
135
|
# - #host_proxy_class
|
|
136
136
|
# - #backend
|
|
137
|
-
# - #debug?
|
|
138
|
-
# - #verbose?
|
|
139
137
|
# - all the dynamically defined methods in the subclass of Invocation
|
|
140
138
|
invoke_method_definition = <<~INVOKE_METHOD
|
|
141
139
|
def _invoke(runtime_env, hashlike_params)
|
|
@@ -167,14 +165,6 @@ module OpsWalrus
|
|
|
167
165
|
@runtime_env.pty
|
|
168
166
|
end
|
|
169
167
|
|
|
170
|
-
def debug?
|
|
171
|
-
@runtime_env.debug?
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def verbose?
|
|
175
|
-
@runtime_env.verbose?
|
|
176
|
-
end
|
|
177
|
-
|
|
178
168
|
def host_proxy_class
|
|
179
169
|
@ops_file.host_proxy_class
|
|
180
170
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'shellwords'
|
|
2
2
|
require 'stringio'
|
|
3
|
+
require 'random/formatter'
|
|
3
4
|
|
|
4
5
|
require 'sshkit'
|
|
5
6
|
require 'sshkit/dsl'
|
|
@@ -73,28 +74,28 @@ module OpsWalrus
|
|
|
73
74
|
|
|
74
75
|
retval
|
|
75
76
|
rescue SSHKit::Command::Failed => e
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
App.instance.error "[!] Command failed:"
|
|
78
|
+
App.instance.error e.message
|
|
78
79
|
rescue Net::SSH::ConnectionTimeout
|
|
79
|
-
|
|
80
|
+
App.instance.error "[!] The host '#{host}' not alive!"
|
|
80
81
|
rescue Net::SSH::Timeout
|
|
81
|
-
|
|
82
|
+
App.instance.error "[!] The host '#{host}' disconnected/timeouted unexpectedly!"
|
|
82
83
|
rescue Errno::ECONNREFUSED
|
|
83
|
-
|
|
84
|
+
App.instance.error "[!] Incorrect port #{port} for #{host}"
|
|
84
85
|
rescue Net::SSH::HostKeyMismatch => e
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
App.instance.error "[!] The host fingerprint does not match the last observed fingerprint for #{host}"
|
|
87
|
+
App.instance.error e.message
|
|
88
|
+
App.instance.error "You might try `ssh-keygen -f ~/.ssh/known_hosts -R \"#{host}\"`"
|
|
88
89
|
rescue Net::SSH::AuthenticationFailed
|
|
89
|
-
|
|
90
|
+
App.instance.error "Wrong Password: #{host} | #{user}:#{password}"
|
|
90
91
|
rescue Net::SSH::Authentication::DisallowedMethod
|
|
91
|
-
|
|
92
|
+
App.instance.error "[!] The host '#{host}' doesn't accept password authentication method."
|
|
92
93
|
rescue Errno::EHOSTUNREACH => e
|
|
93
|
-
|
|
94
|
+
App.instance.error "[!] The host '#{host}' is unreachable"
|
|
94
95
|
rescue => e
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
App.instance.error e.class
|
|
97
|
+
App.instance.error e.message
|
|
98
|
+
App.instance.error e.backtrace.join("\n")
|
|
98
99
|
ensure
|
|
99
100
|
host.clear_ssh_session
|
|
100
101
|
end
|
|
@@ -124,28 +125,28 @@ module OpsWalrus
|
|
|
124
125
|
|
|
125
126
|
retval
|
|
126
127
|
rescue SSHKit::Command::Failed => e
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
App.instance.error "[!] Command failed:"
|
|
129
|
+
App.instance.error e.message
|
|
129
130
|
rescue Net::SSH::ConnectionTimeout
|
|
130
|
-
|
|
131
|
+
App.instance.error "[!] The host '#{host}' not alive!"
|
|
131
132
|
rescue Net::SSH::Timeout
|
|
132
|
-
|
|
133
|
+
App.instance.error "[!] The host '#{host}' disconnected/timeouted unexpectedly!"
|
|
133
134
|
rescue Errno::ECONNREFUSED
|
|
134
|
-
|
|
135
|
+
App.instance.error "[!] Incorrect port #{port} for #{host}"
|
|
135
136
|
rescue Net::SSH::HostKeyMismatch => e
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
App.instance.error "[!] The host fingerprint does not match the last observed fingerprint for #{host}"
|
|
138
|
+
App.instance.error e.message
|
|
139
|
+
App.instance.error "You might try `ssh-keygen -f ~/.ssh/known_hosts -R \"#{host}\"`"
|
|
139
140
|
rescue Net::SSH::AuthenticationFailed
|
|
140
|
-
|
|
141
|
+
App.instance.error "Wrong Password: #{host} | #{user}:#{password}"
|
|
141
142
|
rescue Net::SSH::Authentication::DisallowedMethod
|
|
142
|
-
|
|
143
|
+
App.instance.error "[!] The host '#{host}' doesn't accept password authentication method."
|
|
143
144
|
rescue Errno::EHOSTUNREACH => e
|
|
144
|
-
|
|
145
|
+
App.instance.error "[!] The host '#{host}' is unreachable"
|
|
145
146
|
rescue => e
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
App.instance.error e.class
|
|
148
|
+
App.instance.error e.message
|
|
149
|
+
App.instance.error e.backtrace.join("\n")
|
|
149
150
|
ensure
|
|
150
151
|
host.clear_ssh_session
|
|
151
152
|
end
|
|
@@ -182,7 +183,6 @@ module OpsWalrus
|
|
|
182
183
|
package_reference = ops_file.package_file&.dependency(local_package_name)
|
|
183
184
|
raise Error, "Unknown package reference: #{local_package_name}" unless package_reference
|
|
184
185
|
import_reference = PackageDependencyReference.new(local_package_name, package_reference)
|
|
185
|
-
# puts "import: #{import_reference.inspect}"
|
|
186
186
|
namespace_or_ops_file = @runtime_env.resolve_import_reference(ops_file, import_reference)
|
|
187
187
|
raise SymbolResolutionError, "Import reference '#{import_reference.summary}' not in load path for #{ops_file.ops_file_path}" unless namespace_or_ops_file
|
|
188
188
|
invocation_context = LocalImportInvocationContext.new(@runtime_env, namespace_or_ops_file)
|
|
@@ -223,7 +223,7 @@ module OpsWalrus
|
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
# returns the tuple: [stdout, stderr, exit_status]
|
|
226
|
-
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
|
|
226
|
+
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil, log_level: nil)
|
|
227
227
|
# description = nil
|
|
228
228
|
|
|
229
229
|
return ["", "", 0] if !desc_or_cmd && !cmd && !block # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
|
|
@@ -250,31 +250,55 @@ module OpsWalrus
|
|
|
250
250
|
|
|
251
251
|
#cmd = Shellwords.escape(cmd)
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
cmd_id = Random.uuid.split('-').first
|
|
254
254
|
|
|
255
|
-
if App.instance.report_mode?
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
255
|
+
# if App.instance.report_mode?
|
|
256
|
+
puts Style.green("*" * 80)
|
|
257
|
+
print Style.blue(@runtime_env.local_hostname)
|
|
258
|
+
print " | #{Style.magenta(description)}" if description
|
|
259
|
+
puts
|
|
260
|
+
print Style.yellow(cmd_id)
|
|
261
|
+
print Style.green.bold(" > ")
|
|
262
|
+
puts Style.yellow(cmd)
|
|
263
|
+
# end
|
|
261
264
|
|
|
262
265
|
return unless cmd && !cmd.strip.empty?
|
|
263
266
|
|
|
264
|
-
|
|
267
|
+
t1 = Time.now
|
|
268
|
+
out, err, exit_status = if App.instance.dry_run?
|
|
265
269
|
["", "", 0]
|
|
266
270
|
else
|
|
267
271
|
sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
|
|
268
272
|
# self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
|
|
269
|
-
backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity:
|
|
273
|
+
# backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: SSHKit.config.output_verbosity)
|
|
274
|
+
backend.execute_cmd(cmd, interaction_handler: interaction_handler)
|
|
270
275
|
end
|
|
271
276
|
[sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
|
|
272
277
|
end
|
|
273
|
-
|
|
278
|
+
t2 = Time.now
|
|
279
|
+
seconds = t2 - t1
|
|
280
|
+
|
|
281
|
+
if App.instance.info? || log_level == :info
|
|
282
|
+
puts Style.cyan(out)
|
|
283
|
+
puts Style.red(err)
|
|
284
|
+
elsif App.instance.debug? || log_level == :debug
|
|
285
|
+
puts Style.cyan(out)
|
|
286
|
+
puts Style.red(err)
|
|
287
|
+
elsif App.instance.trace? || log_level == :trace
|
|
288
|
+
puts Style.cyan(out)
|
|
289
|
+
puts Style.red(err)
|
|
290
|
+
end
|
|
291
|
+
print Style.yellow(cmd_id)
|
|
292
|
+
print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
|
293
|
+
if exit_status == 0
|
|
294
|
+
puts Style.green("#{exit_status} (#{exit_status == 0 ? 'success' : 'failure'})")
|
|
295
|
+
else
|
|
296
|
+
puts Style.red("#{exit_status} (#{exit_status == 0 ? 'success' : 'failure'})")
|
|
297
|
+
end
|
|
298
|
+
|
|
274
299
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# end
|
|
300
|
+
[out, err, exit_status]
|
|
301
|
+
end
|
|
278
302
|
|
|
279
303
|
end
|
|
280
304
|
|
|
@@ -207,12 +207,12 @@ module OpsWalrus
|
|
|
207
207
|
def resolve_import_reference(origin_ops_file, import_reference)
|
|
208
208
|
resolved_namespace_or_ops_file = case import_reference
|
|
209
209
|
when PackageDependencyReference
|
|
210
|
-
#
|
|
210
|
+
# App.instance.trace "root namespace: #{@root_namespace.symbol_table}"
|
|
211
211
|
@root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package import_resolution_dirname (i.e. the local name)
|
|
212
212
|
when DynamicPackageImportReference
|
|
213
213
|
dynamic_package_reference = import_reference.package_reference
|
|
214
214
|
@dynamic_package_additions_memo[dynamic_package_reference] ||= begin
|
|
215
|
-
#
|
|
215
|
+
# App.instance.trace "Downloading dynamic package: #{dynamic_package_reference.inspect}"
|
|
216
216
|
App.instance.debug("Downloading dynamic package: #{dynamic_package_reference}")
|
|
217
217
|
dynamically_added_package_dir = @runtime_env.app.bundler.download_git_package(dynamic_package_reference.package_uri, dynamic_package_reference.version)
|
|
218
218
|
dynamically_add_new_package_dir(dynamically_added_package_dir)
|
|
@@ -267,15 +267,16 @@ module OpsWalrus
|
|
|
267
267
|
SSHKit.config.use_format :blackhole
|
|
268
268
|
SSHKit.config.output_verbosity = :info
|
|
269
269
|
|
|
270
|
-
if app.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
270
|
+
# if app.info?
|
|
271
|
+
# SSHKit.config.use_format :pretty
|
|
272
|
+
# SSHKit.config.output_verbosity = :info
|
|
273
|
+
# elsif app.debug?
|
|
274
|
+
# SSHKit.config.use_format :pretty
|
|
275
|
+
# SSHKit.config.output_verbosity = :debug
|
|
276
|
+
# elsif app.trace?
|
|
277
|
+
# SSHKit.config.use_format :pretty
|
|
278
|
+
# SSHKit.config.output_verbosity = :debug
|
|
279
|
+
# end
|
|
279
280
|
|
|
280
281
|
SSHKit::Backend::Netssh.configure do |ssh|
|
|
281
282
|
ssh.pty = true # necessary for interaction with sudo on the remote host
|
|
@@ -292,14 +293,6 @@ module OpsWalrus
|
|
|
292
293
|
SSHKit::Backend::Netssh.pool.idle_timeout = 1 # seconds
|
|
293
294
|
end
|
|
294
295
|
|
|
295
|
-
def debug?
|
|
296
|
-
@app.debug?
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def verbose?
|
|
300
|
-
@app.verbose?
|
|
301
|
-
end
|
|
302
|
-
|
|
303
296
|
def local_hostname
|
|
304
297
|
@app.local_hostname
|
|
305
298
|
end
|
data/lib/opswalrus/sshkit_ext.rb
CHANGED
|
@@ -9,10 +9,10 @@ require_relative 'local_pty_backend'
|
|
|
9
9
|
module SSHKit
|
|
10
10
|
module Backend
|
|
11
11
|
class Abstract
|
|
12
|
-
def execute(*args)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
12
|
+
# def execute(*args)
|
|
13
|
+
# options = { verbosity: :debug, raise_on_non_zero_exit: false }.merge(args.extract_options!)
|
|
14
|
+
# create_command_and_execute(args, options).success?
|
|
15
|
+
# end
|
|
16
16
|
|
|
17
17
|
def execute_cmd(*args)
|
|
18
18
|
options = { verbosity: :debug, strip: true, raise_on_non_zero_exit: false }.merge(args.extract_options!)
|
|
@@ -21,6 +21,12 @@ module SSHKit
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# module Formatter
|
|
25
|
+
# class Pretty < Abstract
|
|
26
|
+
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
|
|
24
30
|
module Runner
|
|
25
31
|
class Sequential < Abstract
|
|
26
32
|
def run_backend(host, &block)
|
data/lib/opswalrus/version.rb
CHANGED
|
@@ -99,7 +99,6 @@ end
|
|
|
99
99
|
# # m = TemplateLang.parse("abc; {{ 'foo' * bar }} def ")
|
|
100
100
|
# m = WalrusLang::Parser.parse("abc; {{ 'foo{{1+2}}' * bar }} def {{ 4 * 4 }}; def")
|
|
101
101
|
# # m = TemplateLang.parse("a{{b{{c}}d}}e{{f}}g{{h{{i{{j{{k{{l}}m{{n}}o}}p}}}}}}")
|
|
102
|
-
# # puts m.dump
|
|
103
102
|
# puts m.render(binding)
|
|
104
103
|
|
|
105
104
|
# puts("abc {{ 1 + 2 }} def".mustache)
|
data/opswalrus.gemspec
CHANGED
|
@@ -32,7 +32,6 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.require_paths = ["lib"]
|
|
33
33
|
|
|
34
34
|
# gem dependencies
|
|
35
|
-
spec.add_dependency "amazing_print", "~> 1.5"
|
|
36
35
|
spec.add_dependency "binding_of_caller", "~> 1.0"
|
|
37
36
|
spec.add_dependency "citrus", "~> 3.0"
|
|
38
37
|
spec.add_dependency "gli", "~> 2.21"
|
data/vms/web-ubuntu/Vagrantfile
CHANGED
|
@@ -20,13 +20,19 @@ Vagrant.configure("2") do |config|
|
|
|
20
20
|
# Enable provisioning with a shell script. Additional provisioners such as
|
|
21
21
|
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
|
|
22
22
|
# documentation for more information about their specific syntax and use.
|
|
23
|
-
config.vm.provision "shell"
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
config.vm.provision "shell" do |s|
|
|
24
|
+
ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_ops.pub").first.strip
|
|
25
|
+
s.inline = <<-SHELL
|
|
26
|
+
echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys
|
|
27
|
+
echo #{ssh_pub_key} >> /root/.ssh/authorized_keys
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
|
|
30
|
+
sudo systemctl restart sshd
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
# change vagrant user to require sudo password
|
|
33
|
+
sudo sed -i 's/vagrant ALL=(ALL) NOPASSWD:ALL/vagrant ALL=(ALL:ALL) ALL/g' /etc/sudoers.d/vagrant
|
|
34
|
+
|
|
35
|
+
# sudo sh -c 'echo root:foo | chpasswd'
|
|
36
|
+
SHELL
|
|
37
|
+
end
|
|
32
38
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: opswalrus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.40
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Ellis
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-09-
|
|
11
|
+
date: 2023-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: amazing_print
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.5'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.5'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: binding_of_caller
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|