turbot 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +36 -0
  3. data/bin/turbot +17 -0
  4. data/data/cacert.pem +3988 -0
  5. data/lib/turbot/auth.rb +315 -0
  6. data/lib/turbot/cli.rb +38 -0
  7. data/lib/turbot/client/cisaurus.rb +25 -0
  8. data/lib/turbot/client/pgbackups.rb +113 -0
  9. data/lib/turbot/client/rendezvous.rb +111 -0
  10. data/lib/turbot/client/ssl_endpoint.rb +25 -0
  11. data/lib/turbot/client/turbot_postgresql.rb +148 -0
  12. data/lib/turbot/client.rb +757 -0
  13. data/lib/turbot/command/auth.rb +85 -0
  14. data/lib/turbot/command/base.rb +192 -0
  15. data/lib/turbot/command/bots.rb +326 -0
  16. data/lib/turbot/command/config.rb +123 -0
  17. data/lib/turbot/command/help.rb +179 -0
  18. data/lib/turbot/command/keys.rb +115 -0
  19. data/lib/turbot/command/logs.rb +34 -0
  20. data/lib/turbot/command/ssl.rb +43 -0
  21. data/lib/turbot/command/status.rb +51 -0
  22. data/lib/turbot/command/update.rb +47 -0
  23. data/lib/turbot/command/version.rb +23 -0
  24. data/lib/turbot/command.rb +304 -0
  25. data/lib/turbot/deprecated/help.rb +38 -0
  26. data/lib/turbot/deprecated.rb +5 -0
  27. data/lib/turbot/distribution.rb +9 -0
  28. data/lib/turbot/errors.rb +28 -0
  29. data/lib/turbot/excon.rb +11 -0
  30. data/lib/turbot/helpers/log_displayer.rb +70 -0
  31. data/lib/turbot/helpers/pg_dump_restore.rb +115 -0
  32. data/lib/turbot/helpers/turbot_postgresql.rb +213 -0
  33. data/lib/turbot/helpers.rb +521 -0
  34. data/lib/turbot/plugin.rb +165 -0
  35. data/lib/turbot/updater.rb +171 -0
  36. data/lib/turbot/version.rb +3 -0
  37. data/lib/turbot.rb +19 -0
  38. data/lib/vendor/turbot/okjson.rb +598 -0
  39. data/spec/helper/legacy_help.rb +16 -0
  40. data/spec/helper/pg_dump_restore_spec.rb +67 -0
  41. data/spec/schemas/dummy_schema.json +12 -0
  42. data/spec/spec.opts +1 -0
  43. data/spec/spec_helper.rb +220 -0
  44. data/spec/support/display_message_matcher.rb +49 -0
  45. data/spec/support/dummy_api.rb +120 -0
  46. data/spec/support/openssl_mock_helper.rb +8 -0
  47. data/spec/support/organizations_mock_helper.rb +11 -0
  48. data/spec/turbot/auth_spec.rb +214 -0
  49. data/spec/turbot/client/pgbackups_spec.rb +43 -0
  50. data/spec/turbot/client/rendezvous_spec.rb +62 -0
  51. data/spec/turbot/client/ssl_endpoint_spec.rb +48 -0
  52. data/spec/turbot/client/turbot_postgresql_spec.rb +71 -0
  53. data/spec/turbot/client_spec.rb +548 -0
  54. data/spec/turbot/command/auth_spec.rb +38 -0
  55. data/spec/turbot/command/base_spec.rb +66 -0
  56. data/spec/turbot/command/bots_spec.rb +54 -0
  57. data/spec/turbot/command/config_spec.rb +143 -0
  58. data/spec/turbot/command/help_spec.rb +90 -0
  59. data/spec/turbot/command/keys_spec.rb +117 -0
  60. data/spec/turbot/command/logs_spec.rb +60 -0
  61. data/spec/turbot/command/status_spec.rb +48 -0
  62. data/spec/turbot/command/version_spec.rb +16 -0
  63. data/spec/turbot/command_spec.rb +131 -0
  64. data/spec/turbot/helpers/turbot_postgresql_spec.rb +181 -0
  65. data/spec/turbot/helpers_spec.rb +48 -0
  66. data/spec/turbot/plugin_spec.rb +172 -0
  67. data/spec/turbot/updater_spec.rb +44 -0
  68. data/templates/manifest.json +7 -0
  69. data/templates/scraper.py +5 -0
  70. data/templates/scraper.rb +6 -0
  71. metadata +199 -0
