opswalrus 1.0.8 → 1.0.10

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.
@@ -0,0 +1,446 @@
1
+ # require 'json'
2
+ # require 'set'
3
+ # require 'shellwords'
4
+ # require 'socket'
5
+ # require 'stringio'
6
+
7
+ # require 'sshkit'
8
+ # require 'sshkit/dsl'
9
+
10
+ # require_relative 'host'
11
+ # require_relative 'sshkit_ext'
12
+ # require_relative 'walrus_lang'
13
+
14
+ # module OpsWalrus
15
+
16
+ # class ArrayOrHashNavigationProxy
17
+ # def initialize(array_or_hash)
18
+ # @obj = array_or_hash
19
+ # end
20
+ # def [](index, *args, **kwargs, &block)
21
+ # @obj.method(:[]).call(index, *args, **kwargs, &block)
22
+ # end
23
+ # def respond_to_missing?(method, *)
24
+ # @obj.is_a?(Hash) && @obj.respond_to?(method)
25
+ # end
26
+ # def method_missing(name, *args, **kwargs, &block)
27
+ # case @obj
28
+ # when Array
29
+ # @obj.method(name).call(*args, **kwargs, &block)
30
+ # when Hash
31
+ # if @obj.respond_to?(name)
32
+ # @obj.method(name).call(*args, **kwargs, &block)
33
+ # else
34
+ # value = self[name.to_s]
35
+ # case value
36
+ # when Array, Hash
37
+ # ArrayOrHashNavigationProxy.new(value)
38
+ # else
39
+ # value
40
+ # end
41
+ # end
42
+ # end
43
+ # end
44
+ # end
45
+
46
+ # class InvocationParams
47
+ # # params : Hash
48
+ # def initialize(params)
49
+ # @params = params
50
+ # end
51
+
52
+ # def [](key)
53
+ # key = key.to_s if key.is_a? Symbol
54
+ # @params[key]
55
+ # end
56
+
57
+ # def dig(*keys)
58
+ # # keys = keys.map {|key| key.is_a?(Integer) ? key : key.to_s }
59
+ # @params.dig(*keys)
60
+ # end
61
+
62
+ # def method_missing(name, *args, **kwargs, &block)
63
+ # if @params.respond_to?(name)
64
+ # @params.method(name).call(*args, **kwargs, &block)
65
+ # else
66
+ # value = self[name]
67
+ # case value
68
+ # when Array, Hash
69
+ # ArrayOrHashNavigationProxy.new(value)
70
+ # else
71
+ # value
72
+ # end
73
+ # end
74
+ # end
75
+ # end
76
+
77
+
78
+ # # BootstrapLinuxHostShellScript = <<~SCRIPT
79
+ # # #!/usr/bin/env bash
80
+ # # ...
81
+ # # SCRIPT
82
+
83
+ # module InvocationDSL
84
+ # def ssh(*args, **kwargs, &block)
85
+ # host_proxy_class = @ops_file_script.host_proxy_class
86
+ # hosts = inventory(*args, **kwargs).map {|host| host_proxy_class.new(host) }
87
+ # sshkit_hosts = hosts.map(&:sshkit_host)
88
+ # sshkit_host_to_ops_host_map = sshkit_hosts.zip(hosts).to_h
89
+ # runtime_env = @runtime_env
90
+ # local_host = self
91
+ # # bootstrap_shell_script = BootstrapLinuxHostShellScript
92
+ # # on sshkit_hosts do |sshkit_host|
93
+ # SSHKit::Coordinator.new(sshkit_hosts).each(in: kwargs[:in] || :parallel) do |sshkit_host|
94
+
95
+ # # in this context, self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
96
+
97
+ # host = sshkit_host_to_ops_host_map[sshkit_host]
98
+ # # puts "#{host.alias} / #{host}:"
99
+
100
+ # begin
101
+ # host.set_runtime_env(runtime_env)
102
+ # host.set_ssh_session_connection(self) # self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
103
+
104
+ # # copy over bootstrap shell script
105
+ # # io = StringIO.new(bootstrap_shell_script)
106
+ # io = File.open(__FILE__.to_pathname.dirname.join("bootstrap.sh"))
107
+ # upload_success = host.upload(io, "tmpopsbootstrap.sh")
108
+ # io.close
109
+ # raise Error, "Unable to upload bootstrap shell script to remote host" unless upload_success
110
+ # host.execute(:chmod, "755", "tmpopsbootstrap.sh")
111
+ # host.execute(:sh, "tmpopsbootstrap.sh")
112
+
113
+ # # copy over ops bundle zip file
114
+ # zip_bundle_path = runtime_env.zip_bundle_path
115
+ # upload_success = host.upload(zip_bundle_path, "tmpops.zip")
116
+ # raise Error, "Unable to upload ops bundle to remote host" unless upload_success
117
+
118
+ # stdout, stderr, exit_status = host.run_ops(:bundle, "unzip tmpops.zip", in_bundle_root_dir: false)
119
+ # raise Error, "Unable to unzip ops bundle on remote host" unless exit_status == 0
120
+ # tmp_bundle_root_dir = stdout.strip
121
+ # host.set_ssh_session_tmp_bundle_root_dir(tmp_bundle_root_dir)
122
+
123
+ # # we run the block in the context of the host, s.t. `self` within the block evaluates to `host`
124
+ # retval = host.instance_exec(local_host, &block) # host is passed as the argument to the block
125
+
126
+ # # puts retval.inspect
127
+
128
+ # # cleanup
129
+ # if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
130
+ # host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip", tmp_bundle_root_dir)
131
+ # else
132
+ # host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip")
133
+ # end
134
+
135
+ # retval
136
+ # rescue SSHKit::Command::Failed => e
137
+ # puts "[!] Command failed:"
138
+ # puts e.message
139
+ # rescue Net::SSH::ConnectionTimeout
140
+ # puts "[!] The host '#{host}' not alive!"
141
+ # rescue Net::SSH::Timeout
142
+ # puts "[!] The host '#{host}' disconnected/timeouted unexpectedly!"
143
+ # rescue Errno::ECONNREFUSED
144
+ # puts "[!] Incorrect port #{port} for #{host}"
145
+ # rescue Net::SSH::HostKeyMismatch => e
146
+ # puts "[!] The host fingerprint does not match the last observed fingerprint for #{host}"
147
+ # puts e.message
148
+ # puts "You might try `ssh-keygen -f ~/.ssh/known_hosts -R \"#{host}\"`"
149
+ # rescue Net::SSH::AuthenticationFailed
150
+ # puts "Wrong Password: #{host} | #{user}:#{password}"
151
+ # rescue Net::SSH::Authentication::DisallowedMethod
152
+ # puts "[!] The host '#{host}' doesn't accept password authentication method."
153
+ # rescue Errno::EHOSTUNREACH => e
154
+ # puts "[!] The host '#{host}' is unreachable"
155
+ # rescue => e
156
+ # puts e.class
157
+ # puts e.message
158
+ # # puts e.backtrace.join("\n")
159
+ # ensure
160
+ # host.clear_ssh_session
161
+ # end
162
+ # end
163
+ # end
164
+
165
+ # def inventory(*args, **kwargs)
166
+ # tags = args.map(&:to_s)
167
+
168
+ # kwargs = kwargs.transform_keys(&:to_s)
169
+ # tags.concat(kwargs["tags"]) if kwargs["tags"]
170
+
171
+ # @runtime_env.app.inventory(tags)
172
+ # end
173
+
174
+ # def exit(exit_status, message = nil)
175
+ # if message
176
+ # puts message
177
+ # end
178
+ # result = if exit_status == 0
179
+ # Invocation::Success.new(nil)
180
+ # else
181
+ # Invocation::Error.new(nil, exit_status)
182
+ # end
183
+ # throw :exit_now, result
184
+ # end
185
+
186
+ # def env(*keys)
187
+ # keys = keys.map(&:to_s)
188
+ # if keys.empty?
189
+ # @env
190
+ # else
191
+ # @env.dig(*keys)
192
+ # end
193
+ # end
194
+
195
+ # # currently, import may only be used to import a package that is referenced in the script's package file
196
+ # # I may decide to extend this to work with dynamic package references
197
+ # #
198
+ # # local_package_name is the local package name defined for the package dependency that is attempting to be referenced
199
+ # def import(local_package_name)
200
+ # local_package_name = local_package_name.to_s
201
+ # package_reference = @ops_file_script.ops_file.package_file&.dependency(local_package_name)
202
+ # raise Error, "Unknown package reference: #{local_package_name}" unless package_reference
203
+ # import_reference = PackageDependencyReference.new(local_package_name, package_reference)
204
+ # # puts "import: #{import_reference.inspect}"
205
+ # @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
206
+ # end
207
+
208
+ # def params(*keys, default: nil)
209
+ # keys = keys.map(&:to_s)
210
+ # if keys.empty?
211
+ # @params
212
+ # else
213
+ # @params.dig(*keys) || default
214
+ # end
215
+ # end
216
+
217
+ # # returns the stdout from the command
218
+ # def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
219
+ # out, err, status = *shell!(desc_or_cmd, cmd, block, input: input)
220
+ # out
221
+ # end
222
+
223
+ # # returns the tuple: [stdout, stderr, exit_status]
224
+ # def shell(desc_or_cmd = nil, cmd = nil, input: nil, &block)
225
+ # shell!(desc_or_cmd, cmd, block, input: input)
226
+ # end
227
+
228
+ # # returns the tuple: [stdout, stderr, exit_status]
229
+ # def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
230
+ # # description = nil
231
+
232
+ # 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
233
+
234
+ # description = desc_or_cmd if cmd || block
235
+ # cmd = block.call if block
236
+ # cmd ||= desc_or_cmd
237
+
238
+ # cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
239
+
240
+ # #cmd = Shellwords.escape(cmd)
241
+
242
+ # # puts "shell! self: #{self.inspect}"
243
+
244
+ # if App.instance.report_mode?
245
+ # print "[#{@runtime_env.local_hostname}] "
246
+ # print "#{description}: " if description
247
+ # puts cmd
248
+ # end
249
+
250
+ # return unless cmd && !cmd.strip.empty?
251
+
252
+ # sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
253
+ # # self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
254
+ # backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: :info)
255
+ # end
256
+
257
+ # [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
258
+ # end
259
+
260
+ # # def init_brew
261
+ # # execute('eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"')
262
+ # # end
263
+
264
+ # end
265
+
266
+ # # An Invocation object represents a stack frame, and the params_hash represents the
267
+ # # arguments that the caller has supplied for that stack frame to reference
268
+ # class Invocation
269
+ # class Result
270
+ # attr_accessor :value
271
+ # attr_accessor :exit_status
272
+ # def initialize(value, exit_status = 0)
273
+ # @value = value
274
+ # @exit_status = exit_status
275
+ # end
276
+ # def success?
277
+ # !failure?
278
+ # end
279
+ # def failure?
280
+ # !success?
281
+ # end
282
+ # end
283
+ # class Success < Result
284
+ # def initialize(value)
285
+ # super(value, 0)
286
+ # end
287
+ # def success?
288
+ # true
289
+ # end
290
+ # end
291
+ # class Error < Result
292
+ # def initialize(value, exit_status = 1)
293
+ # super(value, exit_status == 0 ? 1 : exit_status)
294
+ # end
295
+ # def failure?
296
+ # true
297
+ # end
298
+ # end
299
+
300
+
301
+ # def self.define_invocation_class(ops_file)
302
+ # klass = Class.new(Invocation)
303
+
304
+ # methods_defined = Set.new
305
+
306
+ # # define methods for every import in the script
307
+ # ops_file.local_symbol_table.each do |symbol_name, import_reference|
308
+ # unless methods_defined.include? symbol_name
309
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
310
+ # # puts "0" * 80
311
+ # # puts "@runtime_env.resolve_import_reference(@ops_file_script.ops_file, #{import_reference.inspect})"
312
+ # # puts @ops_file_script.ops_file.ops_file_path
313
+ # # puts symbol_name
314
+ # namespace_or_ops_file = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
315
+ # # puts namespace_or_ops_file.inspect
316
+ # # puts "0" * 80
317
+ # case namespace_or_ops_file
318
+ # when Namespace
319
+ # namespace_or_ops_file
320
+ # when OpsFile
321
+ # params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
322
+ # namespace_or_ops_file.invoke(@runtime_env, params_hash)
323
+ # end
324
+ # end
325
+ # methods_defined << symbol_name
326
+ # end
327
+ # end
328
+
329
+ # # define methods for every Namespace or OpsFile within the namespace that the OpsFile resides within
330
+ # sibling_symbol_table = Set.new
331
+ # sibling_symbol_table |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
332
+ # sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
333
+ # sibling_symbol_table.each do |symbol_name|
334
+ # unless methods_defined.include? symbol_name
335
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
336
+ # # puts "0" * 80
337
+ # # puts "@runtime_env.resolve_symbol(@ops_file_script.ops_file, #{symbol_name})"
338
+ # # puts @ops_file_script.ops_file.ops_file_path
339
+ # # puts symbol_name
340
+ # namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(@ops_file_script.ops_file, symbol_name)
341
+ # # puts namespace_or_ops_file.inspect
342
+ # # puts "0" * 80
343
+ # case namespace_or_ops_file
344
+ # when Namespace
345
+ # namespace_or_ops_file
346
+ # when OpsFile
347
+ # params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
348
+ # namespace_or_ops_file.invoke(@runtime_env, params_hash)
349
+ # end
350
+ # end
351
+ # methods_defined << symbol_name
352
+ # end
353
+ # end
354
+
355
+ # klass
356
+ # end
357
+
358
+ # include InvocationDSL
359
+
360
+ # def initialize(ops_file_script, runtime_env, params_hash)
361
+ # @ops_file_script = ops_file_script
362
+ # @runtime_env = runtime_env
363
+ # @params = InvocationParams.new(params_hash)
364
+ # end
365
+
366
+ # def backend
367
+ # @runtime_env.pty
368
+ # end
369
+
370
+ # def debug?
371
+ # @runtime_env.debug?
372
+ # end
373
+
374
+ # def verbose?
375
+ # @runtime_env.verbose?
376
+ # end
377
+
378
+ # # def evaluate
379
+ # # # the evaluation context needs to be a module with all of the following:
380
+ # # # - InvocationDSL methods
381
+ # # # - @ops_file_script
382
+ # # # - @runtime_env
383
+ # # # - @params
384
+ # # # - #backend
385
+ # # # - #debug?
386
+ # # # - #verbose?
387
+ # # # - all the dynamically defined methods in the subclass of Invocation
388
+ # # end
389
+
390
+ # def evaluate
391
+ # eval(@ops_file_script.script, nil, @ops_file_script.ops_file.ops_file_path.to_s, @ops_file_script.ops_file.script_line_offset)
392
+ # end
393
+
394
+ # # def evaluate
395
+ # # ops_file_script = @ops_file_script
396
+ # # runtime_env = @runtime_env
397
+ # # invocation_params = @params
398
+
399
+ # # # we use a Module as the evaluation context instead of this #evaluate method so that classes and other structures may be defined in an .ops file
400
+ # # evaluation_context = Module.new
401
+ # # evaluation_context.module_exec {
402
+ # # include InvocationDSL
403
+
404
+ # # @ops_file_script = ops_file_script
405
+ # # @runtime_env = runtime_env
406
+ # # @params = invocation_params
407
+
408
+ # # def backend
409
+ # # @runtime_env.pty
410
+ # # end
411
+
412
+ # # def debug?
413
+ # # @runtime_env.debug?
414
+ # # end
415
+
416
+ # # def verbose?
417
+ # # @runtime_env.verbose?
418
+ # # end
419
+ # # }
420
+
421
+ # # evaluation_context.module_eval(@ops_file_script.script, @ops_file_script.ops_file.ops_file_path.to_s, @ops_file_script.ops_file.script_line_offset)
422
+ # # end
423
+
424
+ # # def method_missing(name, *args, **kwargs, &block)
425
+ # # puts "1" * 80
426
+ # # import_reference = @ops_file_script.ops_file.resolve_import(name)
427
+ # # if import_reference
428
+ # # puts "2" * 80
429
+ # # resolved_value = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
430
+ # # return resolved_value if resolved_value
431
+ # # end
432
+
433
+ # # puts "3" * 80
434
+ # # case namespace_or_ops_file = @runtime_env.resolve_symbol(@ops_file_script.ops_file, name.to_s)
435
+ # # when Namespace
436
+ # # puts "4" * 80
437
+ # # namespace_or_ops_file
438
+ # # when OpsFile
439
+ # # puts "5" * 80
440
+ # # namespace_or_ops_file.invoke(@runtime_env, *args, **kwargs, &block)
441
+ # # else
442
+ # # raise NoMethodError, "No method named '#{name}'"
443
+ # # end
444
+ # # end
445
+ # end
446
+ # end
@@ -30,7 +30,7 @@ module OpsWalrus
30
30
 
