opswalrus 1.0.16 → 1.0.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,10 +13,18 @@ module OpsWalrus
13
13
  def method_missing(name, *args, **kwargs, &block)
14
14
  raise "Not implemented in base class"
15
15
  end
16
+
17
+ def _bang_method?(name)
18
+ name.to_s.end_with?("!")
19
+ end
20
+
21
+ def _non_bang_method(name)
22
+ name.to_s.sub(/!$/, '')
23
+ end
16
24
  end
17
25
 
18
26
  class RemoteImportInvocationContext < ImportInvocationContext
19
- def initialize(runtime_env, host_proxy, namespace_or_ops_file, is_invocation_a_call_to_package_in_bundle_dir = false)
27
+ def initialize(runtime_env, host_proxy, namespace_or_ops_file, is_invocation_a_call_to_package_in_bundle_dir = false, prompt_for_sudo_password: nil)
20
28
  @runtime_env = runtime_env
21
29
  @host_proxy = host_proxy
22
30
  @initial_namespace_or_ops_file = @namespace_or_ops_file = namespace_or_ops_file
@@ -24,12 +32,46 @@ module OpsWalrus
24
32
 
25
33
  initial_method_name = @namespace_or_ops_file.dirname.basename
26
34
  @method_chain = [initial_method_name]
35
+ @prompt_for_sudo_password = prompt_for_sudo_password
36
+ end
37
+
38
+ def method_missing(name, *args, **kwargs, &block)
39
+ _resolve_method_and_invoke(name, *args, **kwargs)
40
+ end
41
+
42
+ def _resolve_method_and_invoke(name, *args, **kwargs)
43
+ if _bang_method?(name) # foo! is an attempt to invoke the module's default entrypoint
44
+ method_name = _non_bang_method(name)
45
+
46
+ @method_chain << method_name
47
+
48
+ @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(method_name)
49
+ _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
50
+ else
51
+ @method_chain << name.to_s
52
+
53
+ @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(name)
54
+ _invoke(*args, **kwargs)
55
+ end
56
+ end
57
+
58
+ # if this namespace contains an OpsFile of the same name as the namespace, e.g. pkg/install/install.ops, then this
59
+ # method invokes the OpsFile of that same name and returns the result;
60
+ # otherwise we return this namespace object
61
+ def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block)
62
+ method_name = @namespace_or_ops_file.dirname.basename
63
+ resolved_symbol = @namespace_or_ops_file.resolve_symbol(method_name)
64
+ if resolved_symbol.is_a? OpsFile
65
+ _resolve_method_and_invoke(method_name)
66
+ else
67
+ self
68
+ end
27
69
  end
28
70
 
29
71
  def _invoke(*args, **kwargs)
30
72
  case @namespace_or_ops_file
31
73
  when Namespace
32
- _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
74
+ self
33
75
  when OpsFile
34
76
  _invoke_remote(*args, **kwargs)
35
77
  end
@@ -59,32 +101,13 @@ module OpsWalrus
59
101
  end.join(" ")
60
102
  end
61
103
 
62
- @host_proxy.run_ops(:run, "--script", remote_run_command_args)
63
- end
64
-
65
- # if this namespace contains an OpsFile of the same name as the namespace, e.g. pkg/install/install.ops, then this
66
- # method invokes the OpsFile of that same name and returns the result;
67
- # otherwise we return this namespace object
68
- def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block)
69
- method_name = @namespace_or_ops_file.dirname.basename
70
- resolved_symbol = @namespace_or_ops_file.resolve_symbol(method_name)
71
- if resolved_symbol.is_a? OpsFile
72
- _resolve_method_and_invoke(method_name)
104
+ # @host_proxy.run_ops(:run, "--script", remote_run_command_args)
105
+ if @prompt_for_sudo_password
106
+ @host_proxy.run_ops(:run, "--pass", remote_run_command_args)
73
107
  else
74
- self
108
+ @host_proxy.run_ops(:run, remote_run_command_args)
75
109
  end
76
110
  end