@@ -0,0 +1,521 @@
1
+ require "vendor/turbot/okjson"
2
+
3
+ module Turbot
4
+ module Helpers
5
+
6
+ extend self
7
+
8
+ def home_directory
9
+ running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
10
+ end
11
+
12
+ def running_on_windows?
13
+ RUBY_PLATFORM =~ /mswin32|mingw32/
14
+ end
15
+
16
+ def running_on_a_mac?
17
+ RUBY_PLATFORM =~ /-darwin\d/
18
+ end
19
+
20
+ def display(msg="", new_line=true)
21
+ if new_line
22
+ puts(msg)
23
+ else
24
+ print(msg)
25
+ end
26
+ $stdout.flush
27
+ end
28
+
29
+ def redisplay(line, line_break = false)
30
+ display("\r\e[0K#{line}", line_break)
31
+ end
32
+
33
+ def deprecate(message)
34
+ display "WARNING: #{message}"
35
+ end
36
+
37
+ def confirm(message="Are you sure you wish to continue? (y/n)")
38
+ display("#{message} ", false)
39
+ ['y', 'yes'].include?(ask.downcase)
40
+ end
41
+
42
+ def confirm_command(bot_to_confirm = bot, message=nil)
43
+ if confirmed_bot = Turbot::Command.current_options[:confirm]
44
+ unless confirmed_bot == bot_to_confirm
45
+ raise(Turbot::Command::CommandFailed, "Confirmed bot #{confirmed_bot} did not match the selected bot #{bot_to_confirm}.")
46
+ end
47
+ return true
48
+ else
49
+ display
50
+ message ||= "WARNING: Destructive Action\nThis command will affect the bot: #{bot_to_confirm}"
51
+ message << "\nTo proceed, type \"#{bot_to_confirm}\" or re-run this command with --confirm #{bot_to_confirm}"
52
+ output_with_bang(message)
53
+ display
54
+ display "> ", false
55
+ if ask.downcase != bot_to_confirm
56
+ error("Confirmation did not match #{bot_to_confirm}. Aborted.")
57
+ else
58
+ true
59
+ end
60
+ end
61
+ end
62
+
63
+ def format_date(date)
64
+ date = Time.parse(date).utc if date.is_a?(String)
65
+ date.strftime("%Y-%m-%d %H:%M %Z").gsub('GMT', 'UTC')
66
+ end
67
+
68
+ def ask
69
+ $stdin.gets.to_s.strip
70
+ end
71
+
72
+ def shell(cmd)
73
+ FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
74
+ end
75
+
76
+ def run_command(command, args=[])
77
+ Turbot::Command.run(command, args)
78
+ end
79
+
80
+ def retry_on_exception(*exceptions)
81
+ retry_count = 0
82
+ begin
83
+ yield
84
+ rescue *exceptions => ex
85
+ raise ex if retry_count >= 3
86
+ sleep 3
87
+ retry_count += 1
88
+ retry
89
+ end
90
+ end
91
+
92
+ def has_git?
93
+ %x{ git --version }
94
+ $?.success?
95
+ end
96
+
97
+ def git(args)
98
+ return "" unless has_git?
99
+ flattened_args = [args].flatten.compact.join(" ")
100
+ %x{ git #{flattened_args} 2>&1 }.strip
101
+ end
102
+
103
+ def time_ago(since)
104
+ if since.is_a?(String)
105
+ since = Time.parse(since)
106
+ end
107
+
108
+ elapsed = Time.now - since
109
+
110
+ message = since.strftime("%Y/%m/%d %H:%M:%S")
111
+ if elapsed <= 60
112
+ message << " (~ #{elapsed.floor}s ago)"
113
+ elsif elapsed <= (60 * 60)
114
+ message << " (~ #{(elapsed / 60).floor}m ago)"
115
+ elsif elapsed <= (60 * 60 * 25)
116
+ message << " (~ #{(elapsed / 60 / 60).floor}h ago)"
117
+ end
118
+ message
119
+ end
120
+
121
+ def truncate(text, length)
122
+ if text.size > length
123
+ text[0, length - 2] + '..'
124
+ else
125
+ text
126
+ end
127
+ end
128
+
129
+ @@kb = 1024
130
+ @@mb = 1024 * @@kb
131
+ @@gb = 1024 * @@mb
132
+ def format_bytes(amount)
133
+ amount = amount.to_i
134
+ return '(empty)' if amount == 0
135
+ return amount if amount < @@kb
136
+ return "#{(amount / @@kb).round}k" if amount < @@mb
137
+ return "#{(amount / @@mb).round}M" if amount < @@gb
138
+ return "#{(amount / @@gb).round}G"
139
+ end
140
+
141
+ def quantify(string, num)
142
+ "%d %s" % [ num, num.to_i == 1 ? string : "#{string}s" ]
143
+ end
144
+
145
+ def create_git_remote(remote, url)
146
+ return if git('remote').split("\n").include?(remote)
147
+ return unless File.exists?(".git")
148
+ git "remote add #{remote} #{url}"
149
+ display "Git remote #{remote} added"
150
+ end
151
+
152
+ def longest(items)
153
+ items.map { |i| i.to_s.length }.sort.last
154
+ end
155
+
156
+ def display_table(objects, columns, headers)
157
+ lengths = []
158
+ columns.each_with_index do |column, index|
159
+ header = headers[index]
160
+ lengths << longest([header].concat(objects.map { |o| o[column].to_s }))
161
+ end
162
+ lines = lengths.map {|length| "-" * length}
163
+ lengths[-1] = 0 # remove padding from last column
164
+ display_row headers, lengths
165
+ display_row lines, lengths
166
+ objects.each do |row|
167
+ display_row columns.map { |column| row[column] }, lengths
168
+ end
169
+ end
170
+
171
+ def display_row(row, lengths)
172
+ row_data = []
173
+ row.zip(lengths).each do |column, length|
174
+ format = column.is_a?(Fixnum) ? "%#{length}s" : "%-#{length}s"
175
+ row_data << format % column
176
+ end
177
+ display(row_data.join(" "))
178
+ end
179
+
180
+ def json_encode(object)
181
+ Turbot::OkJson.encode(object)
182
+ rescue Turbot::OkJson::Error
183
+ nil
184
+ end
185
+
186
+ def json_decode(json)
187
+ Turbot::OkJson.decode(json)
188
+ rescue Turbot::OkJson::Error
189
+ nil
190
+ end
191
+
192
+ def set_buffer(enable)
193
+ with_tty do
194
+ if enable
195
+ `stty icanon echo`
196
+ else
197
+ `stty -icanon -echo`
198
+ end
199
+ end
200
+ end
201
+
202
+ def with_tty(&block)
203
+ return unless $stdin.isatty
204
+ begin
205
+ yield
206
+ rescue
207
+ # fails on windows
208
+ end
209
+ end
210
+
211
+ def get_terminal_environment
212
+ { "TERM" => ENV["TERM"], "COLUMNS" => `tput cols`.strip, "LINES" => `tput lines`.strip }
213
+ rescue
214
+ { "TERM" => ENV["TERM"] }
215
+ end
216
+
217
+ def fail(message)
218
+ raise Turbot::Command::CommandFailed, message
219
+ end
220
+
221
+ ## DISPLAY HELPERS
222
+
223
+ def action(message, options={})
224
+ message = "#{message} in organzation #{org}" if options[:org]
225
+ display("#{message}... ", false)
226
+ Turbot::Helpers.error_with_failure = true
227
+ ret = yield
228
+ Turbot::Helpers.error_with_failure = false
229
+ display((options[:success] || "done"), false)
230
+ if @status
231
+ display(", #{@status}", false)
232
+ @status = nil
233
+ end
234
+ display
235
+ ret
236
+ end
237
+
238
+ def status(message)
239
+ @status = message
240
+ end
241
+
242
+ def format_with_bang(message)
243
+ return '' if message.to_s.strip == ""
244
+ " ! " + message.split("\n").join("\n ! ")
245
+ end
246
+
247
+ def output_with_bang(message="", new_line=true)
248
+ return if message.to_s.strip == ""
249
+ display(format_with_bang(message), new_line)
250
+ end
251
+
252
+ def error(message)
253
+ if Turbot::Helpers.error_with_failure
254
+ display("failed")
255
+ Turbot::Helpers.error_with_failure = false
256
+ end
257
+ $stderr.puts(format_with_bang(message))
258
+ exit(1)
259
+ end
260
+
261
+ def self.error_with_failure
262
+ @@error_with_failure ||= false
263
+ end
264
+
265
+ def self.error_with_failure=(new_error_with_failure)
266
+ @@error_with_failure = new_error_with_failure
267
+ end
268
+
269
+ def self.included_into
270
+ @@included_into ||= []
271
+ end
272
+
273
+ def self.extended_into
274
+ @@extended_into ||= []
275
+ end
276
+
277
+ def self.included(base)
278
+ included_into << base
279
+ end
280
+
281
+ def self.extended(base)
282
+ extended_into << base
283
+ end
284
+
285
+ def display_header(message="", new_line=true)
286
+ return if message.to_s.strip == ""
287
+ display("=== " + message.to_s.split("\n").join("\n=== "), new_line)
288
+ end
289
+
290
+ def display_object(object)
291
+ case object
292
+ when Array
293
+ # list of objects
294
+ object.each do |item|
295
+ display_object(item)
296
+ end
297
+ when Hash
298
+ # if all values are arrays, it is a list with headers
299
+ # otherwise it is a single header with pairs of data
300
+ if object.values.all? {|value| value.is_a?(Array)}
301
+ object.keys.sort_by {|key| key.to_s}.each do |key|
302
+ display_header(key)
303
+ display_object(object[key])
304
+ hputs
305
+ end
306
+ end
307
+ else
308
+ hputs(object.to_s)
309
+ end
310
+ end
311
+
312
+ def hputs(string='')
313
+ Kernel.puts(string)
314
+ end
315
+
316
+ def hprint(string='')
317
+ Kernel.print(string)
318
+ $stdout.flush
319
+ end
320
+
321
+ def spinner(ticks)
322
+ %w(/ - \\ |)[ticks % 4]
323
+ end
324
+
325
+ def launchy(message, url)
326
+ action(message) do
327
+ require("launchy")
328
+ launchy = Launchy.open(url)
329
+ if launchy.respond_to?(:join)
330
+ launchy.join
331
+ end
332
+ end
333
+ end
334
+
335
+ # produces a printf formatter line for an array of items
336
+ # if an individual line item is an array, it will create columns
337
+ # that are lined-up
338
+ #
339
+ # line_formatter(["foo", "barbaz"]) # => "%-6s"
340
+ # line_formatter(["foo", "barbaz"], ["bar", "qux"]) # => "%-3s %-6s"
341
+ #
342
+ def line_formatter(array)
343
+ if array.any? {|item| item.is_a?(Array)}
344
+ cols = []
345
+ array.each do |item|
346
+ if item.is_a?(Array)
347
+ item.each_with_index { |val,idx| cols[idx] = [cols[idx]||0, (val || '').length].max }
348
+ end
349
+ end
350
+ cols.map { |col| "%-#{col}s" }.join(" ")
351
+ else
352
+ "%s"
353
+ end
354
+ end
355
+
356
+ def styled_array(array, options={})
357
+ fmt = line_formatter(array)
358
+ array = array.sort unless options[:sort] == false
359
+ array.each do |element|
360
+ display((fmt % element).rstrip)
361
+ end
362
+ display
363
+ end
364
+
365
+ def format_error(error, message='Turbot client internal error.')
366
+ formatted_error = []
367
+ formatted_error << " ! #{message}"
368
+ formatted_error << ' ! Report a bug at: https://github.com/openc/turbot-client/issues/new'
369
+ formatted_error << ''
370
+ formatted_error << " Error: #{error.message} (#{error.class})"
371
+ formatted_error << " Backtrace: #{error.backtrace.first}"
372
+ error.backtrace[1..-1].each do |line|
373
+ formatted_error << " #{line}"
374
+ end
375
+ if error.backtrace.length > 1
376
+ formatted_error << ''
377
+ end
378
+ command = ARGV.map do |arg|
379
+ if arg.include?(' ')
380
+ arg = %{"#{arg}"}
381
+ else
382
+ arg
383
+ end
384
+ end.join(' ')
385
+ formatted_error << " Command: turbot #{command}"
386
+ require 'turbot/auth'
387
+ unless Turbot::Auth.host == Turbot::Auth.default_host
388
+ formatted_error << " Host: #{Turbot::Auth.host}"
389
+ end
390
+ if http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
391
+ formatted_error << " HTTP Proxy: #{http_proxy}"
392
+ end
393
+ if https_proxy = ENV['https_proxy'] || ENV['HTTPS_PROXY']
394
+ formatted_error << " HTTPS Proxy: #{https_proxy}"
395
+ end
396
+ plugins = Turbot::Plugin.list.sort
397
+ unless plugins.empty?
398
+ formatted_error << " Plugins: #{plugins.first}"
399
+ plugins[1..-1].each do |plugin|
400
+ formatted_error << " #{plugin}"
401
+ end
402
+ if plugins.length > 1
403
+ formatted_error << ''
404
+ $stderr.puts
405
+ end
406
+ end
407
+ formatted_error << " Version: #{Turbot.user_agent}"
408
+ formatted_error << "\n"
409
+ formatted_error.join("\n")
410
+ end
411
+
412
+ def styled_error(error, message='Turbot client internal error.')
413
+ if Turbot::Helpers.error_with_failure
414
+ display("failed")
415
+ Turbot::Helpers.error_with_failure = false
416
+ end
417
+ $stderr.puts(format_error(error, message))
418
+ end
419
+
420
+ def styled_header(header)
421
+ display("=== #{header}")
422
+ end
423
+
424
+ def styled_hash(hash, keys=nil)
425
+ max_key_length = hash.keys.map {|key| key.to_s.length}.max + 2
426
+ keys ||= hash.keys.sort {|x,y| x.to_s <=> y.to_s}
427
+ keys.each do |key|
428
+ case value = hash[key]
429
+ when Array
430
+ if value.empty?
431
+ next
432
+ else
433
+ elements = value.sort {|x,y| x.to_s <=> y.to_s}
434
+ display("#{key}: ".ljust(max_key_length), false)
435
+ display(elements[0])
436
+ elements[1..-1].each do |element|
437
+ display("#{' ' * max_key_length}#{element}")
438
+ end
439
+ if elements.length > 1
440
+ display
441
+ end
442
+ end
443
+ when nil
444
+ next
445
+ else
446
+ display("#{key}: ".ljust(max_key_length), false)
447
+ display(value)
448
+ end
449
+ end
450
+ end
451
+
452
+ def string_distance(first, last)
453
+ distances = [] # 0x0s
454
+ 0.upto(first.length) do |index|
455
+ distances << [index] + [0] * last.length
456
+ end
457
+ distances[0] = 0.upto(last.length).to_a
458
+ 1.upto(last.length) do |last_index|
459
+ 1.upto(first.length) do |first_index|
460
+ first_char = first[first_index - 1, 1]
461
+ last_char = last[last_index - 1, 1]
462
+ if first_char == last_char
463
+ distances[first_index][last_index] = distances[first_index - 1][last_index - 1] # noop
464
+ else
465
+ distances[first_index][last_index] = [
466
+ distances[first_index - 1][last_index], # deletion
467
+ distances[first_index][last_index - 1], # insertion
468
+ distances[first_index - 1][last_index - 1] # substitution
469
+ ].min + 1 # cost
470
+ if first_index > 1 && last_index > 1
471
+ first_previous_char = first[first_index - 2, 1]
472
+ last_previous_char = last[last_index - 2, 1]
473
+ if first_char == last_previous_char && first_previous_char == last_char
474
+ distances[first_index][last_index] = [
475
+ distances[first_index][last_index],
476
+ distances[first_index - 2][last_index - 2] + 1 # transposition
477
+ ].min
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end
483
+ distances[first.length][last.length]
484
+ end
485
+
486
+ def suggestion(actual, possibilities)
487
+ distances = Hash.new {|hash,key| hash[key] = []}
488
+ possibilities.each do |suggestion|
489
+ distances[string_distance(actual, suggestion)] << suggestion
490
+ end
491
+ minimum_distance = distances.keys.min
492
+ if minimum_distance < 4
493
+ suggestions = distances[minimum_distance].sort
494
+ if suggestions.length == 1
495
+ "Perhaps you meant `#{suggestions.first}`."
496
+ else
497
+ "Perhaps you meant #{suggestions[0...-1].map {|suggestion| "`#{suggestion}`"}.join(', ')} or `#{suggestions.last}`."
498
+ end
499
+ else
500
+ nil
501
+ end
502
+ end
503
+
504
+ def org_host
505
+ ENV["TURBOT_ORG_HOST"] || default_org_host
506
+ end
507
+
508
+ def default_org_host
509
+ "turbotmanager.com"
510
+ end
511
+
512
+ def org? email
513
+ email =~ /^.*@#{org_host}$/
514
+ end
515
+
516
+ def bot_owner email
517
+ org?(email) ? email.gsub(/^(.*)@#{org_host}$/,'\1') : email
518
+ end
519
+
520
+ end
521
+ end
@@ -0,0 +1,165 @@
1
+ # based on the Rails Plugin
2
+
3
+ module Turbot
4
+ class Plugin
5
+ include Turbot::Helpers
6
+ extend Turbot::Helpers
7
+
8
+ class ErrorUpdatingSymlinkPlugin < StandardError; end
9
+
10
+ DEPRECATED_PLUGINS = %w(
11
+ turbot-cedar
12
+ turbot-certs
13
+ turbot-credentials
14
+ turbot-dyno-size
15
+ turbot-kill
16
+ turbot-labs
17
+ turbot-logging
18
+ turbot-netrc
19
+ turbot-pgdumps
20
+ turbot-postgresql
21
+ turbot-releases
22
+ turbot-shared-postgresql
23
+ turbot-sql-console
24
+ turbot-status
25
+ turbot-stop
26
+ turbot-suggest
27
+ pgbackups-automate
28
+ pgcmd
29
+ turbot-fork
30
+ turbot-orgs
31
+ )
32
+
33
+ attr_reader :name, :uri
34
+
35
+ def self.directory
36
+ File.expand_path("#{home_directory}/.turbot/plugins")
37
+ end
38
+
39
+ def self.list
40
+ Dir["#{directory}/*"].sort.map do |folder|
41
+ File.basename(folder)
42
+ end
43
+ end
44
+
45
+ def self.load!
46
+ list.each do |plugin|
47
+ check_for_deprecation(plugin)
48
+ next if skip_plugins.include?(plugin)
49
+ load_plugin(plugin)
50
+ end
51
+ # check to see if we are using ddollar/turbot-accounts
52
+ if list.include?('turbot-accounts') && Turbot::Auth.methods.include?(:fetch_from_account)
53
+ # setup netrc to match the default, if one exists
54
+ if default_account = %x{ git config turbot.account }.chomp
55
+ account = Turbot::Auth.extract_account rescue nil
56
+ if account && Turbot::Auth.read_credentials != [Turbot::Auth.user, Turbot::Auth.password]
57
+ Turbot::Auth.credentials = [Turbot::Auth.user, Turbot::Auth.password]
58
+ Turbot::Auth.write_credentials
59
+ load("#{File.dirname(__FILE__)}/command/accounts.rb")
60
+ # kill memoization in case '--account' was passed
61
+ Turbot::Auth.instance_variable_set(:@account, nil)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.load_plugin(plugin)
68
+ begin
69
+ folder = "#{self.directory}/#{plugin}"
70
+ $: << "#{folder}/lib" if File.directory? "#{folder}/lib"
71
+ load "#{folder}/init.rb" if File.exists? "#{folder}/init.rb"
72
+ rescue ScriptError, StandardError => error
73
+ styled_error(error, "Unable to load plugin #{plugin}.")
74
+ false
75
+ end
76
+ end
77
+
78
+ def self.remove_plugin(plugin)
79
+ FileUtils.rm_rf("#{self.directory}/#{plugin}")
80
+ end
81
+
82
+ def self.check_for_deprecation(plugin)
83
+ return unless STDIN.isatty
84
+
85
+ if DEPRECATED_PLUGINS.include?(plugin)
86
+ if confirm "The plugin #{plugin} has been deprecated. Would you like to remove it? (y/N)"
87
+ remove_plugin(plugin)
88
+ end
89
+ end
90
+ end
91
+
92
+ def self.skip_plugins
93
+ @skip_plugins ||= ENV["SKIP_PLUGINS"].to_s.split(/[ ,]/)
94
+ end
95
+
96
+ def initialize(uri)
97
+ @uri = uri
98
+ guess_name(uri)
99
+ end
100
+
101
+ def to_s
102
+ name
103
+ end
104
+
105
+ def path
106
+ "#{self.class.directory}/#{name}"
107
+ end
108
+
109
+ def install
110
+ if File.directory?(path)
111
+ uninstall
112
+ end
113
+ FileUtils.mkdir_p(self.class.directory)
114
+ Dir.chdir(self.class.directory) do
115
+ git("clone #{uri}")
116
+ unless $?.success?
117
+ FileUtils.rm_rf path
118
+ return false
119
+ end
120
+ end
121
+ true
122
+ end
123
+
124
+ def uninstall
125
+ ensure_plugin_exists
126
+ FileUtils.rm_r(path)
127
+ end
128
+
129
+ def update
130
+ ensure_plugin_exists
131
+ if File.symlink?(path)
132
+ raise Turbot::Plugin::ErrorUpdatingSymlinkPlugin
133
+ else
134
+ Dir.chdir(path) do
135
+ unless git('config --get branch.master.remote').empty?
136
+ message = git("pull")
137
+ unless $?.success?
138
+ error("Unable to update #{name}.\n" + message)
139
+ end
140
+ else
141
+ error(<<-ERROR)
142
+ #{name} is a legacy plugin installation.
143
+ Enable updating by reinstalling with `turbot plugins:install`.
144
+ ERROR
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def ensure_plugin_exists
153
+ unless File.directory?(path)
154
+ error("#{name} plugin not found.")
155
+ end
156
+ end
157
+
158
+ def guess_name(url)
159
+ @name = File.basename(url)
160
+ @name = File.basename(File.dirname(url)) if @name.empty?
161
+ @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
162
+ end
163
+
164
+ end
165
+ end