31
31
  # runtime_kv_args is an Array(String) of the form: ["arg1:val1", "arg2:val2", ...]
32
32
  # params_json_hash is a Hash representation of a JSON string
33
- def run(runtime_kv_args, params_json_hash: nil, verbose: false)
33
+ def run(runtime_kv_args, params_json_hash: nil)
34
34
  params_hash = runtime_kv_args.reduce(params_json_hash || {}) do |memo, kv_pair_string|
35
35
  str_key, str_value = kv_pair_string.split(":", 2)
36
36
  if pre_existing_value = memo[str_key]
@@ -43,7 +43,7 @@ module OpsWalrus
43
43
  memo
44
44
  end
45
45
 
46
- if verbose == 2
46
+ if app.debug?
47
47
  puts "Script:"
48
48
  puts @entry_point_ops_file.script
49
49
  end
@@ -72,7 +72,7 @@ module OpsWalrus
72
72
  Invocation::Error.new(e)
73
73
  end
74
74
 
75
- if verbose == 2 && result.failure?
75
+ if app.debug? && result.failure?
76
76
  puts "Ops script error details:"
77
77
  puts "Error: #{result.value}"
78
78
  puts "Status code: #{result.exit_status}"
@@ -1,6 +1,7 @@
1
1
  require 'pathname'