77
-
78
- def _resolve_method_and_invoke(name, *args, **kwargs)
79
- @method_chain << name.to_s
80
-
81
- @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(name)
82
- _invoke(*args, **kwargs)
83
- end
84
-
85
- def method_missing(name, *args, **kwargs, &block)
86
- _resolve_method_and_invoke(name, *args, **kwargs)
87
- end
88
111
  end
89
112
 
90
113
  class LocalImportInvocationContext < ImportInvocationContext
@@ -93,24 +116,25 @@ module OpsWalrus
93
116
  @initial_namespace_or_ops_file = @namespace_or_ops_file = namespace_or_ops_file
94
117
  end
95
118
 
96
- def _invoke(*args, **kwargs)
97
- case @namespace_or_ops_file
98
- when Namespace
99
- _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
100
- when OpsFile
101
- _invoke_local(*args, **kwargs)
102
- end
119
+ def method_missing(name, *args, **kwargs, &block)
120
+ _resolve_method_and_invoke(name, *args, **kwargs)
103
121
  end
104
122
 
105
- def _invoke_local(*args, **kwargs)
106
- params_hash = @namespace_or_ops_file.build_params_hash(*args, **kwargs)
107
- @namespace_or_ops_file.invoke(@runtime_env, params_hash)
123
+ def _resolve_method_and_invoke(name, *args, **kwargs)
124
+ if _bang_method?(name) # foo! is an attempt to invoke the module's default entrypoint
125
+ method_name = _non_bang_method(name)
126
+ @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(method_name)
127
+ _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
128
+ else
129
+ @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(name)
130
+ _invoke(*args, **kwargs)
131
+ end
108
132
  end
109
133
 
110
134
  # if this namespace contains an OpsFile of the same name as the namespace, e.g. pkg/install/install.ops, then this
111
135
  # method invokes the OpsFile of that same name and returns the result;
112
136
  # otherwise we return this namespace object
113
- def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block)
137
+ def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
114
138
  method_name = @namespace_or_ops_file.dirname.basename
115
139
  resolved_symbol = @namespace_or_ops_file.resolve_symbol(method_name)
116
140
  if resolved_symbol.is_a? OpsFile
@@ -121,14 +145,20 @@ module OpsWalrus
121
145
  end
122
146
  end
123
147
 
124
- def _resolve_method_and_invoke(name, *args, **kwargs)
125
- @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(name)
126
- _invoke(*args, **kwargs)
148
+ def _invoke(*args, **kwargs)
149
+ case @namespace_or_ops_file
150
+ when Namespace
151
+ self
152
+ when OpsFile
153
+ _invoke_local(*args, **kwargs)
154
+ end
127
155
  end
128
156
 
129
- def method_missing(name, *args, **kwargs, &block)
130
- _resolve_method_and_invoke(name, *args, **kwargs)
157
+ def _invoke_local(*args, **kwargs)
158
+ params_hash = @namespace_or_ops_file.build_params_hash(*args, **kwargs)
159
+ @namespace_or_ops_file.invoke(@runtime_env, params_hash)
131
160
  end
161
+
132
162
  end
133
163
 
134
164
  end
@@ -112,7 +112,6 @@ module SSHKit
112
112
  buffer || ""
113
113
  end
114
114
 
115
-
116
115
  def handle_data_for_stderr(output, cmd, buffer, stdin, is_blocked)
117
116
  # we're blocked on reading, so let's process the buffer
118
117
  lines, buffer = split_buffer(buffer)
@@ -28,24 +28,39 @@ module OpsWalrus
28
28
  @app.sudo_password
29
29
  end
30
30
 
