Class: ConfigmonkeyCli::Application::Manifest

Inherits:
Object
  • Object
show all
Defined in:
lib/configmonkey_cli/application/manifest.rb

Defined Under Namespace

Classes: ExecutionError, Invalid, ThorHelperApp

Constant Summary collapse

MANIFEST_ACTIONS =
[:chmod, :copy, :custom, :inplace, :invoke, :link, :mkdir, :rsync, :remove, :rtfm, :sync_links, :template]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, directory, manifest_file = "manifest.rb") ⇒ Manifest

Returns a new instance of Manifest.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/configmonkey_cli/application/manifest.rb', line 37

def initialize app, directory, manifest_file = "manifest.rb"
  @app = app
  @directory = directory
  init_thor!
  @padding = 26
  @manifest_file = File.join(directory, manifest_file || "manifest.rb")
  @actions = []
  @host_constraint = []
  @target_directory = app.opts[:target_directory]
  all do
    begin
      eval File.read(@manifest_file, encoding: "utf-8"), binding, @manifest_file
    rescue Exception => ex
      raise Invalid.new(@manifest_file, ex)
    end
  end
  app.debug "§constraint-final:#{@host_constraint}", 120
end

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def actions
  @actions
end

#appObject (readonly)

Returns the value of attribute app.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def app
  @app
end

#directoryObject (readonly)

Returns the value of attribute directory.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def directory
  @directory
end

#manifest_fileObject (readonly)

Returns the value of attribute manifest_file.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def manifest_file
  @manifest_file
end

#paddingObject (readonly)

Returns the value of attribute padding.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def padding
  @padding
end

#thorObject (readonly)

Returns the value of attribute thor.



35
36
37
# File 'lib/configmonkey_cli/application/manifest.rb', line 35

def thor
  @thor
end

Instance Method Details

#_breached_constraint?(constraint = nil) ⇒ Boolean

Returns:

  • (Boolean)


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/configmonkey_cli/application/manifest.rb', line 189

def _breached_constraint? constraint = nil
  in_constraint = catch :return_value do
    [@host_constraint, [constraint || []]].each do |list|
      list.each do |act, args|
        case act
        when :any then next
        when :on
          args.include?(app.opts[:hostname]) ? next : throw(:return_value, false)
        when :not_on
          args.include?(app.opts[:hostname]) ? throw(:return_value, false) : next
        end
      end
    end
    true
  end
  !in_constraint
end

#_dump!Object



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/configmonkey_cli/application/manifest.rb', line 127

def _dump!
  @actions.each do |constraint, action, instance|
    begin
      $cm_current_action_name = action
      $cm_current_action_color = :magenta
      thor.say_status :dump, instance, :black
    ensure
      $cm_current_action_name = $cm_current_action_color = nil
    end
  end
end

#_execute!(simulate = false) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/configmonkey_cli/application/manifest.rb', line 143

def _execute! simulate = false
  set_destination_root(@target_directory, false)

  if simulate
    thor.say_status :info, thor.set_color("---> !!! SIMULATION ONLY !!! <---", :green), :cyan
  else
    thor.say_status :info, thor.set_color("---> !!! HOT HOT HOT !!! <---", :red), :cyan
  end
  thor.say_status :info, (thor.set_color("Source Dir: ", :magenta) << thor.set_color(directory, :blue)), :cyan
  thor.say_status :info, (thor.set_color(" Dest Root: ", :magenta) << thor.set_color(thor.destination_root, :blue)), :cyan
  @actions.each_with_index do |(constraint, action, instance), index|
    begin
      $cm_current_action_index = index
      $cm_current_action_name = action
      $cm_current_action_color = :magenta
      run_action(instance, simulate)
    ensure
      $cm_current_action_index = $cm_current_action_name = $cm_current_action_color = nil
      app.haltpoint
    end
  end
rescue Interrupt, SystemExit
  raise
rescue Exception => ex
  raise(ExecutionError.new(@manifest_file, ex))
end

#_simulate!Object



139
140
141
# File 'lib/configmonkey_cli/application/manifest.rb', line 139

def _simulate!
  _execute!(true)
end

#_with_constraint(*constraint) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/configmonkey_cli/application/manifest.rb', line 170

