db_sucker 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +193 -0
  7. data/Rakefile +1 -0
  8. data/VERSION +1 -0
  9. data/bin/db_sucker +12 -0
  10. data/bin/db_sucker.sh +14 -0
  11. data/db_sucker.gemspec +29 -0
  12. data/doc/config_example.rb +53 -0
  13. data/doc/container_example.yml +150 -0
  14. data/lib/db_sucker/adapters/mysql2.rb +103 -0
  15. data/lib/db_sucker/application/colorize.rb +28 -0
  16. data/lib/db_sucker/application/container/accessors.rb +60 -0
  17. data/lib/db_sucker/application/container/ssh.rb +225 -0
  18. data/lib/db_sucker/application/container/validations.rb +53 -0
  19. data/lib/db_sucker/application/container/variation/accessors.rb +45 -0
  20. data/lib/db_sucker/application/container/variation/helpers.rb +21 -0
  21. data/lib/db_sucker/application/container/variation/worker_api.rb +65 -0
  22. data/lib/db_sucker/application/container/variation.rb +60 -0
  23. data/lib/db_sucker/application/container.rb +70 -0
  24. data/lib/db_sucker/application/container_collection.rb +47 -0
  25. data/lib/db_sucker/application/core.rb +222 -0
  26. data/lib/db_sucker/application/dispatch.rb +364 -0
  27. data/lib/db_sucker/application/evented_resultset.rb +149 -0
  28. data/lib/db_sucker/application/fake_channel.rb +22 -0
  29. data/lib/db_sucker/application/output_helper.rb +197 -0
  30. data/lib/db_sucker/application/sklaven_treiber/log_spool.rb +57 -0
  31. data/lib/db_sucker/application/sklaven_treiber/worker/accessors.rb +105 -0
  32. data/lib/db_sucker/application/sklaven_treiber/worker/core.rb +168 -0
  33. data/lib/db_sucker/application/sklaven_treiber/worker/helpers.rb +144 -0
  34. data/lib/db_sucker/application/sklaven_treiber/worker/io/base.rb +240 -0
  35. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_copy.rb +81 -0
  36. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_gunzip.rb +58 -0
  37. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_import_sql.rb +80 -0
  38. data/lib/db_sucker/application/sklaven_treiber/worker/io/file_shasum.rb +49 -0
  39. data/lib/db_sucker/application/sklaven_treiber/worker/io/pv_wrapper.rb +73 -0
  40. data/lib/db_sucker/application/sklaven_treiber/worker/io/sftp_download.rb +57 -0
  41. data/lib/db_sucker/application/sklaven_treiber/worker/io/throughput.rb +219 -0
  42. data/lib/db_sucker/application/sklaven_treiber/worker/routines.rb +313 -0
  43. data/lib/db_sucker/application/sklaven_treiber/worker.rb +48 -0
  44. data/lib/db_sucker/application/sklaven_treiber.rb +281 -0
  45. data/lib/db_sucker/application/slot_pool.rb +137 -0
  46. data/lib/db_sucker/application/tie.rb +25 -0
  47. data/lib/db_sucker/application/window/core.rb +185 -0
  48. data/lib/db_sucker/application/window/dialog.rb +142 -0
  49. data/lib/db_sucker/application/window/keypad/core.rb +85 -0
  50. data/lib/db_sucker/application/window/keypad.rb +174 -0
  51. data/lib/db_sucker/application/window/prompt.rb +124 -0
  52. data/lib/db_sucker/application/window.rb +329 -0
  53. data/lib/db_sucker/application.rb +168 -0
  54. data/lib/db_sucker/patches/beta-warning.rb +374 -0
  55. data/lib/db_sucker/patches/developer.rb +29 -0
  56. data/lib/db_sucker/patches/net-sftp.rb +20 -0
  57. data/lib/db_sucker/patches/thread-count.rb +30 -0
  58. data/lib/db_sucker/version.rb +4 -0
  59. data/lib/db_sucker.rb +81 -0
  60. metadata +217 -0