31
- # runtime_kv_args is an Array(String) of the form: ["arg1:val1", "arg2:val2", ...]
32
- # params_json_hash is a Hash representation of a JSON string
33
- def run(runtime_kv_args, params_json_hash: nil)
34
- params_hash = runtime_kv_args.reduce(params_json_hash || {}) do |memo, kv_pair_string|
35
- str_key, str_value = kv_pair_string.split(":", 2)
36
- if pre_existing_value = memo[str_key]
37
- array = pre_existing_value.is_a?(Array) ? pre_existing_value : [pre_existing_value]
38
- array << str_value
39
- memo[str_key] = array
31
+ # runtime_kv_args is an Array(String) of the form: ["arg1:val1", "arg1:val2", ...]
32
+ # irb(main):057:0> build_params_hash(["names:foo", "names:bar", "names:baz", "age:5", "profile:name:corge", "profile:language:en", "height:5ft8in"])
33
+ # => {"names"=>["foo", "bar", "baz"], "age"=>"5", "profile"=>{"name"=>"corge", "language"=>"en"}, "height"=>"5ft8in"}
34
+ def build_params_hash(runtime_kv_args, params_json_hash: nil)
35
+ runtime_kv_args.reduce(params_json_hash || {}) do |memo, kv_pair_string|
36
+ param_name, str_value = kv_pair_string.split(":", 2)
37
+ key, value = str_value.split(":", 2)
38
+ if pre_existing_value = memo[param_name]
39
+ memo[param_name] = if value # we're dealing with a Hash parameter value
40
+ pre_existing_value.merge(key => value)
41
+ else # we're dealing with an Array parameter value or a scalar parameter value
42
+ array = pre_existing_value.is_a?(Array) ? pre_existing_value : [pre_existing_value]
43
+ array << str_value
44
+ end
40
45
  else
41
- memo[str_key] = str_value
46
+ memo[param_name] = if value # we're dealing with a Hash parameter value
47
+ {key => value}
48
+ else # we're dealing with an Array parameter value or a scalar parameter value
49
+ str_value
50
+ end
42
51
  end
43
52
  memo
44
53
  end
54
+ end
55
+
56
+ # runtime_kv_args is an Array(String) of the form: ["arg1:val1", "arg1:val2", ...]
57
+ # params_json_hash is a Hash representation of a JSON string
58
+ def run(runtime_kv_args, params_json_hash: nil)
59
+ params_hash = build_params_hash(runtime_kv_args, params_json_hash: params_json_hash)
45
60
 
46
61
  if app.debug?
47
- puts "Script:"
48
- puts @entry_point_ops_file.script
62
+ App.instance.trace "Script:"
63
+ App.instance.trace @entry_point_ops_file.script
49
64
  end
50
65
 
51
66
  result = begin
@@ -61,14 +76,14 @@ module OpsWalrus
61
76
  App.instance.error "[!] Command failed: #{e.message}"
62
77
  rescue Error => e
63
78
  App.instance.error "Error: Ops script crashed."
64
- App.instance.error e.message
65
- App.instance.error e.backtrace.take(5).join("\n")
79
+ App.instance.error e
80
+ # App.instance.error e.backtrace.take(5).join("\n")
66
81
  Invocation::Error.new(e)
67
82
  rescue => e
68
83
  App.instance.error "Unhandled Error: Ops script crashed."
69
84
  App.instance.error e.class
70
- App.instance.error e.message
71
- App.instance.error e.backtrace.take(10).join("\n")
85
+ App.instance.error e
86
+ # App.instance.error e.backtrace.take(10).join("\n")
72
87
  Invocation::Error.new(e)
73
88
  end
74
89
 
@@ -76,7 +91,7 @@ module OpsWalrus
76
91
  App.instance.debug "Ops script error details:"
77
92
  App.instance.debug "Error: #{result.value}"
78
93
  App.instance.debug "Status code: #{result.exit_status}"
79
- App.instance.debug @entry_point_ops_file.script
94
+ App.instance.debug @entry_point_ops_file.script.to_s
80
95
  end
81
96
 
82
97
  result
@@ -173,8 +173,9 @@ module OpsWalrus
173
173
  raise Error, "Unknown import reference: #{local_name}: #{import_str.inspect}"
174
174
  end
175
175
 
176
- def invoke(runtime_env, params_hash)
177
- script._invoke(runtime_env, params_hash)
176
+ def invoke(runtime_env, hashlike_params)
177
+ # this invokes the dynamically generated _invoke method that is defined at runtime within OpsFileScript.define_for(...)
178
+ script._invoke(runtime_env, hashlike_params)
178
179
  end
179
180
 
180
181
  def build_params_hash(*args, **kwargs)
@@ -1,9 +1,80 @@
1
+ require 'forwardable'
1
2
  require 'set'
2
3
  require_relative 'invocation'
3
4
  require_relative 'ops_file_script_dsl'
4
5
 
