overman 0.0.1 → 0.87.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -0
  3. data/bin/foreman-runner +41 -0
  4. data/bin/overman +7 -0
  5. data/data/example/Procfile +4 -0
  6. data/data/example/Procfile.without_colon +2 -0
  7. data/data/example/error +7 -0
  8. data/data/example/log/neverdie.log +4 -0
  9. data/data/example/spawnee +14 -0
  10. data/data/example/spawner +7 -0
  11. data/data/example/ticker +14 -0
  12. data/data/example/utf8 +11 -0
  13. data/data/export/bluepill/master.pill.erb +28 -0
  14. data/data/export/daemon/master.conf.erb +14 -0
  15. data/data/export/daemon/process.conf.erb +8 -0
  16. data/data/export/daemon/process_master.conf.erb +2 -0
  17. data/data/export/launchd/launchd.plist.erb +33 -0
  18. data/data/export/runit/log/run.erb +7 -0
  19. data/data/export/runit/run.erb +4 -0
  20. data/data/export/supervisord/app.conf.erb +31 -0
  21. data/data/export/systemd/master.target.erb +5 -0
  22. data/data/export/systemd/process.service.erb +21 -0
  23. data/data/export/upstart/master.conf.erb +2 -0
  24. data/data/export/upstart/process.conf.erb +15 -0
  25. data/data/export/upstart/process_master.conf.erb +2 -0
  26. data/lib/foreman/cli.rb +162 -0
  27. data/lib/foreman/distribution.rb +9 -0
  28. data/lib/foreman/engine/cli.rb +105 -0
  29. data/lib/foreman/engine.rb +494 -0
  30. data/lib/foreman/env.rb +29 -0
  31. data/lib/foreman/export/base.rb +171 -0
  32. data/lib/foreman/export/bluepill.rb +12 -0
  33. data/lib/foreman/export/daemon.rb +28 -0
  34. data/lib/foreman/export/inittab.rb +42 -0
  35. data/lib/foreman/export/launchd.rb +22 -0
  36. data/lib/foreman/export/runit.rb +34 -0
  37. data/lib/foreman/export/supervisord.rb +16 -0
  38. data/lib/foreman/export/systemd.rb +34 -0
  39. data/lib/foreman/export/upstart.rb +46 -0
  40. data/lib/foreman/export.rb +36 -0
  41. data/lib/foreman/helpers.rb +45 -0
  42. data/lib/foreman/process.rb +81 -0
  43. data/lib/foreman/procfile.rb +94 -0
  44. data/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  45. data/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  46. data/lib/foreman/vendor/thor/lib/thor/actions/directory.rb +118 -0
  47. data/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  48. data/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb +327 -0
  49. data/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb +103 -0
  50. data/lib/foreman/vendor/thor/lib/thor/actions.rb +318 -0
  51. data/lib/foreman/vendor/thor/lib/thor/base.rb +656 -0
  52. data/lib/foreman/vendor/thor/lib/thor/command.rb +133 -0
  53. data/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +85 -0
  54. data/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  55. data/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
  56. data/lib/foreman/vendor/thor/lib/thor/error.rb +32 -0
  57. data/lib/foreman/vendor/thor/lib/thor/group.rb +281 -0
  58. data/lib/foreman/vendor/thor/lib/thor/invocation.rb +177 -0
  59. data/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  60. data/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  61. data/lib/foreman/vendor/thor/lib/thor/line_editor.rb +17 -0
  62. data/lib/foreman/vendor/thor/lib/thor/parser/argument.rb +70 -0
  63. data/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  64. data/lib/foreman/vendor/thor/lib/thor/parser/option.rb +146 -0
  65. data/lib/foreman/vendor/thor/lib/thor/parser/options.rb +220 -0
  66. data/lib/foreman/vendor/thor/lib/thor/parser.rb +4 -0
  67. data/lib/foreman/vendor/thor/lib/thor/rake_compat.rb +71 -0
  68. data/lib/foreman/vendor/thor/lib/thor/runner.rb +322 -0
  69. data/lib/foreman/vendor/thor/lib/thor/shell/basic.rb +436 -0
  70. data/lib/foreman/vendor/thor/lib/thor/shell/color.rb +149 -0
  71. data/lib/foreman/vendor/thor/lib/thor/shell/html.rb +126 -0
  72. data/lib/foreman/vendor/thor/lib/thor/shell.rb +81 -0
  73. data/lib/foreman/vendor/thor/lib/thor/util.rb +268 -0
  74. data/lib/foreman/vendor/thor/lib/thor/version.rb +3 -0
  75. data/lib/foreman/vendor/thor/lib/thor.rb +492 -0
  76. data/lib/foreman/version.rb +5 -0
  77. data/lib/foreman.rb +17 -0
  78. data/man/overman.1 +284 -0
  79. data/spec/foreman/cli_spec.rb +111 -0
  80. data/spec/foreman/engine_spec.rb +114 -0
  81. data/spec/foreman/export/base_spec.rb +19 -0
  82. data/spec/foreman/export/bluepill_spec.rb +37 -0
  83. data/spec/foreman/export/daemon_spec.rb +97 -0
  84. data/spec/foreman/export/inittab_spec.rb +40 -0
  85. data/spec/foreman/export/launchd_spec.rb +31 -0
  86. data/spec/foreman/export/runit_spec.rb +36 -0
  87. data/spec/foreman/export/supervisord_spec.rb +38 -0
  88. data/spec/foreman/export/systemd_spec.rb +155 -0
  89. data/spec/foreman/export/upstart_spec.rb +118 -0
  90. data/spec/foreman/export_spec.rb +24 -0
  91. data/spec/foreman/helpers_spec.rb +26 -0
  92. data/spec/foreman/process_spec.rb +71 -0
  93. data/spec/foreman/procfile_spec.rb +57 -0
  94. data/spec/foreman_spec.rb +16 -0
  95. data/spec/helper_spec.rb +19 -0
  96. data/spec/resources/Procfile +5 -0
  97. data/spec/resources/Procfile.bad +2 -0
  98. data/spec/resources/bin/echo +2 -0
  99. data/spec/resources/bin/env +2 -0
  100. data/spec/resources/bin/test +2 -0
  101. data/spec/resources/bin/utf8 +2 -0
  102. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  103. data/spec/resources/export/bluepill/app.pill +81 -0
  104. data/spec/resources/export/daemon/app-alpha-1.conf +7 -0
  105. data/spec/resources/export/daemon/app-alpha-2.conf +7 -0
  106. data/spec/resources/export/daemon/app-alpha.conf +2 -0
  107. data/spec/resources/export/daemon/app-bravo-1.conf +7 -0
  108. data/spec/resources/export/daemon/app-bravo.conf +2 -0
  109. data/spec/resources/export/daemon/app.conf +14 -0
  110. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  111. data/spec/resources/export/inittab/inittab.default +6 -0
  112. data/spec/resources/export/launchd/launchd-a.default +29 -0
  113. data/spec/resources/export/launchd/launchd-b.default +29 -0
  114. data/spec/resources/export/launchd/launchd-c.default +30 -0
  115. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  116. data/spec/resources/export/runit/app-alpha-1/run +4 -0
  117. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  118. data/spec/resources/export/runit/app-alpha-2/run +4 -0
  119. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  120. data/spec/resources/export/runit/app-bravo-1/run +4 -0
  121. data/spec/resources/export/supervisord/app-alpha-1.conf +42 -0
  122. data/spec/resources/export/supervisord/app-alpha-2.conf +22 -0
  123. data/spec/resources/export/systemd/app-alpha.1.service +18 -0
  124. data/spec/resources/export/systemd/app-alpha.2.service +18 -0
  125. data/spec/resources/export/systemd/app-alpha.target +2 -0
  126. data/spec/resources/export/systemd/app-bravo.1.service +18 -0
  127. data/spec/resources/export/systemd/app-bravo.target +2 -0
  128. data/spec/resources/export/systemd/app.target +5 -0
  129. data/spec/resources/export/upstart/app-alpha-1.conf +11 -0
  130. data/spec/resources/export/upstart/app-alpha-2.conf +11 -0
  131. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  132. data/spec/resources/export/upstart/app-bravo-1.conf +11 -0
  133. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  134. data/spec/resources/export/upstart/app.conf +2 -0
  135. data/spec/spec_helper.rb +177 -0
  136. metadata +147 -16
  137. data/lib/overman/version.rb +0 -5
  138. data/lib/overman.rb +0 -1
