loom-core 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d528b08eb52c1ed7fca596af8d47b5e1869c5bd98d1bbeba9cb66c27dad3043
4
- data.tar.gz: 1445b6f3d508a9be19d32f2aa9253e3e6f7b7eb9b771b73ea482b9b3ca07a846
3
+ metadata.gz: 4dd2b1e4959e4556b67f1369c3e8de87bdca21c0082ec446d629319cc58ee748
4
+ data.tar.gz: edb2d6b2f14a01549b6b75820cf3d456d401fb396f7c13469b812922ae700e2b
5
5
  SHA512:
6
- metadata.gz: 93499a20ce6ba74bcf96dc84d6fbf2c1b1c3fd38901dd9f4c06e8f3ce04d6d44dde3176f1aa88b8becbda43b72b5e93767d7e088d8d4fb1c52e40e3b07212f9e
7
- data.tar.gz: f3e2c5a3699f76c3e6d83d41286b421158c9ed2380714dbe2e54658c51c3a466832967f29d1e12601c63beaf2f59d8e83b0b03938cff43459d906e59018d4eec
6
+ metadata.gz: 8459bc0b442c21f59217a8d4646418c17a82a77fa0321f56f1877e8892bc84be56ee16043f9fc362acd437b713296c66f3a8542df0fe9be64c9a08070453bf55
7
+ data.tar.gz: 3fb25060598c49d64efcc60e23a44e1ca927304ae861ddf45baa3ded0481da9da8d0f40f358b7c2e8df4c6ec2a1bf9cb2f017285cb0cf63a8ade311d7b149282
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- loom-core (0.0.3)
4
+ loom-core (0.0.5)
5
5
  bcrypt_pbkdf (>= 1.0, < 2.0)
6
6
  commander (~> 4.4)
7
7
  ed25519 (>= 1.0, < 2.0)
data/bin/loom CHANGED
@@ -19,10 +19,13 @@ module Loom
19
19
  program :name, "Loom - Weaving through infrastructure"
20
20
  program :version , Loom::VERSION
21
21
  program :description, <<EOS