5
6
  module OpsWalrus
6
7
 
8
+ class ArrayOrHashNavigationProxy
9
+ extend Forwardable
10
+
11
+ def initialize(array_or_hash)
12
+ @obj = array_or_hash
13
+ end
14
+
15
+ def_delegators :@obj, :to_s, :inspect, :hash, :===, :eql?, :kind_of?, :is_a?, :instance_of?, :respond_to?, :<=>
16
+
17
+ def [](index, *args, **kwargs, &block)
18
+ @obj.method(:[]).call(index, *args, **kwargs, &block)
19
+ end
20
+ def respond_to_missing?(method, *)
21
+ @obj.is_a?(Hash) && @obj.respond_to?(method)
22
+ end
23
+ def method_missing(name, *args, **kwargs, &block)
24
+ case @obj
25
+ when Array
26
+ @obj.method(name).call(*args, **kwargs, &block)
27
+ when Hash
28
+ if @obj.respond_to?(name)
29
+ @obj.method(name).call(*args, **kwargs, &block)
30
+ else
31
+ value = self[name.to_s]
32
+ case value
33
+ when Array, Hash
34
+ ArrayOrHashNavigationProxy.new(value)
35
+ else
36
+ value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class InvocationParams
44
+ # @params : Hash
45
+
46
+ # params : Hash | ArrayOrHashNavigationProxy
47
+ def initialize(hashlike_params)
48
+ # this doesn't seem to make any difference
49
+ @params = hashlike_params.to_h
50
+ # @params = hashlike_params
51
+ end
52
+
53
+ def [](key)
54
+ key = key.to_s if key.is_a? Symbol
55
+ @params[key]
56
+ end
57
+
58
+ def dig(*keys)
59
+ # keys = keys.map {|key| key.is_a?(Integer) ? key : key.to_s }
60
+ @params.dig(*keys)
61
+ end
62
+
63
+ def method_missing(name, *args, **kwargs, &block)
64
+ if @params.respond_to?(name)
65
+ @params.method(name).call(*args, **kwargs, &block)
66
+ else
67
+ value = self[name]
68
+ case value
69
+ when Array, Hash
70
+ ArrayOrHashNavigationProxy.new(value)
71
+ else
72
+ value
73
+ end
74
+ end
75
+ end
76
+ end
77
+
7
78
  class OpsFileScript
8
79
 
9
80
  def self.define_for(ops_file, ruby_script)
@@ -14,11 +85,11 @@ module OpsWalrus
14
85
  # define methods for the OpsFile's local_symbol_table: local imports and private lib directory
15
86
  ops_file.local_symbol_table.each do |symbol_name, import_reference|
16
87
  unless methods_defined.include? symbol_name
17
- App.instance.debug "defining method for local symbol table entry: #{symbol_name}"
88
+ App.instance.trace "defining method for local symbol table entry: #{symbol_name}"
18
89
  klass.define_method(symbol_name) do |*args, **kwargs, &block|
19
- App.instance.debug "resolving local symbol table entry: #{symbol_name}"
90
+ App.instance.trace "resolving local symbol table entry: #{symbol_name}"
20
91
  namespace_or_ops_file = @runtime_env.resolve_import_reference(ops_file, import_reference)
21
- App.instance.debug "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
92
+ App.instance.trace "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
22
93
 
23
94
  invocation_context = LocalImportInvocationContext.new(@runtime_env, namespace_or_ops_file)
24
95
  invocation_context._invoke(*args, **kwargs)
@@ -33,14 +104,14 @@ module OpsWalrus
33
104
  sibling_symbol_table_names |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
34
105
  sibling_symbol_table_names |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
35
106
  # puts "sibling_symbol_table_names=#{sibling_symbol_table_names}"
36
- App.instance.debug "methods_defined=#{methods_defined}"
107
+ App.instance.trace "methods_defined=#{methods_defined}"
37
108
  sibling_symbol_table_names.each do |symbol_name|
38
109
  unless methods_defined.include? symbol_name
39
- App.instance.debug "defining method for implicit imports: #{symbol_name}"
110
+ App.instance.trace "defining method for implicit imports: #{symbol_name}"
40
111
  klass.define_method(symbol_name) do |*args, **kwargs, &block|