def _with_constraint *constraint
  if @host_constraint.last == constraint
    return yield if block_given?
  end
  if _breached_constraint?(constraint)
    app.debug "§constraint-ignore:#{constraint}", 119
    return
  end
  begin
    @host_constraint << constraint
    app.debug "§constraint-push:#{constraint}", 120
    app.debug "§constraint-now:#{@host_constraint}", 121
    yield if block_given?
  ensure
    app.debug "§constraint-pop:#{@host_constraint.pop}", 120
    app.debug "§constraint-now:#{@host_constraint}", 121
  end
end

#all(&block) ⇒ Object

do block no matter the ‘hostname`



267
268
269
# File 'lib/configmonkey_cli/application/manifest.rb', line 267

def all &block
  _with_constraint(:any, &block)
end

#ask(question, opts = {}) ⇒ Object



288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/configmonkey_cli/application/manifest.rb', line 288

def ask question, opts = {}
  if opts[:use_thread] != false
    return app.interruptable { ask(question, opts.merge(use_thread: false)) }
  end
  opts[:limited_to] = opts.delete(:choose) if opts[:choose]
  opts[:add_to_history] = true unless opts.key?(:add_to_history)
  color = opts.delete(:color)
  spaces = "".ljust(opts[:padding]) if opts[:padding]
  begin
    @thor.ask("#{spaces}#{question}", color, opts).presence
  rescue Interrupt
    app.haltpoint(Thread.main)
  end
end

#c(str, *color) ⇒ Object



239
240
241
# File 'lib/configmonkey_cli/application/manifest.rb', line 239

def c str, *color
  thor.set_color(str, *color)
end

#checksum(*args) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/configmonkey_cli/application/manifest.rb', line 56

def checksum *args
  opts = args.extract_options!
  if opts[:soft]
    @_checksum_soft ||= begin
      to_c = args.map(&:to_s)
      to_c.unshift @manifest_file
      Digest::SHA1.hexdigest(to_c.to_s)
    end
  else
    @_checksum_hard ||= begin
      to_c = args.map(&:to_s)
      to_c.unshift Digest::SHA1.file(@manifest_file)
      Digest::SHA1.hexdigest(to_c.to_s)
    end
  end
end

#init_thor!Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/configmonkey_cli/application/manifest.rb', line 77

def init_thor!
  ThorHelperApp.source_root(directory)
  @thor = ThorHelperApp.new([], { pretend: app.opts[:simulation] })

  @thor.shell.class_eval do
    def say_status(status, message, log_status = true)
      return if quiet? || log_status == false
      spaces = "  " * (padding + 1)
      color  = log_status.is_a?(Symbol) ? log_status : :green

      status = status.to_s.rjust(12)
      status = set_color status, color, true if color

      if($cm_current_action_name)
        cm_action = $cm_current_action_name.to_s.rjust(12)
        cm_action = set_color cm_action, $cm_current_action_color, true if $cm_current_action_color
      end

      buffer = "#{cm_action}#{status}#{spaces}#{message}"
      buffer = "#{buffer}\n" unless buffer.end_with?("\n")

      stdout.print(buffer)
      stdout.flush
    end

    def ask q, *args, &block
      ConfigmonkeyCli::Application.instance_method(:interruptable).bind(Object.new).call do
        begin
          q = "\a" << q if ENV["THOR_ASK_BELL"]
          super(q, *args, &block)
        rescue Interrupt => ex
          Thread.main.raise(ex)
        end
      end
    end
  end
end

#no?(question, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


342
343
344
# File 'lib/configmonkey_cli/application/manifest.rb', line 342

def no? question, opts = {}
  !yes?(question, opts.merge(default: false))
end

#not_on(*hosts, &block) ⇒ Object

do block except if ‘hostname` is in *hosts



277
278
279
# File 'lib/configmonkey_cli/application/manifest.rb', line 277

def not_on *hosts, &block
  _with_constraint(:not_on, hosts.flatten.map(&:to_s), &block)
end

#on(*hosts, &block) ⇒ Object