2
2
  require 'yaml'
3
3
  require_relative 'git'
4
+ require_relative 'host'
4
5
  require_relative 'ops_file_script'
5
6
 
6
7
  module OpsWalrus
@@ -9,7 +10,6 @@ module OpsWalrus
9
10
  attr_accessor :app
10
11
  attr_accessor :ops_file_path
11
12
  attr_accessor :yaml
12
- attr_accessor :script
13
13
 
14
14
  def initialize(app, ops_file_path)
15
15
  @app = app
@@ -24,32 +24,46 @@ module OpsWalrus
24
24
  self.class == other.class && self.hash == other.hash
25
25
  end
26
26
 
27
+ def host_proxy_class
28
+ @host_proxy_class || (lazy_load_file && @host_proxy_class)
29
+ end
30
+
27
31
  def yaml
28
- @yaml || (load_file && @yaml)
32
+ @yaml || (lazy_load_file && @yaml)
29
33
  end
30
34
 
31
35
  def script_line_offset
32
- @script_line_offset || (load_file && @script_line_offset)
36
+ @script_line_offset || (lazy_load_file && @script_line_offset)
33
37
  end
34
38
 
39
+ # returns a subclass of OpsFileScript
40
+ def script_class
41
+ @script_class || (lazy_load_file && @script_class)
42
+ end
43
+
44
+ # returns an instance of the class returned by #script_class
35
45
  def script