@@ -0,0 +1,329 @@
1
+ module DbSucker
2
+ class Application
3
+ class Window
4
+ include Core
5
+ include Curses
6
+ COLOR_GRAY = 8
7
+ COL1 = 20
8
+ COL2 = 25
9
+ COL3 = 20
10
+ OutputHelper.hook(self)
11
+
12
+ attr_reader :app, :sklaventreiber, :keypad, :tick, :spinner_frames
13
+ attr_accessor :view, :x_offset, :force_kill
14
+
15
+ def initialize app, sklaventreiber
16
+ @app = app
17
+ @keypad = Keypad.new(self)
18
+ @sklaventreiber = sklaventreiber
19
+ @monitor = Monitor.new
20
+ @line = 0
21
+ @tick = 0
22
+ @view = :status
23
+ @force_kill = false
24
+ choose_spinner
25
+ end
26
+
27
+ def refresh_screen
28
+ Thread.current[:last_render_duration] = rt = Benchmark.realtime do
29
+ @monitor.synchronize do
30
+ @tick += 1
31
+ update { __send__(:"_view_#{@view}") }
32
+ end
33
+ end
34
+ if rt > 0.020
35
+ @app.warning "window render took: #{"%.6f" % rt}"
36
+ else
37
+ @app.debug "window render took: #{"%.6f" % rt}", 125
38
+ end
39
+ rescue StandardError => ex
40
+ @app.notify_exception("DbSucker::Window encountered an render error on tick ##{@tick}", ex)
41
+
42
+ update do
43
+ next_line
44
+ red "RenderError occured!"
45
+ next_line
46
+ red "#{ex.class}: #{ex.message}"
47
+ ex.backtrace.each do |l|
48
+ next_line
49
+ red(" #{l}")
50
+ end
51
+ end
52
+ Thread.current.wait(1)
53
+ end
54
+
55
+ def _view_help
56
+ _render_status(threads: false, started: false, trxid: false, database: false)
57
+
58
+ #next_line; next_line
59
+ #magenta "db_sucker"
60
+ #blue " #{VERSION}"
61
+ #yellow " – "
62
+ #cyan "(C) 2016-#{Time.current.year} Sven Pachnit (bmonkeys.net)"
63
+
64
+ #next_line
65
+ #gray "Released under the MIT license."
66
+
67
+ next_line; next_line
68
+ blue "Key Bindings (case sensitive):"
69
+ next_line
70
+
71
+ Keypad::HELP_INFO[:key_bindings].each do |key, desc|
72
+ next_line
73
+ magenta " #{key}"
74
+ yellow " #{desc}"
75
+ end
76
+
77
+ next_line; next_line
78
+ blue "Main prompt commands:"
79
+ next_line
80
+
81
+ # only build output once and save it in memory
82
+ @_view_help_memory ||= begin
83
+ mchp = Keypad::HELP_INFO[:main_commands].map do |aliases, options, desc|
84
+ [].tap do |result|
85
+ result << [].tap{|r|
86
+ aliases.each do |al|
87
+ r << (al.is_a?(Array) ? al.join("").length + 4 : al.length + 2)
88
+ end
89
+ }.sum
90
+
91
+ result << [].tap{|r|
92
+ options.each do |type, name|
93
+ r << (type == :mandatory ? "<#{[*name] * "|"}> " : "[#{[*name] * "|"}] ").length
94
+ end
95
+ }.sum
96
+ end
97
+ end
98
+ columns = { aliases: mchp.map(&:first).max, options: mchp.map(&:second).max }
99
+
100
+ # render commands
101
+ [].tap do |instruct|
102
+ Keypad::HELP_INFO[:main_commands].each do |aliases, options, desc|
103
+ # aliases
104
+ instruct << [:next_line]
105
+ instruct << [:addstr, " "]
106
+ cl = 0
107
+ aliases.each do |al|
108
+ instruct << [:magenta, ":"]
109
+ if al.is_a?(Array)
110
+ instruct << [:blue, "#{al[0]}"]
111
+ instruct << [:gray, "("]
112
+ instruct << [:cyan, "#{al[1]}"]
113
+ cl += al.join("").length + 4
114
+ instruct << [:gray, ") "]
115
+ else
116
+ cl += al.length + 2
117
+ instruct << [:blue, "#{al} "]
118
+ end
119
+ end
120
+ instruct << [:addstr, "".ljust(columns[:aliases] - cl, " ")]
121
+
122
+ # options
123
+ cl = 0
124
+ instruct << [:addstr, " "]
125
+ options.each do |type, name|
126
+ if type == :mandatory
127
+ instruct << [:red, "<"]
128
+ [*name].each_with_index do |n, i|
129
+ instruct << [:gray, "|"] if i > 0
130
+ instruct << [:blue, n]
131
+ end
132
+ instruct << [:red, "> "]
133
+ else
134
+ instruct << [:yellow, "["]
135
+ [*name].each_with_index do |n, i|
136
+ instruct << [:gray, "|"] if i > 0
137
+ instruct << [:cyan, n]
138
+ end
139
+ instruct << [:yellow, "] "]
140
+ end
141
+ cl += [*name].join("").length + 3 + [*name].length - 1
142
+ end
143
+ instruct << [:addstr, "".ljust(columns[:options] - cl, " ")]
144
+
145
+ instruct << [:yellow, " #{desc}"]
146
+ end
147
+ end
148
+ end
149
+
150
+ @_view_help_memory.each do |a|
151
+ send(*a)
152
+ end
153
+
154
+ @keypad.prompt.render(self, lines-1)
155
+ end
156
+
157
+ def _view_log
158
+ _render_status(threads: false, started: false, trxid: false, database: false)
159
+ next_line
160
+ if app.opts[:stdout].is_a?(SklavenTreiber::LogSpool)
161
+ limit = [lines - @line - 1, app.opts[:stdout].spool.length].min
162
+ app.opts[:stdout].spool[-limit..-1].each do |m, l, t|
163
+ ts = "[#{t}] "
164
+ gray(ts)
165
+ white decolorize(l.join(" "))[0..(cols - ts.length)]
166
+ next_line
167
+ end
168
+ else
169
+ red "Log spooling is not enabled, can't show log entries!"
170
+ end
171
+ @keypad.prompt.render(self, lines-1)
172
+ end
173
+
174
+ def _view_status
175
+ _render_status
176
+ _render_workers
177
+ @keypad.prompt.render(self, lines-1)
178
+ end
179
+
180
+ def _render_status opts = {}
181
+ opts = opts.reverse_merge(status: true, threads: true, started: true, trxid: true, database: true, progress: true)
182
+
183
+ if opts[:status]
184
+ next_line
185
+ yellow " Status: "
186
+ send(sklaventreiber.status[1].presence || :blue, sklaventreiber.status[0])
187
+ # shutdown message
188
+ if $core_runtime_exiting && sklaventreiber.status[0] != "terminated"
189
+ red " (HALTING … please wait)"
190
+ end
191
+ end
192
+
193
+ if opts[:threads]
194
+ next_line
195
+ yellow " Threads: "
196
+ blue "#{Thread.list.length} ".ljust(COL1, " ")
197
+ end
198
+
199
+ if opts[:started]
200
+ next_line
201
+ yellow " Started: "
202
+ blue "#{@app.boot}"
203
+ yellow " ("
204
+ blue human_seconds(Time.current - app.boot)
205
+ yellow ")"
206
+ end
207
+
208
+ if opts[:trxid]
209
+ next_line
210
+ yellow "Transaction ID: "
211
+ cyan sklaventreiber.trxid
212
+ end
213
+
214
+ if opts[:database]
215
+ next_line
216
+ yellow " Database: "
217
+ magenta sklaventreiber.data[:database] || "?"
218
+ yellow " (transfering "
219
+ blue "#{sklaventreiber.data[:tables_transfer] || "?"}"
220
+ yellow " of "
221
+ blue "#{sklaventreiber.data[:tables_total] || "?"}"
222
+ yellow " tables)"
223
+ end
224
+
225
+ if opts[:progress]
226
+ next_line
227
+ total, done = sklaventreiber.data[:tables_transfer], sklaventreiber.data[:tables_done]
228
+ perc = total && done ? f_percentage(done, total) : "?"
229
+ yellow " Progress: "
230
+ green perc
231
+ yellow " – "
232
+ blue "#{done}/#{total || "?"} workers done"
233
+ end
234
+ end
235
+
236
+ def _render_workers
237
+ if sklaventreiber.workers.any?
238
+ next_line
239
+ limit = lines - @line - 3 - (@keypad.prompt.active? ? 1 : 0) # @l starting at 0, 1 for blank line to come, placeholder
240
+ enum = sklaventreiber.workers.sort_by{|w| [w.priority, w.table] }
241
+ enum.each_with_index do |w, i|
242
+ # limit reached and more than one entry to come?
243
+ if i > limit && (enum.length - i - 1) > 0
244
+ next_line
245
+ rest = enum[i..-1]
246
+ part = rest.group_by(&:state).map do |k, v|
247
+ "#{v.length} #{k}"
248
+ end
249
+ yellow "… #{rest.length} more [#{part.join(", ")}]"
250
+ break
251
+ end
252
+ _render_worker_line(w)
253
+ end
254
+ end
255
+ end
256
+
257
+ def _render_worker_line worker
258
+ next_line
259
+ col1 = sklaventreiber.data[:window_col1]
260
+ col2 = sklaventreiber.data[:window_col2]
261
+
262
+ # status icon
263
+ case worker.state
264
+ when :pending then gray("⊙")
265
+ when :pausing, :paused then gray("♨")
266
+ when :aquired then white("⊙")
267
+ when :done then green("✔")
268
+ when :failed then red("✘")
269
+ when :canceled then red("⊘")
270
+ when :running then yellow("#{worker.spinner_frame}")
271
+ end
272
+
273
+ # table_name
274
+ send(worker.should_cancel ? :red : worker.paused? ? :gray : :magenta, " #{worker.table}".ljust(col1 + 1, " "))
275
+ gray " | "
276
+
277
+ # status
278
+ if worker.step
279
+ cyan "[#{worker.step}/#{worker.perform.length}] "
280
+ end
281
+ if worker.status[0].respond_to?(:to_curses)
282
+ worker.status[0].to_curses(self)
283
+ else
284
+ send(worker.status[1].presence || :blue, decolorize("#{worker.status[0]}"))
285
+ end
286
+ end
287
+
288
+ # used to render everything before exiting, can't fucking dump the pads I tried to implement -.-"
289
+ def _render_final_results
290
+ t_db, t_total, t_done = sklaventreiber.data[:database], sklaventreiber.data[:tables_transfer], sklaventreiber.data[:tables_done]
291
+ perc = t_total && t_done ? f_percentage(t_done, t_total) : "?"
292
+
293
+ puts
294
+ puts c(" Status: ") << c(sklaventreiber.status[0], sklaventreiber.status[1].presence || "red")
295
+ puts c(" Threads: ") << c("#{Thread.list.length} ".ljust(COL1, " "), :blue)
296
+ puts c(" Started: ") << c("#{@app.boot}", :blue) << c(" (") << c(human_seconds(Time.current - app.boot), :blue) << c(")")
297
+ puts c("Transaction ID: ") << c("#{sklaventreiber.trxid}", :cyan)
298
+ puts c(" Database: ") << c(t_db || "?", :magenta) << c(" (transferred ") << c(t_total || "?", :blue) << c(" of ") << c(t_done || "?", :blue) << c(" tables)")
299
+ puts c(" Progress: ") << c(perc, :green) << c(" – ") << c(t_total || "?", :blue) << c("#{t_done}/#{t_total || "?"} workers done", :blue)
300
+
301
+ if sklaventreiber.workers.any?
302
+ puts
303
+ enum = sklaventreiber.workers.sort_by{|w| [w.priority, w.table] }
304
+ enum.each do |worker|
305
+ col1 = sklaventreiber.data[:window_col1]
306
+ col2 = sklaventreiber.data[:window_col2]
307
+
308
+ puts "".tap{|res|
309
+ # status icon
310
+ res << case worker.state
311
+ when :done then c("✔", :green)
312
+ when :failed then c("✘", :red)
313
+ when :canceled then c("⊘", :red)
314
+ else "#{worker.state.inspect}"
315
+ end
316
+ # table
317
+ res << c(" #{worker.table}".ljust(col1 + 1, " "), :magenta) << c(" | ", :black)
318
+ # steps
319
+ res << c("[#{worker.step}/#{worker.perform.length}] ", :cyan) if worker.step
320
+ # status
321
+ res << c(worker.status[0], worker.status[1].presence || :blue)
322
+ }
323
+ end
324
+ puts
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,168 @@
1
+ module DbSucker
2
+ class Application
3
+ attr_reader :opts, :cfg, :sklaventreiber, :boot
4
+ include Core
5
+ include Colorize
6
+ include OutputHelper
7
+ include Dispatch
8
+ GC_FORCE_RATE = 25*1024*1024
9
+
10
+ # main dispatch routine for application
11
+ def self.dispatch *a
12
+ new(*a) do |app|
13
+ begin
14
+ app.signalify_thread(Thread.main)
15
+ Thread.main[:app] = app
16
+ app.load_appconfig
17
+ app.parse_params
18
+ app.debug "Running with PID #{Process.pid}"
19
+ app.debug "Runtime: #{RUBY_DESCRIPTION}"
20
+ app.dispatch
21
+ app.haltpoint
22
+ rescue Interrupt
23
+ app.abort("Interrupted", 1)
24
+ rescue OptionParser::ParseError => ex
25
+ app.fire(:core_exception, ex)
26
+ app.abort("#{ex.message}", false)
27
+ app.log app.c("Run `#{$0} --help' for more info", :blue)
28
+ exit 1
29
+ rescue StandardError => ex
30
+ app.fire(:core_exception, ex)
31
+ app.warn app.c("[FATAL] #{ex.class}: #{ex.message}", :red)
32
+ ex.backtrace.each do |l|
33
+ app.warn app.c("\t#{l}", :red)
34
+ end
35
+ app.abort case ex
36
+ when Container::TableNotFoundError then ex.message
37
+ else "Unhandled exception terminated application!"
38
+ end
39
+ ensure
40
+ app.fire(:core_shutdown)
41
+ remain = Thread.list.length
42
+ if remain > 1
43
+ app.warning "#{remain} threads remain (should be 1)..."
44
+ else
45
+ app.debug "1 thread remains..."
46
+ end
47
+ Thread.main[:app] = nil unless app.opts[:debug]
48
+ end
49
+ end
50
+ end
51
+
52
+ def initialize env, argv
53
+ @boot = Time.current
54
+ @env, @argv = env, argv
55
+ @hooks = {}
56
+ @monitor = Monitor.new
57
+ @output_monitor = Monitor.new
58
+ @cfg = ContainerCollection.new(self)
59
+ @opts = {
60
+ dispatch: :index, # (internal) action to dispatch
61
+ mode: :default, # (internal) mode for action
62
+ check_for_updates: true, # -z flag
63
+ colorize: true, # --monochrome flag
64
+ debug: false, # -d flag
65
+ stdout: STDOUT, # (internal) STDOUT redirect
66
+ pipein: ARGF, # (internal) INPUT redirect
67
+
68
+ list_databases: false, # --list-databases flag
69
+ list_tables: false, # --list-tables flag
70
+ suck_only: [], # --only flag
71
+ suck_except: [], # --except flag
72
+ simulate: false, # --simulate flag
73
+ deferred_import: true, # -n flag
74
+ deferred_threshold: 50_000_000, # 50 MB
75
+
76
+ # features
77
+ status_format: :full, # used for IO operations, can be one of: none, minimal, full
78
+ pv_enabled: true, # disable pv utility autodiscovery (force non-usage)
79
+
80
+ # sklaven treiber
81
+ window_enabled: true, # if disabled effectively disables any status progress or window drawing
82
+ window_draw: true, # wether to refresh screen or not
83
+ window_refresh_delay: 0.25, # refresh screen every so many seconds
84
+ window_keypad: true, # allow keyboard controls
85
+ window_spinner: :circle_quarter, # change spinner... why is this configurable?
86
+ consumers: 10, # amount of workers to run at the same time
87
+
88
+ # thread priorities (-3..+3)
89
+ tp_window_draw_loop: -3,
90
+ tp_window_keypad_loop: +2,
91
+ tp_sklaventreiber_ssh_poll: +3,
92
+ tp_sklaventreiber_throughput: +2,
93
+ tp_sklaventreiber_worker: -1,
94
+ tp_sklaventreiber_worker_ctrl: -1,
95
+ tp_sklaventreiber_worker_slot_progress: -2,
96
+ tp_sklaventreiber_worker_second_progress: -2,
97
+ tp_sklaventreiber_worker_io_pv_killer: -2,
98
+ tp_sklaventreiber_worker_io_import_sql: +3,
99
+
100
+ # used to open core dumps (should be a blocking call, e.g. `subl -w' or `mate -w')
101
+ # MUST be windowed! vim, nano, etc. will not work!
102
+ core_dump_editor: "subl -w",
103
+
104
+ # amount of workers that can use a slot (false = infinite)
105
+ # you can create as many pools as you want and use them in `routine_pools' setting
106
+ slot_pools: {
107
+ all: false,
108
+ remote: false,
109
+ download: false,
110
+ local: false,
111
+ import: 3,
112
+ deferred: 1,
113
+ },
114
+
115
+ # assign tasks to certain slot pools
116
+ routine_pools: {
117
+ r_dump_file: [:all, :remote],
118
+ r_calculate_raw_hash: [:all, :remote],
119
+ r_compress_file: [:all, :remote],
120
+ r_calculate_compressed_hash: [:all, :remote],
121
+ l_download_file: [:all, :download],
122
+ l_verify_compressed_hash: [:all, :local],
123
+ l_copy_file: [:all, :local],
124
+ l_decompress_file: [:all, :local],
125
+ l_verify_raw_hash: [:all, :local],
126
+ l_import_file: [:all, :local, :import],
127
+ l_wait_for_workers: [], # does nothing, no need for any slots
128
+ l_import_file_deferred: [:all, :local, :deferred],
129
+ }
130
+ }
131
+ init_params
132
+ Tie.hook_all!(self)
133
+ yield(self)
134
+ end
135
+
136
+ def init_params
137
+ @optparse = OptionParser.new do |opts|
138
+ opts.banner = "Usage: db_sucker [options] [identifier [variation]]"
139
+
140
+ opts.separator("\n" << "# Application options")
141
+ opts.on( "--new NAME", String, "Generates new container config in #{core_cfg_path}") {|v| @opts[:dispatch] = :generate_config; @opts[:config_name] = v }
142
+ opts.on("-a", "--action ACTION", String, "Dispatch given action") {|v| @opts[:dispatch] = v }
143
+ opts.on("-m", "--mode MODE", String, "Dispatch action with given mode") {|v| @opts[:mode] = v.to_sym }
144
+ opts.on("-n", "--no-deffer", "Don't use deferred import for files > 50 MB SQL data size.") { @opts[:deferred_import] = false }
145
+ opts.on("-l", "--list-databases", "List databases for given identifier.") { @opts[:list_databases] = true }
146
+ opts.on("-t", "--list-tables [DATABASE]", String, "List tables for given identifier and database.", "If used with --list-databases the DATABASE parameter is optional.") {|s| @opts[:list_tables] = s || :all }
147
+ opts.on("-o", "--only table,table2", Array, "Only suck given tables. Identifier is required, variation is optional (defaults to default).", "WARNING: ignores ignore_always option") {|s| @opts[:suck_only] = s }
148
+ opts.on("-e", "--except table,table2", Array, "Don't suck given tables. Identifier is required, variation is optional (defaults to default).") {|s| @opts[:suck_except] = s }
149
+ opts.on("-c", "--consumers NUM=10", Integer, "Maximal amount of tasks to run simultaneously") {|n| @opts[:consumers] = n }
150
+ opts.on( "--stat-tmp", "Show information about the remote temporary directory.", "If no identifier is given check local temp directory instead.") { @opts[:dispatch] = :stat_tmp }
151
+ opts.on( "--cleanup-tmp", "Remove all temporary files from db_sucker in target directory.") { @opts[:dispatch] = :cleanup_tmp }
152
+ opts.on( "--simulate", "To use with --cleanup-tmp to not actually remove anything.") { @opts[:simulate] = true }
153
+
154
+ opts.separator("\n" << "# General options")
155
+ opts.on("-d", "--debug [lvl=1]", Integer, "Enable debug output") {|l| @opts[:debug] = l || 1 }
156
+ opts.on("--monochrome", "Don't colorize output (does not apply to curses)") { @opts[:colorize] = false }
157
+ opts.on("--no-window", "Disables curses window alltogether (no progress)") { @opts[:window_enabled] = false }
158
+ opts.on("-h", "--help", "Shows this help") { @opts[:dispatch] = :help }
159
+ opts.on("-v", "--version", "Shows version and other info") { @opts[:dispatch] = :info }
160
+ opts.on("-z", "Do not check for updates on GitHub (with -v/--version)") { @opts[:check_for_updates] = false }
161
+ end
162
+ end
163
+
164
+ def parse_params
165
+ @optparse.parse!(@argv)
166
+ end
167
+ end
168
+ end