do block only if ‘hostname` is in *hosts



272
273
274
# File 'lib/configmonkey_cli/application/manifest.rb', line 272

def on *hosts, &block
  _with_constraint(:on, hosts.flatten.map(&:to_s), &block)
end

#padded(str, *color) ⇒ Object

DSL =



235
236
237
# File 'lib/configmonkey_cli/application/manifest.rb', line 235

def padded str, *color
  "".rjust(padding, " ") << (color.any? ? c(str.to_s, *color) : str.to_s)
end

#prompt(opts = {}) ⇒ Object



247
248
249
250
251
# File 'lib/configmonkey_cli/application/manifest.rb', line 247

def prompt opts = {}
  TTY::Prompt.new(opts).tap do |prompt|
    yield(prompt) if block_given?
  end
end

#push_action(*args) ⇒ Object



207
208
209
210
211
212
213
214
# File 'lib/configmonkey_cli/application/manifest.rb', line 207

def push_action *args
  if $cm_current_action_index
    $cm_current_action_index += 1
    @actions.insert $cm_current_action_index, [@host_constraint.dup] + args
  else
    @actions.push [@host_constraint.dup] + args
  end
end

#run_action(which, simulate = nil, *args, &block) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/configmonkey_cli/application/manifest.rb', line 115

def run_action which, simulate = nil, *args, &block
  simulate = app.opts[:simulation] if simulate.nil?
  action = case which
  when String, Symbol
    "ConfigmonkeyCli::Application::ManifestAction::#{which.to_s.camelize}".constantize.new(app, self, *args, &block)
  else
    action = which
  end
  action.prepare
  simulate ? action.simulate : action.destructive
end

#say(str, *color) ⇒ Object



243
244
245
# File 'lib/configmonkey_cli/application/manifest.rb', line 243

def say str, *color
  thor.say((color.any? ? c(str.to_s, *color) : str.to_s))
end

#set_destination_root(drpath, from_manifest = true) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/configmonkey_cli/application/manifest.rb', line 216

def set_destination_root drpath, from_manifest = true
  if from_manifest
    base = File.realpath(File.expand_path(@directory))
    xpath = File.expand_path(drpath[0] == "/" ? drpath : File.join(base, drpath))
    if app.opts[:target_directory] != "/"
      thor.say_status :warn, (thor.set_color(" Dest Root: ", :magenta) << thor.set_color(xpath, :blue) << thor.set_color(" IGNORED! -o parameter will take precedence", :red)), :red
    else
      @target_directory = xpath
    end
  else
    thor.destination_root = File.realpath(File.expand_path(drpath))
  end
end

#status(name, *args) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/configmonkey_cli/application/manifest.rb', line 253

def status name, *args
  case args.length
  when 0
    raise ArgumentError("at least name and string is required")
  when 1 # status :fake, rel(@destination)
    thor.say_status name, args[0], :green
  when 2 # status :fake, :green, rel(@destination)
    thor.say_status name, args[1], args[0]
  when 3 # status :fake, :green, rel(@destination), :red
    thor.say_status name, thor.set_color(args[1], *args[2..-1]), args[0]
  end
end

#to_sObject



73
74
75
# File 'lib/configmonkey_cli/application/manifest.rb', line 73

def to_s
  "#<…::Manifest @directory=#{@directory} @actions=#{@actions.length}>"
end

#yes?(question, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/configmonkey_cli/application/manifest.rb', line 303

def yes? question, opts = {}
  opts[:quit] = true unless opts.key?(:quit)
  opts[:default] = true unless opts.key?(:default)
  opts[:padding] = @padding unless opts.key?(:padding)
  return true if app.opts[:default_yes]
  return opts[:default] if app.opts[:default_accept]
  o = "#{opts[:default] ? :Yn : :yN}"
  o << "h" if opts[:help]
  o << "q" if opts[:quit]
  q = "#{question} [#{o}]"
  c = opts[:color].presence || (opts[:default] ? :red : :yellow)
  askopts = opts.slice(:padding, :limited_to, :choose, :add_to_history).merge(color: c, use_thread: false)
  if askopts[:padding] && askopts[:padding] > 10
    qq = thor.set_color(q, askopts[:color]) if askopts[:color]
    q = "#{thor.set_color("?", :red)}  #{qq || q}"
    askopts[:padding] -= 3
  end

  app.interruptable do
    catch :return_value do
      loop {
        x = (ask(q, askopts) || (opts[:default] ? :y : :n)).to_s.downcase.strip

        if ["y", "yes", "1", "t", "true"].include?(x)
          throw :return_value, true
        elsif ["n", "no", "0", "f", "false"].include?(x)
          throw :return_value, false
        elsif ["h", "help", "?"].include?(x)
          @thor.say_status :help, "#{opts[:help]}", :cyan
        elsif ["q", "quit", "exit"].include?(x)
          raise SystemExit
        else
          @thor.say_status :warn, "choose one of y|yes|1|t|true|n|no|0|f|false#{"|q|quit|exit" if opts[:quit]}#{"|?|h|help" if opts[:help]}", :red
        end
      }
    end
  end
end