36
- @script || (load_file && @script)
46
+ @script || (lazy_load_file && @script)
37
47
  end
38
48
 
39
- def load_file
49
+ def lazy_load_file
50
+ # puts "OpsFile#lazy_load_file for #{ops_file_path}"
51
+ # puts caller
40
52
  yaml, ruby_script = if @ops_file_path.exist?
41
53
  parse(File.read(@ops_file_path))
42
54
  end || ["", ""]
43
55
  @script_line_offset = yaml.lines.size + 1 # +1 to account for the ... line
44
56
  @yaml = YAML.load(yaml) || {} # post_invariant: @yaml is a Hash
45
- @script = OpsFileScript.new(self, ruby_script)
57
+ @script_class = OpsFileScript.define_for(self, ruby_script)
58
+ @script = @script_class.new(self, ruby_script)
59
+ @host_proxy_class = HostProxy.define_host_proxy_class(self)
46
60
  end
47
61
 
48
62
  def parse(script_string)
49
63
  file_halves = script_string.split(/^\.\.\.$/, 2)
50
64
  case file_halves.count
51
65
  when 1
52
- yaml, ruby_script = "", file_halves
66
+ yaml, ruby_script = "", file_halves.first
53
67
  when 2
54
68
  yaml, ruby_script = *file_halves
