loom-core 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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