opswalrus 1.0.4 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  opswalrus is a tool that runs scripts against hosts. It's kind of like Ansible, but aims to be simpler to use.
4
4
 
5
+ # Getting started
6
+
7
+ You have two options:
8
+ - Install via Rubygems
9
+ - Install via Docker
10
+
11
+ ## Rubygems install
12
+
13
+ ## Docker install
14
+
15
+ ```shell
16
+ alias ops='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/opswalrus/ops'
17
+
18
+ ops --version
19
+ ```
20
+
5
21
  # Examples
6
22
 
7
23
  ```bash
data/build.ops ADDED
@@ -0,0 +1,20 @@
1
+ params:
2
+ version: string
3
+
4
+ imports:
5
+ core: "https://github.com/opswalrus/core.git"
6
+ ...
7
+
8
+ version = params.version
9
+
10
+ template = <<TEMPLATE
11
+ module OpsWalrus
12
+ VERSION = "{{ version }}"
13
+ end
14
+ TEMPLATE
15
+
16
+ puts "Write version.rb for version #{version}"
17
+ core.template.write(path: "./lib/opswalrus/version.rb", template: template, variables: {version: version})
18
+
19
+ sh("Build gem") { 'gem build opswalrus.gemspec' }
20
+ sh("Build docker image") { 'docker build -t opswalrus/ops:{{ version }} .' }
data/lib/opswalrus/app.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "citrus"
2
- require "git"
3
2
  require "io/console"
4
3
  require "json"
5
4
  require "random/formatter"
@@ -8,6 +7,7 @@ require "socket"
8
7
  require "stringio"
9
8
  require "yaml"
10
9
  require "pathname"
10
+ require_relative "git"
11
11
  require_relative "host"
12
12
  require_relative "hosts_file"
13
13
  require_relative "operation_runner"
@@ -158,24 +158,11 @@ module OpsWalrus
158
158
  ops_file = OpsFile.new(self, ops_file_path)
159
159
 
160
160
  # if the ops file is part of a package, then set the package directory as the app's pwd
161
- # puts "run1: #{ops_file.ops_file_path}"
162
161
  if ops_file.package_file && ops_file.package_file.dirname.to_s !~ /#{Bundler::BUNDLE_DIR}/
163
- # puts "set pwd: #{ops_file.package_file.dirname}"
164
- set_pwd(ops_file.package_file.dirname)
165
- rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(ops_file.package_file.dirname)
166
- # note: rebased_ops_file_relative_path is a relative path that is relative to ops_file.package_file.dirname
167
- # puts "rebased path: #{rebased_ops_file_relative_path}"
168
- absolute_ops_file_path = ops_file.package_file.dirname.join(rebased_ops_file_relative_path)
169
- # puts "absolute path: #{absolute_ops_file_path}"
170
- ops_file = OpsFile.new(self, absolute_ops_file_path)
162
+ ops_file = set_pwd_to_ops_file_package_directory(ops_file)
171
163
  end
172
- # puts "run2: #{ops_file.ops_file_path}"
173
164
 
174
165
  op = OperationRunner.new(self, ops_file)
175
- # if op.requires_sudo?
176
- # prompt_sudo_password unless sudo_password
177
- # end
178
- # exit_status, out, err, script_output_structure = op.run(operation_kv_args, params_json_hash: @params, verbose: @verbose)
179
166
  result = op.run(operation_kv_args, params_json_hash: @params, verbose: @verbose)
180
167
  exit_status = result.exit_status
181
168
 
@@ -183,14 +170,7 @@ module OpsWalrus
183
170
  puts "Op exit_status"
184
171
  puts exit_status
185
172
 
186
- # puts "Op stdout"
187
- # puts out
188
-
189
- # puts "Op stderr"
190
- # puts err
191
-
192
173
  puts "Op output"
193
- # puts script_output_structure ? JSON.pretty_generate(script_output_structure) : nil.inspect
194
174
  puts JSON.pretty_generate(result.value)
195
175
  end
196
176
 
@@ -203,6 +183,19 @@ module OpsWalrus
203
183
  FileUtils.remove_entry(tmp_dir) if tmp_dir
204
184
  end
205
185
 
186
+ # sets the App's pwd to the ops file's package directory and
187
+ # returns a new OpsFile that points at the revised pathname with considered as relative to the package file's directory
188
+ def set_pwd_to_ops_file_package_directory(ops_file)
189
+ # puts "set pwd: #{ops_file.package_file.dirname}"
190
+ set_pwd(ops_file.package_file.dirname)
191
+ rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(ops_file.package_file.dirname)
192
+ # note: rebased_ops_file_relative_path is a relative path that is relative to ops_file.package_file.dirname
193
+ # puts "rebased path: #{rebased_ops_file_relative_path}"
194
+ absolute_ops_file_path = ops_file.package_file.dirname.join(rebased_ops_file_relative_path)
195
+ # puts "absolute path: #{absolute_ops_file_path}"
196
+ OpsFile.new(self, absolute_ops_file_path)
197
+ end
198
+
206
199
  # package_operation_and_args can take one of the following forms:
207
200
  # - ["github.com/davidkellis/my-package", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
208
201
  # - ["foo.zip", "foo/myfile.ops", "arg1:val1", "arg2:val2", "arg3:val3"]
@@ -244,7 +237,7 @@ module OpsWalrus
244
237
 
245
238
  # operation_kv_args = package_operation_and_args
246
239
  [ops_file_path, operation_kv_args, tmp_dir]
247
- when repo_url = git_repo?(package_or_ops_file_reference)
240
+ when repo_url = Git.repo?(package_or_ops_file_reference)
248
241
  destination_package_path = bundler.download_git_package(repo_url)
249
242
 
250
243
  ops_file_path, operation_kv_args = find_entry_point_ops_file_in_dir(destination_package_path, package_operation_and_args)
@@ -301,22 +294,6 @@ module OpsWalrus
301
294
  [ops_file_path, operation_kv_args]
302
295
  end
303
296
 
304
- # git_repo?("davidkellis/arborist") -> "https://github.com/davidkellis/arborist"
305
- # returns the repo URL
306
- def git_repo?(repo_reference)
307
- candidate_repo_references = [
308
- repo_reference,
309
- repo_reference =~ /(\.(com|net|org|dev|io|local))\// && "https://#{repo_reference}",
310
- repo_reference !~ /github\.com\// && repo_reference =~ /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/([\w\.@\:\-~]+)$/i && "https://github.com/#{repo_reference}" # this regex is from https://www.npmjs.com/package/github-username-regex and https://www.debuggex.com/r/H4kRw1G0YPyBFjfm
311
- ].compact
312
- working_repo_reference = candidate_repo_references.find {|reference| Git.ls_remote(reference) rescue nil }
313
- working_repo_reference
314
- end
315
-
316
- # def is_dir_git_repo?(dir_path)
317
- # Git.ls_remote(reference) rescue nil
318
- # end
319
-
320
297
  def bundle_status
321
298
  end
322
299
 
@@ -22,8 +22,8 @@ module OpsWalrus
22
22
  @bundle_dir
23
23
  end
24
24
 
25
- def ensure_package_bundle_directory_exists
26
- FileUtils.mkdir_p(@bundle_dir)
25
+ def ensure_pwd_bundle_directory_exists
26
+ FileUtils.mkdir_p(@bundle_dir) unless @bundle_dir.exist?
27
27
  end
28
28
 
29
29
  # # returns the OpsFile within the bundle directory that represents the given ops_file (which is outside of the bundle directory)
@@ -50,7 +50,7 @@ module OpsWalrus
50
50
  # end
51
51
 
52
52
  def update
53
- ensure_package_bundle_directory_exists
53
+ ensure_pwd_bundle_directory_exists
54
54
 
55
55
  package_yaml_files = pwd.glob("./**/package.yaml") - pwd.glob("./**/#{BUNDLE_DIR}/**/package.yaml")
56
56
  package_files_within_pwd = package_yaml_files.map {|path| PackageFile.new(path.realpath) }
@@ -83,7 +83,7 @@ module OpsWalrus
83
83
 
84
84
  # returns the self_pkg directory within the bundle directory
85
85
  # def include_directory_in_bundle_as_self_pkg(dirname = pwd)
86
- # ensure_package_bundle_directory_exists
86
+ # ensure_pwd_bundle_directory_exists
87
87
 
88
88
  # destination_package_path = @bundle_dir.join("self_pkg")
89
89
 
@@ -103,7 +103,7 @@ module OpsWalrus
103
103
  # This method downloads a package_url that is a dependency referenced in the specified package_file
104
104
  # returns the destination directory that the package was downloaded to
105
105
  def download_package(package_file, package_reference)
106
- ensure_package_bundle_directory_exists
106
+ ensure_pwd_bundle_directory_exists
107
107
 
108
108
  local_name = package_reference.local_name
109
109
  package_url = package_reference.package_uri
@@ -144,19 +144,26 @@ module OpsWalrus
144
144
  end
145
145
 
146
146
  def download_git_package(package_url, version = nil, destination_package_path = nil)
147
- destination_package_path ||= begin
148
- package_reference_dirname = sanitize_path(package_url)
149
- bundle_dir.join(package_reference_dirname)
150
- end
151
- FileUtils.remove_dir(destination_package_path) if File.exist?(destination_package_path)
147
+ ensure_pwd_bundle_directory_exists
148
+
149
+ destination_package_path ||= dynamic_package_path_for_git_package(package_url, version)
150
+
151
+ return destination_package_path if destination_package_path.exist?
152
+
152
153
  if version
153
- Git.clone(package_url, destination_package_path, branch: version, config: ['submodule.recurse=true'])
154
+ ::Git.clone(package_url, destination_package_path, branch: version, config: ['submodule.recurse=true'])
154
155
  else
155
- Git.clone(package_url, destination_package_path, config: ['submodule.recurse=true'])
156
+ ::Git.clone(package_url, destination_package_path, config: ['submodule.recurse=true'])
156
157
  end
158
+
157
159
  destination_package_path
158
160
  end
159
161
 
162
+ def dynamic_package_path_for_git_package(package_url, version = nil)
163
+ package_reference_dirname = sanitize_path(package_url)
164
+ bundle_dir.join(package_reference_dirname)
165
+ end
166
+
160
167
  def sanitize_path(path)
161
168
  # found this at https://apidock.com/rails/v5.2.3/ActiveStorage/Filename/sanitized
162
169
  path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
@@ -0,0 +1,22 @@
1
+ require "git"
2
+
3
+ module OpsWalrus
4
+ class Git
5
+ # repo?("davidkellis/arborist") -> "https://github.com/davidkellis/arborist"
6
+ # returns the repo URL or directory path
7
+ def self.repo?(repo_reference)
8
+ if Dir.exist?(repo_reference)
9
+ ::Git.ls_remote(repo_reference) && repo_reference rescue nil
10
+ else
11
+ candidate_repo_references = [
12
+ repo_reference,
13
+ repo_reference =~ /(\.(com|net|org|dev|io|local))\// && "https://#{repo_reference}",
14
+ repo_reference !~ /github\.com\// && repo_reference =~ /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/([\w\.@\:\-~]+)$/i && "https://github.com/#{repo_reference}" # this regex is from https://www.npmjs.com/package/github-username-regex and https://www.debuggex.com/r/H4kRw1G0YPyBFjfm
15
+ ].compact
16
+ working_repo_reference = candidate_repo_references.find {|reference| ::Git.ls_remote(reference) rescue nil }
17
+ working_repo_reference
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -27,6 +27,10 @@ module OpsWalrus
27
27
  cmd = block.call if block
28
28
  cmd ||= desc_or_cmd
29
29
 
30
+ cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
31
+
32
+ #cmd = Shellwords.escape(cmd)
33
+
30
34
  if self.alias
31
35
  print "[#{self.alias} | #{host}] "
32
36
  else
@@ -35,11 +39,8 @@ module OpsWalrus
35
39
  print "#{description}: " if description
36
40
  puts cmd
37
41
 
38
- cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
39
42
  return unless cmd && !cmd.strip.empty?
40
43
 
41
- #cmd = Shellwords.escape(cmd)
42
-
43
44
  # puts "shell: #{cmd}"
44
45
  # puts "shell: #{cmd.inspect}"
45
46
  # puts "sudo_password: #{sudo_password}"
@@ -72,7 +72,7 @@ module OpsWalrus
72
72
  Invocation::Error.new(e)
73
73
  end
74
74
 
75
- if verbose && result.failure?
75
+ if verbose == 2 && result.failure?
76
76
  puts "Ops script error details:"
77
77
  puts "Error: #{result.value}"
78
78
  puts "Status code: #{result.exit_status}"
@@ -1,5 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'yaml'
3
+ require_relative 'git'
3
4
  require_relative 'ops_file_script'
4
5
 
5
6
  module OpsWalrus
@@ -91,12 +92,16 @@ module OpsWalrus
91
92
  imports_hash.map do |local_name, yaml_import_reference|
92
93
  local_name = local_name.to_s
93
94
  import_reference = case yaml_import_reference
95
+
96
+ # when the imports line says:
97
+ # imports:
98
+ # my_package: my_package
94
99
  in String => import_str
95
100
  case
96
101
  when package_reference = package_file&.dependency(import_str) # package dependency reference
97
102
  # in this context, import_str is the local package name documented in the package's dependencies
98
103
  PackageDependencyReference.new(local_name, package_reference)
99
- when import_str.to_pathname.exist? # path reference
104
+ when import_str.to_pathname.exist? # path reference
100
105
  path = import_str.to_pathname
101
106
  case
102
107
  when path.directory?
@@ -106,13 +111,26 @@ module OpsWalrus
106
111
  else
107
112
  raise Error, "Unknown import reference: #{local_name} -> #{import_str.inspect}"
108
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}"
109
121
  end
110
- # in Hash
122
+
123
+ # when the imports line says:
124
+ # imports:
125
+ # my_package:
126
+ # url: https://...
127
+ # version: 2.1
128
+ # in Hash => package_defn
111
129
  # url = package_defn["url"]
112
130
  # version = package_defn["version"]
113
131
  # PackageReference.new(local_name, url, version&.to_s)
114
132
  else
115
- raise Error, "Unknown package reference: #{package_defn.inspect}"
133
+ raise Error, "Unknown import reference: #{local_name}: #{yaml_import_reference.inspect}"
116
134
  end
117
135
  [local_name, import_reference]
118
136
  end.to_h
@@ -205,36 +223,5 @@ module OpsWalrus
205
223
  dirname.glob("*").select(&:directory?)
206
224
  end
207
225
 
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
- def supporting_library_include_dir_and_require_lib
216
- if Dir.exist?(ops_file_helper_library_directory)
217
- [ops_file_helper_library_directory, ops_file_helper_library_basename]
218
- elsif File.exist?(ops_file_sibling_helper_library_file)
219
- [dirname, ops_file_helper_library_basename]
220
- else
221
- [nil, nil]
222
- end
223
- end
224
-
225
- def ops_file_helper_library_basename
226
- basename.sub_ext(".rb")
227
- end
228
-
229
- # "/home/david/sync/projects/ops/ops/core/host/info.ops" => "/home/david/sync/projects/ops/ops/core/host/info"
230
- def ops_file_helper_library_directory
231
- File.join(dirname, basename)
232
- end
233
-
234
- # "/home/david/sync/projects/ops/ops/core/host/info.ops" => "/home/david/sync/projects/ops/ops/core/host/info.rb"
235
- def ops_file_sibling_helper_library_file
236
- "#{ops_file_helper_library_directory}.rb"
237
- end
238
-
239
226
  end
240
227
  end
@@ -20,6 +20,9 @@ module OpsWalrus
20
20
  def [](index, *args, **kwargs, &block)
21
21
  @obj.method(:[]).call(index, *args, **kwargs, &block)
22
22
  end
23
+ def respond_to_missing?(method, *)
24
+ @obj.is_a?(Hash) && @obj.respond_to?(method)
25
+ end
23
26
  def method_missing(name, *args, **kwargs, &block)
24
27
  case @obj
25
28
  when Array
@@ -214,6 +217,9 @@ module OpsWalrus
214
217
  end
215
218
  end
216
219
 
220
+ # currently, import may only be used to import a package that is referenced in the script's package file
221
+ # I may decide to extend this to work with dynamic package references
222
+ #
217
223
  # local_package_name is the local package name defined for the package dependency that is attempting to be referenced
218
224
  def import(local_package_name)
219
225
  local_package_name = local_package_name.to_s
@@ -224,12 +230,12 @@ module OpsWalrus
224
230
  @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
225
231
  end
226
232
 
227
- def params(*keys)
233
+ def params(*keys, default: nil)
228
234
  keys = keys.map(&:to_s)
229
235
  if keys.empty?
230
236
  @params
231
237
  else
232
- @params.dig(*keys)
238
+ @params.dig(*keys) || default
233
239
  end
234
240
  end
235
241
 
@@ -254,17 +260,18 @@ module OpsWalrus
254
260
  cmd = block.call if block
255
261
  cmd ||= desc_or_cmd
256
262
 
263
+ cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
264
+
265
+ #cmd = Shellwords.escape(cmd)
266
+
257
267
  # puts "shell! self: #{self.inspect}"
258
268
 
259
269
  print "[#{@runtime_env.local_hostname}] "
260
270
  print "#{description}: " if description
261
271
  puts cmd
262
272
 
263
- cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
264
273
  return unless cmd && !cmd.strip.empty?
265
274
 
266
- #cmd = Shellwords.escape(cmd)
267
-
268
275
  sudo_password = @runtime_env.sudo_password
269
276
  sudo_password &&= sudo_password.gsub(/\n+$/,'') # remove trailing newlines from sudo_password
270
277
 
@@ -5,6 +5,7 @@ require_relative 'bundler'
5
5
 
6
6
  module OpsWalrus
7
7
 
8
+ # these are static package references defined ahead of time in the package file
8
9
  class PackageReference
9
10
  attr_accessor :local_name
10
11
  attr_accessor :package_uri
@@ -23,7 +24,7 @@ module OpsWalrus
23
24
  path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
24
25
  end
25
26
 
26
- # important: the dirname implemented as the local_name is critical because Bundler#download_package downloads
27
+ # important: the import_resolution_dirname implemented as the local_name is critical because Bundler#download_package downloads
27
28
  # package dependencies to the name that this method returns, which must match the package reference's local name
28
29
  # so that later, when the package is being looked up on the load path (in LoadPath#resolve_import_reference),
29
30
  # the package reference's referenced git repo or file path may not exist or be available, and so the package
@@ -32,12 +33,20 @@ module OpsWalrus
32
33
  # If this implementation changes, then Bundler#download_package and LoadPath#resolve_import_reference must also
33
34
  # change in order for the three things to reconcile with respect to one another, since all three bits of logic are
34
35
  # what make bundling package dependencies and loading them function properly.
35
- def dirname
36
+ def import_resolution_dirname
36
37
  local_name
37
38
  end
38
39
 
39
40
  end
40
41
 
42
+ # these are dynamic package references defined at runtime when an OpsFile's imports are being evaluated.
43
+ # this will usually be the case when an ops file does not belong to a package
44
+ class DynamicPackageReference < PackageReference
45
+ def import_resolution_dirname
46
+ sanitized_package_uri
47
+ end
48
+ end
49
+
41
50
  class PackageFile
42
51
  attr_accessor :package_file_path
43
52
  attr_accessor :yaml
@@ -32,6 +32,14 @@ module OpsWalrus
32
32
  end
33
33
  end
34
34
 
35
+ class DynamicPackageImportReference < ImportReference
36
+ attr_accessor :package_reference
37
+ def initialize(local_name, package_reference)
38
+ super(local_name)
39
+ @package_reference = package_reference
40
+ end
41
+ end
42
+
35
43
  class OpsFileReference < ImportReference
36
44
  attr_accessor :ops_file_path
37
45
  def initialize(local_name, ops_file_path)
@@ -86,6 +94,8 @@ module OpsWalrus
86
94
  @dir = dir
87
95
  @root_namespace = build_symbol_resolution_tree(@dir)
88
96
  @path_map = build_path_map(@root_namespace)
97
+
98
+ @dynamic_package_additions_memo = {}
89
99
  end
90
100
 
91
101
  # returns a tree of Namespace -> {Namespace* -> {Namespace* -> ..., OpsFile*}, OpsFile*}
@@ -129,6 +139,19 @@ module OpsWalrus
129
139
  path_map
130
140
  end
131
141
 
142
+ def dynamically_add_new_package_dir(new_package_dir)
143
+ # patch the symbol resolution (namespace) tree
144
+ dir_basename = new_package_dir.basename
145
+ unless @root_namespace.resolve_symbol(dir_basename)
146
+ new_child_namespace = build_symbol_resolution_tree(new_package_dir)
147
+ @root_namespace.add(dir_basename, new_child_namespace)
148
+
149
+ # patch the path map
150
+ new_partial_path_map = build_path_map(new_child_namespace)
151
+ @path_map.merge!(new_partial_path_map)
152
+ end
153
+ end
154
+
132
155
  # returns a Namespace
133
156
  def lookup_namespace(ops_file)
134
157
  @path_map[ops_file.dirname]
@@ -144,7 +167,16 @@ module OpsWalrus
144
167
  case import_reference
145
168
  when PackageDependencyReference
146
169
  # puts "root namespace: #{@root_namespace.symbol_table}"
147
- @root_namespace.resolve_symbol(import_reference.package_reference.local_name) # returns the Namespace associated with the bundled package dirname (i.e. the local name)
170
+ @root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package dirname (i.e. the local name)
171
+ when DynamicPackageImportReference
172
+ dynamic_package_reference = import_reference.package_reference
173
+ @dynamic_package_additions_memo[dynamic_package_reference] ||= begin
174
+ # puts "Downloading dynamic package: #{dynamic_package_reference.inspect}"
175
+ dynamically_added_package_dir = @runtime_env.app.bundler.download_git_package(dynamic_package_reference.package_uri, dynamic_package_reference.version)
176
+ dynamically_add_new_package_dir(dynamically_added_package_dir)
177
+ dynamically_added_package_dir
178
+ end
179
+ @root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package dirname (i.e. the sanitized package uri)
148
180
  when DirectoryReference
149
181
  @path_map[import_reference.dirname]
150
182
  when OpsFileReference
@@ -244,7 +276,7 @@ module OpsWalrus
244
276
  case import_reference
245
277
 
246
278
  # we know we're dealing with a package dependency reference, so we want to do the lookup in the bundle load path, where package dependencies live
247
- when PackageDependencyReference
279
+ when PackageDependencyReference, DynamicPackageImportReference
248
280
  @bundle_load_path.resolve_import_reference(origin_ops_file, import_reference)
249
281
 
250
282
  # we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to do the lookup in the app load path
@@ -1,3 +1,3 @@
1
1
  module OpsWalrus
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.6"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require "citrus"
2
+ require "ostruct"
2
3
  require "stringio"
3
4
 
4
5
  module WalrusLang
@@ -54,9 +55,17 @@ module WalrusLang
54
55
 
55
56
  Citrus.eval(Grammar)
56
57
 
57
- def self.render(template, binding)
58
+ # binding_obj : Binding | Hash
59
+ def self.render(template, binding_obj)
60
+ binding_obj = binding_obj.to_binding if binding_obj.respond_to?(:to_binding)
58
61
  ast = WalrusLang::Parser.parse(template)
59
- ast.render(binding)
62
+ ast.render(binding_obj)
63
+ end
64
+ end
65
+
66
+ class Hash
67
+ def to_binding
68
+ OpenStruct.new(self).instance_eval { binding }
60
69
  end
61
70
  end
62
71
 
data/opswalrus.gemspec CHANGED
@@ -3,17 +3,18 @@
3
3
  require_relative "lib/opswalrus/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "opswalrus"
7
- spec.version = OpsWalrus::VERSION
8
- spec.authors = ["David Ellis"]
9
- spec.email = ["david@conquerthelawn.com"]
10
-
11
- spec.summary = "opswalrus is a tool that runs scripts against a fleet of hosts"
12
- spec.description = "opswalrus is a tool that runs scripts against a fleet of hosts hosts. It's kind of like Ansible, but aims to be simpler to use."
13
- spec.homepage = "https://github.com/opswalrus/opswalrus"
6
+ spec.name = "opswalrus"
7
+ spec.version = OpsWalrus::VERSION
8
+ spec.authors = ["David Ellis"]
9
+ spec.email = ["david@conquerthelawn.com"]
10
+
11
+ spec.summary = "opswalrus is a tool that runs scripts against a fleet of hosts"
12
+ spec.description = "opswalrus is a tool that runs scripts against a fleet of hosts hosts. It's kind of like Ansible, but aims to be simpler to use."
13
+ spec.homepage = "https://github.com/opswalrus/opswalrus"
14
+ spec.license = "EPL-2.0"
14
15
  spec.required_ruby_version = ">= 2.6.0"
15
16
 
16
- # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server - https://example.com"
17
18
 
18
19
  spec.metadata["homepage_uri"] = spec.homepage
19
20
  spec.metadata["source_code_uri"] = "https://github.com/opswalrus/opswalrus"
@@ -31,14 +32,14 @@ Gem::Specification.new do |spec|
31
32
  spec.require_paths = ["lib"]
32
33
 
33
34
  # gem dependencies
34
- spec.add_dependency "citrus"
35
- spec.add_dependency "gli"
36
- spec.add_dependency "git"
37
- spec.add_dependency "rubyzip"
38
-
39
- spec.add_dependency "bcrypt_pbkdf"
40
- spec.add_dependency "ed25519"
41
- spec.add_dependency "sshkit"
35
+ spec.add_dependency "citrus", "~> 3.0"
36
+ spec.add_dependency "gli", "~> 2.21"
37
+ spec.add_dependency "git", "~> 1.18"
38
+ spec.add_dependency "rubyzip", "~> 2.3"
39
+
40
+ spec.add_dependency "bcrypt_pbkdf", "~> 1.1"
41
+ spec.add_dependency "ed25519", "~> 1.3"
42
+ spec.add_dependency "sshkit", "~> 1.21"
42
43
 
43
44
  # For more information and examples about making a new gem, check out our
44
45
  # guide at: https://bundler.io/guides/creating_gem.html