turbot 0.1.36 → 0.2.3
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 +4 -4
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/.travis.yml +15 -0
- data/.yardopts +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +44 -25
- data/Rakefile +16 -0
- data/appveyor.yml +35 -0
- data/bin/turbot +2 -16
- data/data/schema.json +134 -0
- data/{templates → data/templates}/LICENSE.txt +0 -0
- data/{templates → data/templates}/manifest.json +0 -0
- data/{templates → data/templates}/python/scraper.py +0 -0
- data/{templates → data/templates}/ruby/scraper.rb +0 -0
- data/dist/deb.rake +32 -0
- data/dist/gem.rake +16 -0
- data/dist/manifest.rake +9 -0
- data/dist/pkg.rake +60 -0
- data/dist/resources/deb/control +10 -0
- data/dist/resources/deb/postinst +45 -0
- data/dist/resources/deb/turbot +25 -0
- data/dist/resources/deb/turbot-release-key.txt +30 -0
- data/dist/resources/pkg/Distribution.erb +15 -0
- data/dist/resources/pkg/PackageInfo.erb +6 -0
- data/dist/resources/pkg/postinstall +45 -0
- data/dist/resources/pkg/turbot +24 -0
- data/dist/resources/tgz/turbot +24 -0
- data/dist/rpm.rake +35 -0
- data/dist/tgz.rake +26 -0
- data/dist/zip.rake +40 -0
- data/lib/turbot.rb +18 -15
- data/lib/turbot/cli.rb +10 -27
- data/lib/turbot/command.rb +59 -212
- data/lib/turbot/command/auth.rb +72 -34
- data/lib/turbot/command/base.rb +22 -61
- data/lib/turbot/command/bots.rb +251 -300
- data/lib/turbot/command/help.rb +57 -110
- data/lib/turbot/command/version.rb +6 -10
- data/lib/turbot/handlers/base_handler.rb +21 -0
- data/lib/turbot/handlers/dump_handler.rb +10 -0
- data/lib/turbot/handlers/preview_handler.rb +30 -0
- data/lib/turbot/handlers/validation_handler.rb +17 -0
- data/lib/turbot/helpers.rb +14 -482
- data/lib/turbot/helpers/api_helper.rb +41 -0
- data/lib/turbot/helpers/netrc_helper.rb +66 -0
- data/lib/turbot/helpers/shell_helper.rb +36 -0
- data/lib/turbot/version.rb +1 -1
- data/spec/fixtures/bad_permissions +0 -0
- data/spec/fixtures/empty +0 -0
- data/spec/fixtures/netrc +6 -0
- data/spec/spec_helper.rb +17 -219
- data/spec/support/bot_helper.rb +102 -0
- data/spec/support/command_helper.rb +20 -0
- data/spec/support/custom_matchers.rb +5 -0
- data/spec/support/fixture_helper.rb +9 -0
- data/spec/support/netrc_helper.rb +21 -0
- data/spec/turbot/command/auth_spec.rb +202 -20
- data/spec/turbot/command/base_spec.rb +22 -58
- data/spec/turbot/command/bots_spec.rb +580 -89
- data/spec/turbot/command/help_spec.rb +32 -75
- data/spec/turbot/command/version_spec.rb +11 -10
- data/spec/turbot/command_spec.rb +55 -87
- data/spec/turbot/helpers_spec.rb +28 -44
- data/turbot.gemspec +31 -0
- metadata +88 -178
- data/data/cacert.pem +0 -3988
- data/lib/turbot/auth.rb +0 -315
- data/lib/turbot/client.rb +0 -757
- data/lib/turbot/client/cisaurus.rb +0 -25
- data/lib/turbot/client/pgbackups.rb +0 -113
- data/lib/turbot/client/rendezvous.rb +0 -111
- data/lib/turbot/client/ssl_endpoint.rb +0 -25
- data/lib/turbot/client/turbot_postgresql.rb +0 -148
- data/lib/turbot/command/ssl.rb +0 -43
- data/lib/turbot/deprecated.rb +0 -5
- data/lib/turbot/deprecated/help.rb +0 -38
- data/lib/turbot/distribution.rb +0 -9
- data/lib/turbot/errors.rb +0 -28
- data/lib/turbot/excon.rb +0 -11
- data/lib/turbot/helpers/log_displayer.rb +0 -70
- data/lib/turbot/helpers/pg_dump_restore.rb +0 -115
- data/lib/turbot/helpers/turbot_postgresql.rb +0 -213
- data/lib/turbot/plugin.rb +0 -165
- data/lib/turbot/updater.rb +0 -171
- data/lib/vendor/turbot/okjson.rb +0 -598
- data/spec/helper/legacy_help.rb +0 -16
- data/spec/helper/pg_dump_restore_spec.rb +0 -67
- data/spec/spec.opts +0 -1
- data/spec/support/display_message_matcher.rb +0 -49
- data/spec/support/dummy_api.rb +0 -120
- data/spec/support/openssl_mock_helper.rb +0 -8
- data/spec/support/organizations_mock_helper.rb +0 -11
- data/spec/turbot/auth_spec.rb +0 -214
- data/spec/turbot/client/pgbackups_spec.rb +0 -43
- data/spec/turbot/client/rendezvous_spec.rb +0 -62
- data/spec/turbot/client/ssl_endpoint_spec.rb +0 -48
- data/spec/turbot/client/turbot_postgresql_spec.rb +0 -71
- data/spec/turbot/client_spec.rb +0 -548
- data/spec/turbot/helpers/turbot_postgresql_spec.rb +0 -181
- data/spec/turbot/plugin_spec.rb +0 -172
- data/spec/turbot/updater_spec.rb +0 -44
data/lib/turbot/command/help.rb
CHANGED
@@ -1,176 +1,123 @@
|
|
1
|
-
|
2
|
-
require "turbot/deprecated/help"
|
3
|
-
|
4
|
-
# list commands and display help
|
1
|
+
#List commands and display help
|
5
2
|
#
|
6
3
|
class Turbot::Command::Help < Turbot::Command::Base
|
7
|
-
|
8
|
-
PRIMARY_NAMESPACES = %w( auth bots ps run addons config releases domains logs sharing )
|
9
|
-
|
10
|
-
include Turbot::Deprecated::Help
|
4
|
+
PRIMARY_NAMESPACES = Set.new(%w(auth bots))
|
11
5
|
|
12
6
|
# help [COMMAND]
|
13
7
|
#
|
14
|
-
#
|
8
|
+
#List available commands or display help for a specific command.
|
15
9
|
#
|
16
10
|
#Examples:
|
17
11
|
#
|
18
|
-
#
|
19
|
-
#
|
12
|
+
# $ turbot help
|
13
|
+
# Usage: turbot COMMAND [--bot APP] [command-specific-options]
|
20
14
|
#
|
21
|
-
#
|
15
|
+
# Primary help topics, type "turbot help TOPIC" for more details:
|
22
16
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# ...
|
17
|
+
# auth # Login or logout from Turbot
|
18
|
+
# bots # Manage bots (generate skeleton, validate data, submit code)
|
26
19
|
#
|
27
|
-
#
|
20
|
+
# Additional topics:
|
28
21
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# ...
|
22
|
+
# help # List commands and display help
|
23
|
+
# version # Display version
|
32
24
|
#
|
33
|
-
#
|
34
|
-
#
|
25
|
+
# $ turbot help auth:whoam
|
26
|
+
# Usage: turbot auth:whoami
|
35
27
|
#
|
36
|
-
#
|
28
|
+
# Display your Turbot email address.
|
37
29
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
30
|
+
# Example:
|
31
|
+
#
|
32
|
+
# $ turbot auth:whoami
|
33
|
+
# email@example.com
|
42
34
|
#
|
43
35
|
def index
|
44
|
-
|
36
|
+
command = args.shift
|
37
|
+
if command
|
45
38
|
help_for_command(command)
|
46
39
|
else
|
47
40
|
help_for_root
|
48
41
|
end
|
49
42
|
end
|
50
|
-
|
51
|
-
alias_command
|
52
|
-
alias_command "--help", "help"
|
43
|
+
alias_command '-h', 'help'
|
44
|
+
alias_command '--help', 'help'
|
53
45
|
|
54
46
|
private
|
55
47
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
def namespaces
|
63
|
-
namespaces = Turbot::Command.namespaces
|
64
|
-
namespaces.delete("bot")
|
65
|
-
namespaces
|
66
|
-
end
|
67
|
-
|
68
|
-
def commands
|
69
|
-
Turbot::Command.commands
|
70
|
-
end
|
71
|
-
|
72
|
-
def legacy_help_for_namespace(namespace)
|
73
|
-
instance = Turbot::Command::Help.groups.map do |group|
|
74
|
-
[ group.title, group.select { |c| c.first =~ /^#{namespace}/ }.length ]
|
75
|
-
end.sort_by { |l| l.last }.last
|
76
|
-
return nil unless instance
|
77
|
-
return nil if instance.last.zero?
|
78
|
-
instance.first
|
79
|
-
end
|
80
|
-
|
81
|
-
def legacy_help_for_command(command)
|
82
|
-
Turbot::Command::Help.groups.each do |group|
|
83
|
-
group.each do |cmd, description|
|
84
|
-
return description if cmd.split(" ").first == command
|
85
|
-
end
|
48
|
+
def help_for(items, name_key, description_key)
|
49
|
+
size = items.map { |namespace| namespace[name_key].size }.max
|
50
|
+
items.sort_by { |namespace| namespace[name_key] }.each do |namespace|
|
51
|
+
puts " %-#{size}s # %s" % [namespace[name_key], namespace[description_key]]
|
86
52
|
end
|
87
|
-
nil
|
88
53
|
end
|
89
54
|
|
90
|
-
|
91
|
-
return true if ns[:description] =~ /DEPRECATED:/
|
92
|
-
return true if ns[:description] =~ /HIDDEN:/
|
93
|
-
false
|
94
|
-
end
|
55
|
+
### Root
|
95
56
|
|
96
|
-
def
|
97
|
-
|
98
|
-
return true if command[:help] =~ /^ HIDDEN:/
|
99
|
-
false
|
57
|
+
def namespaces
|
58
|
+
Turbot::Command.namespaces
|
100
59
|
end
|
101
60
|
|
102
61
|
def primary_namespaces
|
103
|
-
|
62
|
+
namespaces.select { |name,_| PRIMARY_NAMESPACES.include?(name) }.values
|
104
63
|
end
|
105
64
|
|
106
65
|
def additional_namespaces
|
107
|
-
|
66
|
+
namespaces.reject { |name,_| PRIMARY_NAMESPACES.include?(name) }.values
|
108
67
|
end
|
109
68
|
|
110
69
|
def summary_for_namespaces(namespaces)
|
111
|
-
|
112
|
-
namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
|
113
|
-
next if skip_namespace?(namespace)
|
114
|
-
name = namespace[:name]
|
115
|
-
namespace[:description] ||= legacy_help_for_namespace(name)
|
116
|
-
puts " %-#{size}s # %s" % [ name, namespace[:description] ]
|
117
|
-
end
|
70
|
+
help_for(namespaces, :name, :description)
|
118
71
|
end
|
119
72
|
|
120
73
|
def help_for_root
|
121
|
-
puts
|
74
|
+
puts 'Usage: turbot COMMAND [--bot APP] [command-specific-options]'
|
122
75
|
puts
|
123
|
-
puts
|
76
|
+
puts 'Primary help topics, type "turbot help TOPIC" for more details:'
|
124
77
|
puts
|
125
78
|
summary_for_namespaces(primary_namespaces)
|
126
79
|
puts
|
127
|
-
puts
|
80
|
+
puts 'Additional topics:'
|
128
81
|
puts
|
129
82
|
summary_for_namespaces(additional_namespaces)
|
130
83
|
puts
|
131
84
|
end
|
132
85
|
|
133
|
-
|
134
|
-
|
86
|
+
### Command
|
87
|
+
|
88
|
+
def commands
|
89
|
+
Turbot::Command.commands
|
90
|
+
end
|
135
91
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
next if skip_command?(command)
|
140
|
-
command[:summary] ||= legacy_help_for_command(command[:command])
|
141
|
-
puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
|
142
|
-
end
|
92
|
+
def commands_for_namespace(name)
|
93
|
+
commands.values.select do |command|
|
94
|
+
command[:namespace] == name && command[:command] != name
|
143
95
|
end
|
144
96
|
end
|
145
97
|
|
98
|
+
def help_for_namespace(namespace_commands)
|
99
|
+
help_for(namespace_commands, :banner, :summary)
|
100
|
+
end
|
101
|
+
|
146
102
|
def help_for_command(name)
|
147
|
-
|
148
|
-
|
103
|
+
command_alias = Turbot::Command.command_aliases[name]
|
104
|
+
if command_alias
|
105
|
+
puts "Alias: #{name} redirects to #{command_alias}"
|
149
106
|
name = command_alias
|
150
107
|
end
|
151
|
-
if command = commands[name]
|
152
|
-
puts "Usage: turbot #{command[:banner]}"
|
153
108
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
puts help[1..-1].join("\n")
|
159
|
-
else
|
160
|
-
puts
|
161
|
-
puts " " + legacy_help_for_command(name).to_s
|
162
|
-
end
|
109
|
+
command = commands[name]
|
110
|
+
if command
|
111
|
+
puts "Usage: turbot #{command[:banner]}"
|
112
|
+
puts command[:help].split("\n").drop(1).join("\n")
|
163
113
|
puts
|
164
114
|
end
|
165
115
|
|
166
|
-
namespace_commands = commands_for_namespace(name)
|
167
|
-
command[:help] =~ /DEPRECATED/
|
168
|
-
end
|
169
|
-
|
116
|
+
namespace_commands = commands_for_namespace(name)
|
170
117
|
if !namespace_commands.empty?
|
171
|
-
puts
|
118
|
+
puts 'Additional commands, type "turbot help COMMAND" for more details:'
|
172
119
|
puts
|
173
|
-
help_for_namespace(
|
120
|
+
help_for_namespace(namespace_commands)
|
174
121
|
puts
|
175
122
|
elsif command.nil?
|
176
123
|
error "#{name} is not a turbot command. See `turbot help`."
|
@@ -1,23 +1,19 @@
|
|
1
|
-
|
2
|
-
require "turbot/version"
|
3
|
-
|
4
|
-
# display version
|
1
|
+
#Display version
|
5
2
|
#
|
6
3
|
class Turbot::Command::Version < Turbot::Command::Base
|
7
4
|
|
8
5
|
# version
|
9
6
|
#
|
10
|
-
#
|
7
|
+
#Show the Turbot client's version.
|
11
8
|
#
|
12
9
|
#Example:
|
13
10
|
#
|
14
|
-
#
|
15
|
-
#
|
11
|
+
# $ turbot version
|
12
|
+
# turbot-gem/0.1.36 (x86_64-darwin14) ruby/2.2.0
|
16
13
|
#
|
17
14
|
def index
|
18
15
|
validate_arguments!
|
19
|
-
|
20
|
-
display(Turbot.user_agent)
|
16
|
+
puts "#{Turbot::DEBUG_VERSION}"
|
21
17
|
end
|
22
|
-
|
18
|
+
alias_command '--version', 'version'
|
23
19
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Turbot
|
2
|
+
module Handlers
|
3
|
+
class BaseHandler < TurbotRunner::BaseHandler
|
4
|
+
# Implements `TurbotRunner::BaseHandler#handle_invalid_record`.
|
5
|
+
def handle_invalid_record(record, data_type, error_message)
|
6
|
+
puts
|
7
|
+
puts 'The following record is invalid:'
|
8
|
+
puts record.to_json
|
9
|
+
puts " * #{error_message}"
|
10
|
+
puts
|
11
|
+
end
|
12
|
+
|
13
|
+
# Implements `TurbotRunner::BaseHandler#handle_invalid_json`.
|
14
|
+
def handle_invalid_json(line)
|
15
|
+
puts
|
16
|
+
puts 'The following line was not valid JSON:'
|
17
|
+
puts line
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Turbot
|
2
|
+
module Handlers
|
3
|
+
class PreviewHandler < BaseHandler
|
4
|
+
attr_reader :count
|
5
|
+
|
6
|
+
def initialize(bot_name, api)
|
7
|
+
@bot_name = bot_name
|
8
|
+
@api = api
|
9
|
+
@count = 0
|
10
|
+
@batch = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Implements `TurbotRunner::BaseHandler#handle_valid_record`.
|
14
|
+
def handle_valid_record(record, data_type)
|
15
|
+
@count += 1
|
16
|
+
STDOUT.write('.')
|
17
|
+
@batch << record.merge(:data_type => data_type)
|
18
|
+
if @count % 20 == 0
|
19
|
+
submit_batch
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def submit_batch
|
24
|
+
result = @api.create_draft_data(@bot_name, @batch.to_json)
|
25
|
+
@batch = []
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Turbot
|
2
|
+
module Handlers
|
3
|
+
class ValidationHandler < BaseHandler
|
4
|
+
attr_reader :count
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@count = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
# Implements `TurbotRunner::BaseHandler#handle_valid_record`.
|
11
|
+
def handle_valid_record(record, data_type)
|
12
|
+
@count += 1
|
13
|
+
STDOUT.write('.')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/turbot/helpers.rb
CHANGED
@@ -1,368 +1,27 @@
|
|
1
|
-
require "vendor/turbot/okjson"
|
2
|
-
|
3
1
|
module Turbot
|
4
2
|
module Helpers
|
3
|
+
DEFAULT_HOST = 'http://turbot.opencorporates.com'
|
5
4
|
|
6
5
|
extend self
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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)
|
7
|
+
# Returns the base URL of the Turbot server.
|
8
|
+
#
|
9
|
+
# @return [String] the base URL of the Turbot server
|
10
|
+
def host
|
11
|
+
ENV['TURBOT_HOST'] || DEFAULT_HOST
|
250
12
|
end
|
251
13
|
|
252
14
|
def error(message)
|
253
|
-
|
254
|
-
|
255
|
-
Turbot::Helpers.error_with_failure = false
|
256
|
-
end
|
257
|
-
$stderr.puts(format_with_bang(message))
|
15
|
+
prefix = ' ! '
|
16
|
+
$stderr.puts(prefix + message.split("\n").join("\n#{prefix}"))
|
258
17
|
exit(1)
|
259
18
|
end
|
260
19
|
|
261
|
-
def
|
262
|
-
|
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
|
20
|
+
def styled_error(error, message = 'Turbot client internal error.')
|
21
|
+
$stderr.puts(format_error(error, message))
|
363
22
|
end
|
364
23
|
|
365
|
-
def format_error(error, message='Turbot client internal error.')
|
24
|
+
def format_error(error, message = 'Turbot client internal error.')
|
366
25
|
formatted_error = []
|
367
26
|
formatted_error << " ! #{message}"
|
368
27
|
formatted_error << ' ! Report a bug at: https://github.com/openc/turbot-client/issues/new'
|
@@ -383,139 +42,12 @@ module Turbot
|
|
383
42
|
end
|
384
43
|
end.join(' ')
|
385
44
|
formatted_error << " Command: turbot #{command}"
|
386
|
-
|
387
|
-
|
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}"
|
45
|
+
unless host == DEFAULT_HOST
|
46
|
+
formatted_error << " Host: #{host}"
|
392
47
|
end
|
393
|
-
|
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}"
|
48
|
+
formatted_error << " Version: #{Turbot::DEBUG_VERSION}"
|
408
49
|
formatted_error << "\n"
|
409
50
|
formatted_error.join("\n")
|
410
51
|
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
52
|
end
|
521
53
|
end
|