@@ -0,0 +1,436 @@
1
+ require "tempfile"
2
+ require "io/console" if RUBY_VERSION > "1.9.2"
3
+
4
+ class Foreman::Thor
5
+ module Shell
6
+ class Basic
7
+ attr_accessor :base
8
+ attr_reader :padding
9
+
10
+ # Initialize base, mute and padding to nil.
11
+ #
12
+ def initialize #:nodoc:
13
+ @base = nil
14
+ @mute = false
15
+ @padding = 0
16
+ @always_force = false
17
+ end
18
+
19
+ # Mute everything that's inside given block
20
+ #
21
+ def mute
22
+ @mute = true
23
+ yield
24
+ ensure
25
+ @mute = false
26
+ end
27
+
28
+ # Check if base is muted
29
+ #
30
+ def mute?
31
+ @mute
32
+ end
33
+
34
+ # Sets the output padding, not allowing less than zero values.
35
+ #
36
+ def padding=(value)
37
+ @padding = [0, value].max
38
+ end
39
+
40
+ # Sets the output padding while executing a block and resets it.
41
+ #
42
+ def indent(count = 1)
43
+ orig_padding = padding
44
+ self.padding = padding + count
45
+ yield
46
+ self.padding = orig_padding
47
+ end
48
+
49
+ # Asks something to the user and receives a response.
50
+ #
51
+ # If asked to limit the correct responses, you can pass in an
52
+ # array of acceptable answers. If one of those is not supplied,
53
+ # they will be shown a message stating that one of those answers
54
+ # must be given and re-asked the question.
55
+ #
56
+ # If asking for sensitive information, the :echo option can be set
57
+ # to false to mask user input from $stdin.
58
+ #
59
+ # If the required input is a path, then set the path option to
60
+ # true. This will enable tab completion for file paths relative
61
+ # to the current working directory on systems that support
62
+ # Readline.
63
+ #
64
+ # ==== Example
65
+ # ask("What is your name?")
66
+ #
67
+ # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
68
+ #
69
+ # ask("What is your password?", :echo => false)
70
+ #
71
+ # ask("Where should the file be saved?", :path => true)
72
+ #
73
+ def ask(statement, *args)
74
+ options = args.last.is_a?(Hash) ? args.pop : {}
75
+ color = args.first
76
+
77
+ if options[:limited_to]
78
+ ask_filtered(statement, color, options)
79
+ else
80
+ ask_simply(statement, color, options)
81
+ end
82
+ end
83
+
84
+ # Say (print) something to the user. If the sentence ends with a whitespace
85
+ # or tab character, a new line is not appended (print + flush). Otherwise
86
+ # are passed straight to puts (behavior got from Highline).
87
+ #
88
+ # ==== Example
89
+ # say("I know you knew that.")
90
+ #
91
+ def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
92
+ buffer = prepare_message(message, *color)
93
+ buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
94
+
95
+ stdout.print(buffer)
96
+ stdout.flush
97
+ end
98
+
99
+ # Say a status with the given color and appends the message. Since this
100
+ # method is used frequently by actions, it allows nil or false to be given
101
+ # in log_status, avoiding the message from being shown. If a Symbol is
102
+ # given in log_status, it's used as the color.
103
+ #
104
+ def say_status(status, message, log_status = true)
105
+ return if quiet? || log_status == false
106
+ spaces = " " * (padding + 1)
107
+ color = log_status.is_a?(Symbol) ? log_status : :green
108
+
109
+ status = status.to_s.rjust(12)
110
+ status = set_color status, color, true if color
111
+
112
+ buffer = "#{status}#{spaces}#{message}"
113
+ buffer << "\n" unless buffer.end_with?("\n")
114
+
115
+ stdout.print(buffer)
116
+ stdout.flush
117
+ end
118
+
119
+ # Make a question the to user and returns true if the user replies "y" or
120
+ # "yes".
121
+ #
122
+ def yes?(statement, color = nil)
123
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
124
+ end
125
+
126
+ # Make a question the to user and returns true if the user replies "n" or
127
+ # "no".
128
+ #
129
+ def no?(statement, color = nil)
130
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:no))
131
+ end
132
+
133
+ # Prints values in columns
134
+ #
135
+ # ==== Parameters
136
+ # Array[String, String, ...]
137
+ #
138
+ def print_in_columns(array)
139
+ return if array.empty?
140
+ colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
141
+ array.each_with_index do |value, index|
142
+ # Don't output trailing spaces when printing the last column
143
+ if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
144
+ stdout.puts value
145
+ else
146
+ stdout.printf("%-#{colwidth}s", value)
147
+ end
148
+ end
149
+ end
150
+
151
+ # Prints a table.
152
+ #
153
+ # ==== Parameters
154
+ # Array[Array[String, String, ...]]
155
+ #
156
+ # ==== Options
157
+ # indent<Integer>:: Indent the first column by indent value.
158
+ # colwidth<Integer>:: Force the first column to colwidth spaces wide.
159
+ #
160
+ def print_table(array, options = {}) # rubocop:disable MethodLength
161
+ return if array.empty?
162
+
163
+ formats = []
164
+ indent = options[:indent].to_i
165
+ colwidth = options[:colwidth]
166
+ options[:truncate] = terminal_width if options[:truncate] == true
167
+
168
+ formats << "%-#{colwidth + 2}s" if colwidth
169
+ start = colwidth ? 1 : 0
170
+
171
+ colcount = array.max { |a, b| a.size <=> b.size }.size
172
+
173
+ maximas = []
174
+
175
+ start.upto(colcount - 1) do |index|
176
+ maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
177
+ maximas << maxima
178
+ formats << if index == colcount - 1
179
+ # Don't output 2 trailing spaces when printing the last column
180
+ "%-s"
181
+ else
182
+ "%-#{maxima + 2}s"
183
+ end
184
+ end
185
+
186
+ formats[0] = formats[0].insert(0, " " * indent)
187
+ formats << "%s"
188
+
189
+ array.each do |row|
190
+ sentence = ""
191
+
192
+ row.each_with_index do |column, index|
193
+ maxima = maximas[index]
194
+
195
+ f = if column.is_a?(Numeric)
196
+ if index == row.size - 1
197
+ # Don't output 2 trailing spaces when printing the last column
198
+ "%#{maxima}s"
199
+ else
200
+ "%#{maxima}s "
201
+ end
202
+ else
203
+ formats[index]
204
+ end
205
+ sentence << f % column.to_s
206
+ end
207
+
208
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
209
+ stdout.puts sentence
210
+ end
211
+ end
212
+
213
+ # Prints a long string, word-wrapping the text to the current width of the
214
+ # terminal display. Ideal for printing heredocs.
215
+ #
216
+ # ==== Parameters
217
+ # String
218
+ #
219
+ # ==== Options
220
+ # indent<Integer>:: Indent each line of the printed paragraph by indent value.
221
+ #
222
+ def print_wrapped(message, options = {})
223
+ indent = options[:indent] || 0
224
+ width = terminal_width - indent
225
+ paras = message.split("\n\n")
226
+
227
+ paras.map! do |unwrapped|
228
+ unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
229
+ end
230
+
231
+ paras.each do |para|
232
+ para.split("\n").each do |line|
233
+ stdout.puts line.insert(0, " " * indent)
234
+ end
235
+ stdout.puts unless para == paras.last
236
+ end
237
+ end
238
+
239
+ # Deals with file collision and returns true if the file should be
240
+ # overwritten and false otherwise. If a block is given, it uses the block
241
+ # response as the content for the diff.
242
+ #
243
+ # ==== Parameters
244
+ # destination<String>:: the destination file to solve conflicts
245
+ # block<Proc>:: an optional block that returns the value to be used in diff
246
+ #
247
+ def file_collision(destination)
248
+ return true if @always_force
249
+ options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
250
+
251
+ loop do
252
+ answer = ask(
253
+ %[Overwrite #{destination}? (enter "h" for help) #{options}],
254
+ :add_to_history => false
255
+ )
256
+
257
+ case answer
258
+ when is?(:yes), is?(:force), ""
259
+ return true
260
+ when is?(:no), is?(:skip)
261
+ return false
262
+ when is?(:always)
263
+ return @always_force = true
264
+ when is?(:quit)
265
+ say "Aborting..."
266
+ raise SystemExit
267
+ when is?(:diff)
268
+ show_diff(destination, yield) if block_given?
269
+ say "Retrying..."
270
+ else
271
+ say file_collision_help
272
+ end
273
+ end
274
+ end
275
+
276
+ # This code was copied from Rake, available under MIT-LICENSE
277
+ # Copyright (c) 2003, 2004 Jim Weirich
278
+ def terminal_width
279
+ result = if ENV["THOR_COLUMNS"]
280
+ ENV["THOR_COLUMNS"].to_i
281
+ else
282
+ unix? ? dynamic_width : 80
283
+ end
284
+ result < 10 ? 80 : result
285
+ rescue
286
+ 80
287
+ end
288
+
289
+ # Called if something goes wrong during the execution. This is used by Foreman::Thor
290
+ # internally and should not be used inside your scripts. If something went
291
+ # wrong, you can always raise an exception. If you raise a Foreman::Thor::Error, it
292
+ # will be rescued and wrapped in the method below.
293
+ #
294
+ def error(statement)
295
+ stderr.puts statement
296
+ end
297
+
298
+ # Apply color to the given string with optional bold. Disabled in the
299
+ # Foreman::Thor::Shell::Basic class.
300
+ #
301
+ def set_color(string, *) #:nodoc:
302
+ string
303
+ end
304
+
305
+ protected
306
+
307
+ def prepare_message(message, *color)
308
+ spaces = " " * padding
309
+ spaces + set_color(message.to_s, *color)
310
+ end
311
+
312
+ def can_display_colors?
313
+ false
314
+ end
315
+
316
+ def lookup_color(color)
317
+ return color unless color.is_a?(Symbol)
318
+ self.class.const_get(color.to_s.upcase)
319
+ end
320
+
321
+ def stdout
322
+ $stdout
323
+ end
324
+
325
+ def stderr
326
+ $stderr
327
+ end
328
+
329
+ def is?(value) #:nodoc:
330
+ value = value.to_s
331
+
332
+ if value.size == 1
333
+ /\A#{value}\z/i
334
+ else
335
+ /\A(#{value}|#{value[0, 1]})\z/i
336
+ end
337
+ end
338
+
339
+ def file_collision_help #:nodoc:
340
+ <<-HELP
341
+ Y - yes, overwrite
342
+ n - no, do not overwrite
343
+ a - all, overwrite this and all others
344
+ q - quit, abort
345
+ d - diff, show the differences between the old and the new
346
+ h - help, show this help
347
+ HELP
348
+ end
349
+
350
+ def show_diff(destination, content) #:nodoc:
351
+ diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
352
+
353
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
354
+ temp.write content
355
+ temp.rewind
356
+ system %(#{diff_cmd} "#{destination}" "#{temp.path}")
357
+ end
358
+ end
359
+
360
+ def quiet? #:nodoc:
361
+ mute? || (base && base.options[:quiet])
362
+ end
363
+
364
+ # Calculate the dynamic width of the terminal
365
+ def dynamic_width
366
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
367
+ end
368
+
369
+ def dynamic_width_stty
370
+ `stty size 2>/dev/null`.split[1].to_i
371
+ end
372
+
373
+ def dynamic_width_tput
374
+ `tput cols 2>/dev/null`.to_i
375
+ end
376
+
377
+ def unix?
378
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
379
+ end
380
+
381
+ def truncate(string, width)
382
+ as_unicode do
383
+ chars = string.chars.to_a
384
+ if chars.length <= width
385
+ chars.join
386
+ else
387
+ chars[0, width - 3].join + "..."
388
+ end
389
+ end
390
+ end
391
+
392
+ if "".respond_to?(:encode)
393
+ def as_unicode
394
+ yield
395
+ end
396
+ else
397
+ def as_unicode
398
+ old = $KCODE
399
+ $KCODE = "U"
400
+ yield
401
+ ensure
402
+ $KCODE = old
403
+ end
404
+ end
405
+
406
+ def ask_simply(statement, color, options)
407
+ default = options[:default]
408
+ message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
409
+ message = prepare_message(message, *color)
410
+ result = Foreman::Thor::LineEditor.readline(message, options)
411
+
412
+ return unless result
413
+
414
+ result.strip!
415
+
416
+ if default && result == ""
417
+ default
418
+ else
419
+ result
420
+ end
421
+ end
422
+
423
+ def ask_filtered(statement, color, options)
424
+ answer_set = options[:limited_to]
425
+ correct_answer = nil
426
+ until correct_answer
427
+ answers = answer_set.join(", ")
428
+ answer = ask_simply("#{statement} [#{answers}]", color, options)
429
+ correct_answer = answer_set.include?(answer) ? answer : nil
430
+ say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
431
+ end
432
+ correct_answer
433
+ end
434
+ end
435
+ end
436
+ end
@@ -0,0 +1,149 @@
1
+ require "foreman/vendor/thor/lib/thor/shell/basic"
2
+
3
+ class Foreman::Thor
4
+ module Shell
5
+ # Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check
6
+ # Foreman::Thor::Shell::Basic to see all available methods.
7
+ #
8
+ class Color < Basic
9
+ # Embed in a String to clear all previous ANSI sequences.
10
+ CLEAR = "\e[0m"
11
+ # The start of an ANSI bold sequence.
12
+ BOLD = "\e[1m"
13
+
14
+ # Set the terminal's foreground ANSI color to black.
15
+ BLACK = "\e[30m"
16
+ # Set the terminal's foreground ANSI color to red.
17
+ RED = "\e[31m"
18
+ # Set the terminal's foreground ANSI color to green.
19
+ GREEN = "\e[32m"
20
+ # Set the terminal's foreground ANSI color to yellow.
21
+ YELLOW = "\e[33m"
22
+ # Set the terminal's foreground ANSI color to blue.
23
+ BLUE = "\e[34m"
24
+ # Set the terminal's foreground ANSI color to magenta.
25
+ MAGENTA = "\e[35m"
26
+ # Set the terminal's foreground ANSI color to cyan.
27
+ CYAN = "\e[36m"
28
+ # Set the terminal's foreground ANSI color to white.
29
+ WHITE = "\e[37m"
30
+
31
+ # Set the terminal's background ANSI color to black.
32
+ ON_BLACK = "\e[40m"
33
+ # Set the terminal's background ANSI color to red.
34
+ ON_RED = "\e[41m"
35
+ # Set the terminal's background ANSI color to green.
36
+ ON_GREEN = "\e[42m"
37
+ # Set the terminal's background ANSI color to yellow.
38
+ ON_YELLOW = "\e[43m"
39
+ # Set the terminal's background ANSI color to blue.
40
+ ON_BLUE = "\e[44m"
41
+ # Set the terminal's background ANSI color to magenta.
42
+ ON_MAGENTA = "\e[45m"
43
+ # Set the terminal's background ANSI color to cyan.
44
+ ON_CYAN = "\e[46m"
45
+ # Set the terminal's background ANSI color to white.
46
+ ON_WHITE = "\e[47m"
47
+
48
+ # Set color by using a string or one of the defined constants. If a third
49
+ # option is set to true, it also adds bold to the string. This is based
50
+ # on Highline implementation and it automatically appends CLEAR to the end
51
+ # of the returned String.
52
+ #
53
+ # Pass foreground, background and bold options to this method as
54
+ # symbols.
55
+ #
56
+ # Example:
57
+ #
58
+ # set_color "Hi!", :red, :on_white, :bold
59
+ #
60
+ # The available colors are:
61
+ #
62
+ # :bold
63
+ # :black
64
+ # :red
65
+ # :green
66
+ # :yellow
67
+ # :blue
68
+ # :magenta
69
+ # :cyan
70
+ # :white
71
+ # :on_black
72
+ # :on_red
73
+ # :on_green
74
+ # :on_yellow
75
+ # :on_blue
76
+ # :on_magenta
77
+ # :on_cyan
78
+ # :on_white
79
+ def set_color(string, *colors)
80
+ if colors.compact.empty? || !can_display_colors?
81
+ string
82
+ elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
83
+ ansi_colors = colors.map { |color| lookup_color(color) }
84
+ "#{ansi_colors.join}#{string}#{CLEAR}"
85
+ else
86
+ # The old API was `set_color(color, bold=boolean)`. We
87
+ # continue to support the old API because you should never
88
+ # break old APIs unnecessarily :P
89
+ foreground, bold = colors
90
+ foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol)
91
+
92
+ bold = bold ? BOLD : ""
93
+ "#{bold}#{foreground}#{string}#{CLEAR}"
94
+ end
95
+ end
96
+
97
+ protected
98
+
99
+ def can_display_colors?
100
+ stdout.tty?
101
+ end
102
+
103
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
104
+ # available.
105
+ #
106
+ def show_diff(destination, content) #:nodoc:
107
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
108
+ actual = File.binread(destination).to_s.split("\n")
109
+ content = content.to_s.split("\n")
110
+
111
+ Diff::LCS.sdiff(actual, content).each do |diff|
112
+ output_diff_line(diff)
113
+ end
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ def output_diff_line(diff) #:nodoc:
120
+ case diff.action
121
+ when "-"
122
+ say "- #{diff.old_element.chomp}", :red, true
123
+ when "+"
124
+ say "+ #{diff.new_element.chomp}", :green, true
125
+ when "!"
126
+ say "- #{diff.old_element.chomp}", :red, true
127
+ say "+ #{diff.new_element.chomp}", :green, true
128
+ else
129
+ say " #{diff.old_element.chomp}", nil, true
130
+ end
131
+ end
132
+
133
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
134
+ # for diff.
135
+ #
136
+ def diff_lcs_loaded? #:nodoc:
137
+ return true if defined?(Diff::LCS)
138
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
139
+
140
+ @diff_lcs_loaded = begin
141
+ require "diff/lcs"
142
+ true
143
+ rescue LoadError
144
+ false
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,126 @@
1
+ require "foreman/vendor/thor/lib/thor/shell/basic"
2
+
3
+ class Foreman::Thor
4
+ module Shell
5
+ # Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check
6
+ # Foreman::Thor::Shell::Basic to see all available methods.
7
+ #
8
+ class HTML < Basic
9
+ # The start of an HTML bold sequence.
10
+ BOLD = "font-weight: bold"
11
+
12
+ # Set the terminal's foreground HTML color to black.
13
+ BLACK = "color: black"
14
+ # Set the terminal's foreground HTML color to red.
15
+ RED = "color: red"
16
+ # Set the terminal's foreground HTML color to green.
17
+ GREEN = "color: green"
18
+ # Set the terminal's foreground HTML color to yellow.
19
+ YELLOW = "color: yellow"
20
+ # Set the terminal's foreground HTML color to blue.
21
+ BLUE = "color: blue"
22
+ # Set the terminal's foreground HTML color to magenta.
23
+ MAGENTA = "color: magenta"
24
+ # Set the terminal's foreground HTML color to cyan.
25
+ CYAN = "color: cyan"
26
+ # Set the terminal's foreground HTML color to white.
27
+ WHITE = "color: white"
28
+
29
+ # Set the terminal's background HTML color to black.
30
+ ON_BLACK = "background-color: black"
31
+ # Set the terminal's background HTML color to red.
32
+ ON_RED = "background-color: red"
33
+ # Set the terminal's background HTML color to green.
34
+ ON_GREEN = "background-color: green"
35
+ # Set the terminal's background HTML color to yellow.
36
+ ON_YELLOW = "background-color: yellow"
37
+ # Set the terminal's background HTML color to blue.
38
+ ON_BLUE = "background-color: blue"
39
+ # Set the terminal's background HTML color to magenta.
40
+ ON_MAGENTA = "background-color: magenta"
41
+ # Set the terminal's background HTML color to cyan.
42
+ ON_CYAN = "background-color: cyan"
43
+ # Set the terminal's background HTML color to white.
44
+ ON_WHITE = "background-color: white"
45
+
46
+ # Set color by using a string or one of the defined constants. If a third
47
+ # option is set to true, it also adds bold to the string. This is based
48
+ # on Highline implementation and it automatically appends CLEAR to the end
49
+ # of the returned String.
50
+ #
51
+ def set_color(string, *colors)
52
+ if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
53
+ html_colors = colors.map { |color| lookup_color(color) }
54
+ "<span style=\"#{html_colors.join('; ')};\">#{string}</span>"
55
+ else
56
+ color, bold = colors
57
+ html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
58
+ styles = [html_color]
59
+ styles << BOLD if bold
60
+ "<span style=\"#{styles.join('; ')};\">#{string}</span>"
61
+ end
62
+ end
63
+
64
+ # Ask something to the user and receives a response.
65
+ #
66
+ # ==== Example
67
+ # ask("What is your name?")
68
+ #
69
+ # TODO: Implement #ask for Foreman::Thor::Shell::HTML
70
+ def ask(statement, color = nil)
71
+ raise NotImplementedError, "Implement #ask for Foreman::Thor::Shell::HTML"
72
+ end
73
+
74
+ protected
75
+
76
+ def can_display_colors?
77
+ true
78
+ end
79
+
80
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
81
+ # available.
82
+ #
83
+ def show_diff(destination, content) #:nodoc:
84
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
85
+ actual = File.binread(destination).to_s.split("\n")
86
+ content = content.to_s.split("\n")
87
+
88
+ Diff::LCS.sdiff(actual, content).each do |diff|
89
+ output_diff_line(diff)
90
+ end
91
+ else
92
+ super
93
+ end
94
+ end
95
+
96
+ def output_diff_line(diff) #:nodoc:
97
+ case diff.action
98
+ when "-"
99
+ say "- #{diff.old_element.chomp}", :red, true
100
+ when "+"
101
+ say "+ #{diff.new_element.chomp}", :green, true
102
+ when "!"
103
+ say "- #{diff.old_element.chomp}", :red, true
104
+ say "+ #{diff.new_element.chomp}", :green, true
105
+ else
106
+ say " #{diff.old_element.chomp}", nil, true
107
+ end
108
+ end
109
+
110
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
111
+ # for diff.
112
+ #
113
+ def diff_lcs_loaded? #:nodoc:
114
+ return true if defined?(Diff::LCS)
115
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
116
+
117
+ @diff_lcs_loaded = begin
118
+ require "diff/lcs"
119
+ true
120
+ rescue LoadError
121
+ false
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end