41
- App.instance.debug "resolving implicit import: #{symbol_name}"
112
+ App.instance.trace "resolving implicit import: #{symbol_name}"
42
113
  namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(ops_file, symbol_name)
43
- App.instance.debug "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
114
+ App.instance.trace "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
44
115
 
45
116
  invocation_context = LocalImportInvocationContext.new(@runtime_env, namespace_or_ops_file)
46
117
  invocation_context._invoke(*args, **kwargs)
@@ -60,9 +131,9 @@ module OpsWalrus
60
131
  # - #verbose?
61
132
  # - all the dynamically defined methods in the subclass of Invocation
62
133
  invoke_method_definition = <<~INVOKE_METHOD
63
- def _invoke(runtime_env, params_hash)
134
+ def _invoke(runtime_env, hashlike_params)
64
135
  @runtime_env = runtime_env
65
- @params = InvocationParams.new(params_hash)
136
+ @params = InvocationParams.new(hashlike_params)
66
137
  #{ruby_script}
67
138
  end
68
139
  INVOKE_METHOD
@@ -101,7 +172,7 @@ module OpsWalrus
101
172
  end
102
173
 
103
174
  # The _invoke method is dynamically defined as part of OpsFileScript.define_for
104
- def _invoke(runtime_env, params_hash)
175
+ def _invoke(runtime_env, hashlike_params)
105
176
  raise "Not implemented in base class."
106
177
  end
107
178
 
@@ -12,67 +12,6 @@ require_relative 'walrus_lang'
12
12
 
13
13
  module OpsWalrus
14
14
 
15
- class ArrayOrHashNavigationProxy
16
- def initialize(array_or_hash)
17
- @obj = array_or_hash
18
- end
19
- def [](index, *args, **kwargs, &block)
20
- @obj.method(:[]).call(index, *args, **kwargs, &block)
21
- end
22
- def respond_to_missing?(method, *)
23
- @obj.is_a?(Hash) && @obj.respond_to?(method)
24
- end
25
- def method_missing(name, *args, **kwargs, &block)
26
- case @obj
27
- when Array
28
- @obj.method(name).call(*args, **kwargs, &block)
29
- when Hash
30
- if @obj.respond_to?(name)
31
- @obj.method(name).call(*args, **kwargs, &block)
32
- else
33
- value = self[name.to_s]
34
- case value
35
- when Array, Hash
36
- ArrayOrHashNavigationProxy.new(value)
37
- else
38
- value
39
- end
40
- end
41
- end
42
- end
43
- end
44
-
45
- class InvocationParams
46
- # params : Hash
47
- def initialize(params)
48
- @params = params
49
- end
50
-
51
- def [](key)
52
- key = key.to_s if key.is_a? Symbol
53
- @params[key]
54
- end
55
-
56
- def dig(*keys)
57
- # keys = keys.map {|key| key.is_a?(Integer) ? key : key.to_s }
58
- @params.dig(*keys)
59
- end
60
-
61
- def method_missing(name, *args, **kwargs, &block)
62
- if @params.respond_to?(name)
63
- @params.method(name).call(*args, **kwargs, &block)
64
- else
65
- value = self[name]
66
- case value
67
- when Array, Hash
68
- ArrayOrHashNavigationProxy.new(value)
69
- else
70
- value
71
- end
72
- end
73
- end
74
- end
75
-
76
15
  module Invocation
77
16
  class Result
78
17
  attr_accessor :value
@@ -157,7 +96,7 @@ module OpsWalrus
157
96
 
158
97
  # puts retval.inspect
159
98
 
160
- # cleanup
99
+ # todo: cleanup
161
100
  # if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
162
101
  # host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip", tmp_bundle_root_dir)
163
102
  # else
@@ -187,7 +126,7 @@ module OpsWalrus
187
126
  rescue => e
188
127
  puts e.class
189
128
  puts e.message
190
- # puts e.backtrace.join("\n")
129
+ puts e.backtrace.join("\n")
191
130
  ensure
192
131
  host.clear_ssh_session
193
132
  end
@@ -275,19 +214,23 @@ module OpsWalrus
275
214
  # puts "shell! self: #{self.inspect}"