22
- A lightweight infrastructure managment tool designed to manage hosts
23
- through SSH, loosely inspired by Python Fabric - http://www.fabfile.org/.
22
+ A lightweight infrastructure managment tool for managing hosts through SSH,
23
+ inspired by [Python Fabric](http://www.fabfile.org/) with thanks and nods
24
+ [Capistrano](https://capistranorb.com/).
24
25
 
25
26
  Try `loom weave uptime -H localhost` to see an example.
27
+
28
+ Want to know how loom works? @see lib/loom/pattern/dsl.rb
26
29
  EOS
27
30
 
28
31
  global_option "-V", "--verbose", "Report verbose results" do |v|
@@ -38,8 +41,12 @@ EOS
38
41
  end
39
42
  end
40
43
 
41
- global_option "--dbg [N]", Integer,
42
- "Enable deep debug logging, where N is 0-6, implies --verbose" do |n|
44
+ global_option("--dbg [N]", Integer, <<EOS
45
+ Enables deep debug logging, where N is 1-6, implies --verbose. The difference
46
+ between -d and --dbg is the former is intended for site.loom authors and the
47
+ latter for authors looking to inspect loom internals.
48
+ EOS
49
+ ) do |n|
43
50
  raise "N must be greater than 0" if n < 0
44
51
  Loom.configure do |c|
45
52
  c.log_level = n * -1
@@ -116,12 +123,14 @@ EOS
116
123
  puts "Loom mods are:"
117
124
  puts ""
118
125
 
126
+ # TODO: I think this is broken... fix this.
119
127
  Loom::Mods::ModLoader.registered_mods.each do |name, aliases|
120
128
  puts aliases.join(", ")
121
129
  puts "\t#{name}"
122
130
  end
123
131
  end
124
132
  end
133
+ alias_command :"m", "mods"
125
134
 
126
135
  command :"patterns" do |c|
127
136
  c.syntax = "loom patterns [pattern]"
@@ -7,6 +7,8 @@ module Loom
7
7
 
8
8
  class Config
9
9
 
10
+ # TODO: Add a more module config_var registry mechanism for Modules and
11
+ # FactProviders to register their own values & defaults.
10
12
  CONFIG_VARS = {
11
13
  :loom_search_paths => ['/etc/loom', File.join(ENV['HOME'], '.loom'), './.loom'],
12
14
  :loom_files => ['site.loom'],
@@ -1,5 +1,10 @@
1
1
  require 'socket'
2
2
 
3
+ ##
4
+ # Links to relevant SSHKit code:
5
+ # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/abstract.rb
6
+ # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/netssh.rb
7
+ # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/local.rb
3
8
  module Loom
4
9
 
5
10
  # TODO: Rename this to something like SSHKitWrapper, DSL is a
@@ -1,5 +1,12 @@
1
1
  module Loom::Facts
2
2
 
3
+ EMPTY = {}
4
+ class << EMPTY
5
+ def [](*args)
6
+ self
7
+ end
8
+ end
9
+
3
10
  ##
4
11
  # A factset is created by running each registered Loom::Facts::Provider and
5
12
  # calling Provider+collect_facts+. See ./fact_file_provider.rb and
@@ -79,14 +86,28 @@ module Loom::Facts
79
86
  end
80
87
 
81
88
  def hostname
89
+ Loom.log.debug(<<NB
90
+ `facts.hostname ` is actually the name by which SSH knows the host, not
91
+ necessarily the actual host name. e.g. it may be an ip address, /etc/hosts
92
+ alias, or ssh config alias. Use `facts.sshname` for the same value without this
93
+ warning.
94
+ NB
95
+ )
96
+ sshname
97
+ end
98
+
99
+ def sshname
82
100
  host_spec.hostname
83
101
  end
84
102
 
85
103
  def get(fact_name)
86
104
  v = @fact_map[fact_name.to_sym]
87
105
  dup = v.dup rescue v
88
- dup = FactSet.new(@host_spec, {}) if dup.nil?
89
- dup.is_a?(Hash) ? FactSet.new(@host_spec, dup) : dup
106
+ if dup.nil?
107
+ EMPTY
108
+ else
109
+ dup
110
+ end
90
111
  end
91
112
  alias_method :[], :get
92
113
 
@@ -6,6 +6,8 @@ module Loom::Pattern
6
6
  # includes all contexts of parent modules.
7
7
  class DefinitionContext
8
8
 
9
+ NilLetValueError = Class.new Loom::LoomError
10
+
9
11
  def initialize(pattern_module, parent_context=nil)
10
12
  @fact_map = pattern_module.facts.dup
11
13
  @let_map = pattern_module.let_map.dup
@@ -26,7 +28,9 @@ module Loom::Pattern
26
28
  host_fact_set.merge merged_fact_map
27
29
  end
28
30
 
29
- # TODO: #define_let_readers is a TERRIBLE name. Rename this method.
31
+ # TODO: #define_let_readers is a TERRIBLE name. Rename this method. Also
32
+ # consider moving the instance_exec call to inside RunContext, it's a bit
33
+ # misplaced here.
30
34
  #
31
35
  # The method is called by Reference#call with the Reference::RunContext
32
36
  # instance. The "let" defined local declarations are added as method
@@ -34,9 +38,16 @@ module Loom::Pattern
34
38
  # let definitions, merged from the associated module, up the namespace
35
39
  # graph, allowing for inheriting and overriding +let+ declarations.
36
40
  def define_let_readers(scope_object, fact_set)
37
- @merged_let_map.each do |let_key, block|
38
- raise "no let block" unless block
39
- value = scope_object.instance_exec fact_set, &block
41
+ @merged_let_map.each do |let_key, let_map_entry|
42
+ Loom.log.debug { "evaluating let expression[:#{let_key}]" }
43
+ value = scope_object.instance_exec fact_set, &let_map_entry.block
44
+ value = value.nil? ? let_map_entry.default : value
45
+ Loom.log.debug1(self) { "let[:#{let_key}] => #{value}" }
46
+
47
+ if value.nil? || value.equal?(Loom::Facts::EMPTY)
48
+ Loom.log.e "value of let expression[:#{let_key}] is nil"
49
+ raise NilLetValueError, let_key
50
+ end
40
51
  scope_object.define_singleton_method(let_key) { value }
41
52
  end
42
53
  end
@@ -1,23 +1,43 @@
1
1
  =begin
2
2
 
3
- # .loom File DSL
3
+ # TODO: DSL extensions:
4
+ - Pattern+non_idempotent+ marks a pattern as explicitly not idempotent, this
5
+ let's additional warnings and checks to be added
6
+ - A history module, store a log of each executed command, a hash of the .loom
7
+ file, and the requisite facts (the let declarations) for each executed pattern
8
+ on the host it executes. /var/log/loom/history? Create this log on startup.
9
+ -- add a new set of history commands through the CLI and a history
10
+ FactProvider exposing host/loom/pattern_slug execution stats.
11
+ - Provide automatic command reversion support with a =Module= DSL that ties in
12
+ with local revision history.
13
+ -- allow Module actions/mods to define an "undo" command of itself given the
14
+ original inputs to the action
15
+ -- using the history to pull previous params (let defns) into a revert command.
16
+ -- usages of the raw shell, such as `loom.x` and `loom.capture` would be
17
+ unsupported. so would accesses to the fact_set in any before/after/pattern
18
+ blocks.
19
+ -- however patterns that only used let blocks, and used all "revertable"
20
+ module methods, could have automatic state reversion and integrity checking
21
+ managed.
22
+ -- best practices (encouraged through warnings) to be to heavily discourage
23
+ uses of loom.execute and loom.capture in .loom files and encourage all
24
+ accesses to fact_set be done in let expressions (enforce this maybe?)
25
+ -- Later... before/after hooks can ensure the entire loom execution sequence
26
+ was "revertable"
27
+
28
+ ## .loom File DSL
29
+
30
+ See specs/test.loom for a valid .loom file.
31
+
32
+ I've tried to take inspriation from the RSpec DSL for .loom, so hopefully it
33
+ feels comfortable.
4
34
 
5
35
  Loom::Pattern::DSL is the mixin that defines the declarative API for all .loom
6
36
  file defined modules. It is included into Loom::Pattern by default. The outer
7
37
  most module that a .loom file declares has Loom::Pattern mixed in by
8
38
  default. Submodules must explicitly include Loom::Pattern, and will receive DSL.
9
39
 
10
- To follow the code path for .loom file loading see:
11
-
12
- Loom::Runner#load
13
- -> Loom::Pattern::Loader.load
14
- -> Loom::Pattern::ReferenceSet.load_from_file
15
- -> Loom::Pattern::ReferenceSet::Builder.create
16
-
17
- The Loom::Pattern::ReferenceSet::Builder creates a ReferenceSet from a .loom
18
- file. A ReferenseSet being a collection of references with uniquely named
19
- slugs. The slug of a reference is computed from the module namespace and
20
- instance method name. For example, given the following .loom file:
40
+ For example, given the following .loom file:
21
41
 
22
42
  ``` ~ruby
23
43
  def top_level; end
@@ -42,13 +62,31 @@ It declares a reference set with slugs:
42
62
 
43
63
  Defining the same pattern slug twice raises a DuplicatPatternRef error.
44
64
 
45
- Module Inner inherits all +let+ declarations from its outer contexts, both ::
46
- (root) and ::Outer. +before+ hooks are run from a top-down module ordering,
47
- +after+ hooks are run bottom-up. For example, given the following .loom file:
65
+ #### Code Details
66
+
67
+ To follow the code path for .loom file loading see:
68
+
69
+ Loom::Runner#load
70
+ -> Loom::Pattern::Loader.load
71
+ -> Loom::Pattern::ReferenceSet.load_from_file
72
+ -> Loom::Pattern::ReferenceSet::Builder.create
73
+
74
+ The Loom::Pattern::ReferenceSet::Builder creates a ReferenceSet from a .loom
75
+ file. A ReferenseSet being a collection of references with uniquely named
76
+ slugs. The slug of a reference is computed from the module namespace and
77
+ instance method name.
78
+
79
+ ### `let`, `before`, and `after`
80
+
81
+ Module::Inner, from above, inherits all +let+ declarations from its outer
82
+ contexts, both :: (root) and ::Outer. +before+ hooks are run from a top-down
83
+ module ordering, +after+ hooks are run bottom-up. For example, given the
84
+ following .loom file:
48
85
 
49
86
  ``` ~ruby
50
87
  let(:var_1) { "v1 value" }
51
88
  let(:var_2) { "v2 value" }
89
+ let(:var_3, "otherwise") { |facts| facts[:a] || facts[:b] }
52
90
 
53
91
  before { puts "runs first +before+" }
54
92
  after { puts "runs last +after+" }
@@ -65,13 +103,19 @@ module Submod
65
103
  end
66
104
  ```
67
105
 
68
-
69
106
  If running `loom submod:a_pattern`, then let declarations would declare values:
70
107
 
71
108
  { :var_1 => "submod value", :var_2 => "v2 value" }
72
109
 
110
+ :var_3 would be set to either fact :a or fact :b. If the let expression
111
+ evaluates to nil?, then "otherwise" will be used as a default.
112
+
73
113
  Each let value is effectively available as an `attr_reader` declaration from
74
- ::Submod#a_pattern. Before and After hook ordering with pattern execution would
114
+ ::Submod#a_pattern. Because the attr_reader is defined on the RunContext scope
115
+ object, let expressions are available all before/after hooks and pattern
116
+ expresions.
117
+
118
+ Before and After hook ordering with pattern execution would
75
119
  look like:
76
120
 
77
121
  => runs first +before+
@@ -83,12 +127,16 @@ look like:
83
127
  For the code that executes before hooks, pattern, after hooks see
84
128
  Loom::Pattern::Reference::RunContext#run.
85
129
 
130
+ #### Code Details
131
+
86
132
  The Loom::Pattern::Reference::RunContext acts as the the binding object for each
87
- pattern slug. i.e. When running a pattern slug, the RunContext is the self
88
- object. Let definitions, before and after hooks, and fact maps are unique to
89
- each RunContext, for each RunContext they are defined in the
133
+ pattern. i.e. RunContext is the self object for all the blocks defined in a loom
134
+ file. Let definitions, before and after hooks, and fact maps are unique to each
135
+ RunContext, for each RunContext they are defined in the
90
136
  Loom::Pattern::DefinitionContext. Each DefinitionContext is merged from it's
91
- parent module, see Loom::Pattern::DefinitionContext#merge_contexts for info.
137
+ parent module, see Loom::Pattern::DefinitionContext#merge_contexts for
138
+ info. Each pattern/host combo gets a unique RunContext instance via
139
+ Loom::Runner+execute_pattern+ -> Loom::Pattern::Reference+call+.
92
140
 
93
141
  The RunContext#run method is the actual execution of the pattern. A pattern,
94
142
  before association to a RunContext instance is an unbound method. During
@@ -96,6 +144,71 @@ RunContext#initialize the pattern is bound to the RunContext instance and
96
144
  executed during RunContext#run with the associated Loom::Shell::Api and
97
145
  Loom::Facts::FactSet as parameters.
98
146
 
147
+ See Loom::Pattern::DefinitionContext for evaluation of `let` blocks and
148
+ before/after context ordering.
149
+
150
+ ### `weave`
151
+
152
+ The `weave` keyword allows aliasing a sequence of patterns a single
153
+ name. Pattern execution will be flattened and run sequentially before or after
154
+ any other patterns in the `$ loom` invocation.
155
+
156
+ ``` ~ruby
157
+ pattern :step_1 { ... }
158
+ pattern :step_2 { ... }
159
+
160
+ weave :do_it, [ :step_1, :step_2 ]
161
+ ```
162
+
163
+ This creates pattern :do_it, which when run `$ loom do_it` will run :step_1,
164
+ :step_2. Recursive expansion is explicitly disallowed, only pattern names (not
165
+ weaves), are allowed in the 2nd param of `weave`.
166
+
167
+ #### Code Details
168
+
169
+ Weave expansion to pattern slugs is accomplished by creating a
170
+ Loom::Pattern::ExpandingReference via the Loom::Pattern::Loader+load+ path
171
+ invoked via Loom::Runner+load+. Expansion happens on read via
172
+ Loom::Pattern::Loader+patterns+, thus the list of patterns is constant
173
+ throughout all phases of pattern execution.
174
+
175
+ #### Pattern Execution Phases
176
+
177
+ Once hosts and patterns are identified in earlier Loom::Runner phases,
178
+ Loom::Runner+run_internal+, per host, initiates an SSH session and command
179
+ execution phases of processing the .loom file. First phase is fact collection
180
+ via +Loom::Facts.fact_set, see Loom::Facts::FactSet for createing, registering,
181
+ and executing Loom::Facts::FactProviders.
182
+
183
+ The inputs to fact collection are a Loom::Shell::Core, Loom::HostSpec, and
184
+ Loom::Config. Fact collection must be FAST and idempotent (or as reasonably
185
+ possible). No network requests should be made during fact colleciton. Fact
186
+ collection is done prior to EACH pattern/host combination in order to ensure
187
+ having the newest facts from prevoius pattern executions.
188
+
189
+ After fact collection is pattern block execution, including before and after
190
+ block execution. See comments above for pattern code pointers and other details.
191
+
192
+ ## Decorating the Loom Object with Custom Modules and FactProviders
193
+
194
+ TODO
195
+
196
+ See lib/loomext/coremods & lib/loomext/corefacts for examples.
197
+
198
+ ## Facts and Inventory
199
+
200
+ TODO
201
+
202
+ ## Config
203
+
204
+ TODO
205
+
206
+ ## Logger
207
+
208
+ TODO
209
+
210
+ ##
211
+
99
212
  =end
100
213
  module Loom::Pattern
101
214
 
@@ -121,9 +234,11 @@ module Loom::Pattern
121
234
  @facts = yield_result if yield_result.is_a? Hash
122
235
  end
123
236
 
124
- def let(name, &block)
237
+ def let(name, default: nil, &block)
238
+ raise "malformed let expression: missing block" unless block_given?
239
+
125
240
  @let_map ||= {}
126
- @let_map[name.to_sym] = block
241
+ @let_map[name.to_sym] = LetMapEntry.new default, &block
127
242
  end
128
243
 
129
244
  def pattern(name, &block)
@@ -143,26 +258,6 @@ module Loom::Pattern
143
258
  define_pattern_internal(name) { |_, _| true }
144
259
  end
145
260
 
146
- def define_pattern_internal(name, &block)
147
- @pattern_methods ||= []
148
- @pattern_method_map ||= {}
149
- @pattern_descriptions ||= {}
150
-
151
- method_name = name.to_sym
152
-
153
- @pattern_methods << method_name
154
- @pattern_method_map[method_name] = true
155
- @pattern_descriptions[method_name] = @next_description
156
- @next_description = nil
157
-
158
- define_method method_name, &block
159
- end
160
-
161
- def hook(scope, &block)
162
- @hooks ||= []
163
- @hooks << Hook.new(scope, &block)
164
- end
165
-
166
261
  def before(&block)
167
262
  hook :before, &block
168
263
  end
@@ -203,5 +298,34 @@ module Loom::Pattern
203
298
  def let_map
204
299
  @let_map || {}
205
300
  end
301
+
302
+ private
303
+ def define_pattern_internal(name, &block)
304
+ @pattern_methods ||= []
305
+ @pattern_method_map ||= {}
306
+ @pattern_descriptions ||= {}
307
+
308
+ method_name = name.to_sym
309
+
310
+ @pattern_methods << method_name
311
+ @pattern_method_map[method_name] = true
312
+ @pattern_descriptions[method_name] = @next_description
313
+ @next_description = nil
314
+
315
+ define_method method_name, &block
316
+ end
317
+
318
+ def hook(scope, &block)
319
+ @hooks ||= []
320
+ @hooks << Hook.new(scope, &block)
321
+ end
322
+ end # DSL
323
+
324
+ class LetMapEntry
325
+ attr_reader :default, :block
326
+ def initialize(default, &block)
327
+ @default = default
328
+ @block = block
329
+ end
206
330
  end
207
331
  end
@@ -19,8 +19,12 @@ module Loom::Pattern
19
19
  run_context = RunContext.new @unbound_method, @definition_ctx
20
20
 
21
21
  fact_set = @definition_ctx.fact_set host_fact_set
22
- Loom.log.debug5(self) { "fact set for pattern execution => #{fact_set.facts}" }
22
+ Loom.log.debug5(self) {
23
+ "fact set for pattern execution => #{fact_set.facts}" }
23
24
 
25
+ # TODO: wrap up this fact_set in a delegator/facade/proxy to eliminate the
26
+ # .loom file from directly accessing it. Add logging and deprecation
27
+ # warnings there.... like FactSet+hostname+ currently.
24
28
  @definition_ctx.define_let_readers run_context, fact_set
25
29
 
26
30
  begin
@@ -59,11 +59,21 @@ module Loom
59
59
  exit code
60
60
  rescue PatternExecutionError => e
61
61
  num_patterns_failed = @run_failures.size
62
- Loom.log.error "error executing #{num_patterns_failed} patterns => #{e}"
63
- Loom.log.debug e.backtrace.join "\n"
62
+ Loom.log.error "error executing #{num_patterns_failed} patterns => #{e}, run with -d for more"
63
+ Loom.log.debug e.backtrace.join "\n\t"
64
+ # TODO: I think the max return code is 255. Cap this if so.
64
65
  exit 100 + num_patterns_failed
66
+ rescue SSHKit::Runner::ExecuteError => e
67
+ Loom.log.error "wrapped SSHKit::Runner::ExecuteError, run with -d for more"
68
+ Loom.log.error e.cause
69
+ Loom.log.debug e.cause.backtrace.join "\n\t"
70
+ Loom.log.debug1(self) { "Original error:" }
71
+ Loom.log.debug1(self) { e.inspect }
72
+ Loom.log.debug1(self) { e.backtrace.join "\n\t" }
73
+ exit 97
65
74
  rescue Loom::LoomError => e
66
- Loom.log.error "loom error => #{e.inspect}"
75
+ Loom.log.error "Loom::LoomError => #{e.inspect}, run with -d for more"
76
+ Loom.log.debug e.cause.backtrace.join "\n\t"
67
77
  exit 98
68
78
  rescue => e
69
79
  Loom.log.fatal "fatal error => #{e.inspect}"
@@ -99,6 +109,8 @@ module Loom
99
109
  Loom::Inventory::InventoryList.active_inventory @loom_config
100
110
  @active_hosts = @inventory_list.hosts
101
111
 
112
+ # TODO: the naming inconsistency between Pattern::Loader and
113
+ # Mods::ModLoader bothers me... :(
102
114
  pattern_loader = Loom::Pattern::Loader.load @loom_config
103
115
  @pattern_refs = pattern_loader.patterns @pattern_slugs
104
116
 
@@ -141,11 +153,18 @@ module Loom
141
153
  pattern_shell = Loom::Shell.create @mod_loader, sshkit_backend, dry_run
142
154
 
143
155
  Loom.log.warn "dry run only => #{pattern_description}" if dry_run
144
- execute_pattern pattern_ref, pattern_shell, fact_set
156
+ failures = execute_pattern pattern_ref, pattern_shell, fact_set
157
+
158
+ if failures.empty?
159
+ Loom.log.debug "success on => #{pattern_description}"
160
+ else
161
+ Loom.log.error "failures on => #{pattern_description}:\n\t%s" % (
162
+ failures.join("\n\t"))
163
+ end
145
164
  end
146
165
  rescue IOError => e
147
- # TODO: Try to patch SSHKit for a more specific error for unexpected SSH
148
- # disconnections
166
+ # TODO: Try to patch SSHKit for a more specific error for unexpected
167
+ # SSH disconnections
149
168
  Loom.log.error "unexpected SSH disconnect => #{hostname}"
150
169
  Loom.log.debug e
151
170
  handle_host_failure_strategy hostname, e.message
@@ -168,6 +187,7 @@ module Loom
168
187
  # when a command fails and pattern execution should stop. All errors
169
188
  # should come from exceptions. This needs to be thought about wrt to the
170
189
  # defined `failure_strategy`
190
+ # @see the TODO at Loom::Shell::Core+test+
171
191
  run_failure = []
172
192
  begin
173
193
  pattern_ref.call(shell.shell_api, fact_set)
@@ -187,6 +207,7 @@ module Loom
187
207
  @result_reports << result_reporter
188
208
  @run_failures << run_failure unless run_failure.empty?
189
209
  end
210
+ run_failure
190
211
  end
191
212
 
192
213
  private
@@ -40,6 +40,7 @@ module Loom::Shell
40
40
  # objects and declare style of reporting & error code handling it
41
41
  # has. Commands can be defined to ignore errors and just return their
42
42
  # results.
43
+ # @see the TODO at Loom::Runner+execute_pattern+
43
44
  execute *cmd, :is_test => true, **cmd_opts
44
45
 
45
46
  case check
@@ -100,6 +101,8 @@ module Loom::Shell
100
101
  end
101
102
  end
102
103
 
104
+ # TODO: finish the harness... avoiding dealing w/ shell escaping, like
105
+ # from sudo, is the goal.
103
106
  # def sudo(user=nil, *sudo_cmd, &block)
104
107
  # # I'm trying to work around crappy double escaping issues caused by
105
108
  # # sudo_legacy... but I'm failing
@@ -1,3 +1,3 @@
1
1
  module Loom
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -7,6 +7,8 @@ module LoomExt::CoreMods
7
7
  #
8
8
  # loom << :echo, "hello there"
9
9
  class Exec < Loom::Mods::Module
10
+ # TODO: add an "example" DSL to Loom::Mods::Module to automate
11
+ # documentation.
10
12
  register_mod :exec, :alias => [:x, :<<] do |*cmd, **opts|
11
13
  shell.execute *cmd, **opts
12
14
  end
@@ -89,6 +89,7 @@ module LoomExt::CoreMods
89
89
  end
90
90
 
91
91
  def uninstall(pkg_name)
92
+ # TODO: fix all loom.execute calls w/ properly atomized params
92
93
  loom << "echo dnf uninstall"
93
94
  end
94
95
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loom-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erick Johnson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-21 00:00:00.000000000 Z
11
+ date: 2018-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sshkit