55
69
  else
@@ -59,7 +73,7 @@ module OpsWalrus
59
73
  end
60
74
 
61
75
  def params
62
- yaml["params"]
76
+ yaml["params"] || {}
63
77
  end
64
78
 
65
79
  def output
@@ -97,28 +111,7 @@ module OpsWalrus
97
111
  # imports:
98
112
  # my_package: my_package
99
113
  in String => import_str
100
- case
101
- when package_reference = package_file&.dependency(import_str) # package dependency reference
102
- # in this context, import_str is the local package name documented in the package's dependencies
103
- PackageDependencyReference.new(local_name, package_reference)
104
- when import_str.to_pathname.exist? # path reference
105
- path = import_str.to_pathname
106
- case
107
- when path.directory?
108
- DirectoryReference.new(local_name, path.realpath)
109
- when path.file? && path.extname.downcase == ".ops"
110
- OpsFileReference.new(local_name, path.realpath)
111
- else
112
- raise Error, "Unknown import reference: #{local_name} -> #{import_str.inspect}"
113
- end
114
- when Git.repo?(import_str) # ops file has imported an ad-hoc git repo
115
- package_uri = import_str
116
- destination_package_path = app.bundler.dynamic_package_path_for_git_package(package_uri)
117
- # puts "DynamicPackageImportReference: #{local_name} -> #{destination_package_path}"
118
- DynamicPackageImportReference.new(local_name, DynamicPackageReference.new(local_name, package_uri, nil))
119
- else
120
- raise Error, "Unknown import reference: #{local_name}: #{yaml_import_reference.inspect}"
121
- end
114
+ import_string_to_import_reference(local_name, import_str)
122
115
 