276
215
 
277
216
  if App.instance.report_mode?
278
- print "[#{@runtime_env.local_hostname}] "
217
+ puts Style.green("*" * 80)
218
+ print "[#{Style.blue(@runtime_env.local_hostname)}] "
279
219
  print "#{description}: " if description
280
- puts cmd
220
+ puts Style.yellow(cmd)
281
221
  end
282
222
 
283
223
  return unless cmd && !cmd.strip.empty?
284
224
 
285
- sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
286
- # self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
287
- backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: :info)
225
+ if App.instance.dry_run?
226
+ ["", "", 0]
227
+ else
228
+ sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
229
+ # self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
230
+ backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: :info)
231
+ end
232
+ [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
288
233
  end
289
-
290
- [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
291
234
  end
292
235
 
293
236
  # def init_brew
@@ -15,15 +15,6 @@ module OpsWalrus
15
15
  @local_name, @package_uri, @version = local_name, package_uri, version
16
16
  end
17
17
 
18
- def sanitized_package_uri
19
- sanitize_path(@package_uri)
20
- end
21
-
22
- def sanitize_path(path)
23
- # found this at https://apidock.com/rails/v5.2.3/ActiveStorage/Filename/sanitized
24
- path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
25
- end
26
-
27
18
  # important: the import_resolution_dirname implemented as the local_name is critical because Bundler#download_package downloads
28
19
  # package dependencies to the name that this method returns, which must match the package reference's local name
29
20
  # so that later, when the package is being looked up on the load path (in LoadPath#resolve_import_reference),
@@ -34,7 +25,7 @@ module OpsWalrus
34
25
  # change in order for the three things to reconcile with respect to one another, since all three bits of logic are
35
26
  # what make bundling package dependencies and loading them function properly.
36
27
  def import_resolution_dirname
37
- local_name
28
+ "pkg_#{local_name}_version_#{version}"
38
29
  end
39
30
 
40
31
  def to_s
@@ -53,8 +44,17 @@ module OpsWalrus
53
44
  # these are dynamic package references defined at runtime when an OpsFile's imports are being evaluated.
54
45
  # this will usually be the case when an ops file does not belong to a package
55
46
  class DynamicPackageReference < PackageReference
47
+ def self.import_resolution_dirname(package_uri, version)
48
+ sanitized_package_uri = sanitize_path(package_uri || raise(Error, "Unspecified package reference"))
49
+ sanitized_version = sanitize_path(version || "")
50
+ "pkg_#{sanitized_package_uri}_version_#{sanitized_version}"
51
+ end
52
+ def self.sanitize_path(path)
53
+ # found this at https://apidock.com/rails/v5.2.3/ActiveStorage/Filename/sanitized
54
+ path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
55
+ end
56
56
  def import_resolution_dirname
57
- sanitized_package_uri
57
+ DynamicPackageReference.import_resolution_dirname(@package_uri, @version)
58
58
  end
59
59
  end
60
60
 
@@ -90,7 +90,7 @@ module OpsWalrus
90
90
  str = "Namespace: #{@dirname.to_s}\n"
91
91
  @symbol_table.each do |k, v|
92
92
  if v.is_a? Namespace
93
- str << "#{' ' * (indent)}|- #{k} : #{v.to_s(indent + 1)}\n"
93
+ str << "#{' ' * (indent)}|- #{k} : #{v.to_s(indent + 1)}"
94
94
  else
95
95
  str << "#{' ' * (indent)}|- #{k} : #{v.to_s}\n"
96
96
  end
@@ -106,31 +106,6 @@ module OpsWalrus
106
106
  @symbol_table[symbol_name.to_s]
107
107
  end
108
108
 
109
- # # if this namespace contains an OpsFile of the same name as the namespace, e.g. pkg/install/install.ops, then this
110
- # # method invokes the OpsFile of that same name and returns the result;
111
- # # otherwise we return this namespace object
112
- # def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block)
113
- # resolved_symbol = resolve_symbol(@dirname.basename)
114
- # if resolved_symbol.is_a? OpsFile
115
- # params_hash = resolved_symbol.build_params_hash(*args, **kwargs)
116
- # resolved_symbol.invoke(runtime_env, params_hash)
117
- # else
118
- # self
119
- # end
120
- # end
121
-
122
- # def method_missing(name, *args, **kwargs, &block)
123
- # # puts "method_missing: #{name}"
124
- # # puts caller
125
- # resolved_symbol = resolve_symbol(name)
126
- # case resolved_symbol
127
- # when Namespace
128
- # resolved_symbol._invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
129
- # when OpsFile
130
- # params_hash = resolved_symbol.build_params_hash(*args, **kwargs)
131
- # resolved_symbol.invoke(runtime_env, params_hash)
132
- # end
133
- # end
134
109
  end
135
110
 
136
111
  # the assumption is that we have a bundle directory with all the packages in it
@@ -147,16 +122,13 @@ module OpsWalrus
147
122
  @root_namespace = build_symbol_resolution_tree(@dir)
148
123
  @path_map = build_path_map(@root_namespace)
149
124
 
150
- # puts "*" * 80
151
- # puts "load path for #{@dir}"
152
- # puts "-" * 80
153
- # puts 'root namespace'
154
- # puts @root_namespace.to_s
155
- # puts "-" * 80
156
- # puts 'path map'
157
- # @path_map.each do |k,v|
158
- # puts "#{k.to_s}: #{v.to_s}"
159
- # end
125
+ App.instance.trace "LoadPath for #{@dir} ************************************************************"
126
+ App.instance.trace 'root namespace ******************************************************************'
127
+ App.instance.trace @root_namespace.to_s
128
+ App.instance.trace 'path map ************************************************************************'
129
+ @path_map.each do |k,v|
130
+ App.instance.trace "#{k.to_s}: #{v.to_s}"
131
+ end
160
132
 
161
133
  @dynamic_package_additions_memo = {}
162
134
  end
@@ -268,9 +240,16 @@ module OpsWalrus
268
240
  @bundle_load_path = LoadPath.new(self, @app.bundle_dir)
269
241
  @app_load_path = LoadPath.new(self, @app.pwd)
270
242
 
271
- @interaction_handler = ScopedMappingInteractionHandler.new({
272
- /\[sudo\] password for .*?:\s*/ => "#{sudo_password}\n",
273
- })
243
+ # we include this sudo password mapping by default because if a bundled script is being run on a remote host,
244
+ # then the remote command invocation will include the --pass flag when being run on the remote host, which will
245
+ # interactively prompt for a sudo password, and that will be the only opportunity for the command host
246
+ # that is running the bundled script on the remote host to interactively enter a sudo password for the remote context
247
+ # since the remote ops command will be running within a PTY, and the interactive prompts running on the remote
248
+ # host will be managed by the local ScopedMappingInteractionHandler running within the instance of the ops command
249
+ # process on the remote host, and the command host will not have any further opportunity to interactively enter
250
+ # any prompts on the remote host
251
+ interaction_handler_mapping_for_sudo_password = ScopedMappingInteractionHandler.mapping_for_sudo_password(sudo_password)
252
+ @interaction_handler = ScopedMappingInteractionHandler.new(interaction_handler_mapping_for_sudo_password)
274
253
 
275
254
  configure_sshkit
276
255
  end
@@ -291,6 +270,7 @@ module OpsWalrus
291
270
  # SSHKit.config.use_format :simpletext
292
271
  SSHKit.config.output_verbosity = :debug
293
272
  elsif app.verbose?
273
+ SSHKit.config.use_format :pretty
294
274
  # SSHKit.config.use_format :dot
295
275
  SSHKit.config.output_verbosity = :info
296
276
  end
@@ -344,8 +324,8 @@ module OpsWalrus
344
324
  end.run
345
325
  end
346
326
 
347
- def invoke(ops_file, params_hash)
348
- ops_file.invoke(self, params_hash)
327
+ def invoke(ops_file, hashlike_params)
328
+ ops_file.invoke(self, hashlike_params)
349
329
  end
350
330
 
351
331
  def find_load_path_that_includes_path(path)
@@ -1,3 +1,3 @@
1
1
  module OpsWalrus
2
- VERSION = "1.0.16"
2
+ VERSION = "1.0.18"
3
3
  end