123
116
  # when the imports line says:
124
117
  # imports:
@@ -137,9 +130,51 @@ module OpsWalrus
137
130
  end
138
131
  end
139
132
 
133
+ def import_string_to_import_reference(local_name, import_str)
134
+ if package_reference = package_file&.dependency(import_str) # package dependency reference
135
+ # in this context, import_str is the local package name documented in the package's dependencies
136
+ return PackageDependencyReference.new(local_name, package_reference)
137
+ end
138
+
139
+ import_path = import_str.to_pathname
140
+ if import_path.absolute? && import_path.exist? # absolute path reference
141
+ return case
142
+ when import_path.directory?
143
+ DirectoryReference.new(local_name, import_path.realpath)
144
+ when import_path.file? && import_path.extname.downcase == ".ops"
145
+ OpsFileReference.new(local_name, import_path.realpath)
146
+ else
147
+ raise Error, "Unknown import reference for absolute path: #{local_name}: #{import_path}"
148
+ end
149
+ end
150
+ if import_path.relative? # relative path reference
151
+ rebased_path = dirname.join(import_path)
152
+ if rebased_path.exist?
153
+ return case
154
+ when rebased_path.directory?
155
+ DirectoryReference.new(local_name, rebased_path.realpath)
156
+ when rebased_path.file? && rebased_path.extname.downcase == ".ops"
157
+ OpsFileReference.new(local_name, rebased_path.realpath)
158
+ else
159
+ raise Error, "Unknown import reference for relative path: #{local_name}: #{import_path}"
160
+ end
161
+ elsif rebased_path.sub_ext(".ops").exist?
162
+ return OpsFileReference.new(local_name, rebased_path.sub_ext(".ops").realpath)
163
+ end
164
+ end
165
+
166
+ package_uri = import_str
167
+ if Git.repo?(package_uri) # ops file has imported an ad-hoc git repo
168
+ destination_package_path = app.bundler.dynamic_package_path_for_git_package(package_uri)
169
+ # puts "DynamicPackageImportReference: #{local_name} -> #{destination_package_path}"
170
+ return DynamicPackageImportReference.new(local_name, DynamicPackageReference.new(local_name, package_uri, nil))
171
+ end
172
+
173
+ raise Error, "Unknown import reference: #{local_name}: #{import_str.inspect}"
174
+ end
175
+
140
176
  def invoke(runtime_env, params_hash)
141
- # puts "invoking: #{ops_file_path}"
142
- script.invoke(runtime_env, params_hash)
177
+ script._invoke(runtime_env, params_hash)
143
178
  end
144
179
 
145
180
  def build_params_hash(*args, **kwargs)
@@ -223,5 +258,9 @@ module OpsWalrus
223
258
  dirname.glob("*").select(&:directory?)
224
259
  end
225
260
 
261
+ def to_s
262
+ "OpsFile: #{ops_file_path}"
263
+ end
264
+
226
265
  end
227
266
  end