pbox 1.17.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/COPYRIGHT +1 -0
  3. data/LICENSE +11 -0
  4. data/README.md +40 -0
  5. data/Rakefile +6 -0
  6. data/autocomplete/pbox_bash +1639 -0
  7. data/bin/pbox +37 -0
  8. data/conf/protonbox.conf +8 -0
  9. data/features/assets/deploy.tar.gz +0 -0
  10. data/features/core_feature.rb +178 -0
  11. data/features/deployments_feature.rb +127 -0
  12. data/features/domains_feature.rb +49 -0
  13. data/features/keys_feature.rb +37 -0
  14. data/features/members_feature.rb +166 -0
  15. data/lib/rhc/auth/basic.rb +64 -0
  16. data/lib/rhc/auth/token.rb +102 -0
  17. data/lib/rhc/auth/token_store.rb +53 -0
  18. data/lib/rhc/auth.rb +5 -0
  19. data/lib/rhc/autocomplete.rb +66 -0
  20. data/lib/rhc/autocomplete_templates/bash.erb +39 -0
  21. data/lib/rhc/cartridge_helpers.rb +118 -0
  22. data/lib/rhc/cli.rb +40 -0
  23. data/lib/rhc/command_runner.rb +186 -0
  24. data/lib/rhc/commands/account.rb +25 -0
  25. data/lib/rhc/commands/alias.rb +124 -0
  26. data/lib/rhc/commands/app.rb +701 -0
  27. data/lib/rhc/commands/apps.rb +20 -0
  28. data/lib/rhc/commands/authorization.rb +96 -0
  29. data/lib/rhc/commands/base.rb +174 -0
  30. data/lib/rhc/commands/cartridge.rb +326 -0
  31. data/lib/rhc/commands/deployment.rb +82 -0
  32. data/lib/rhc/commands/domain.rb +167 -0
  33. data/lib/rhc/commands/env.rb +142 -0
  34. data/lib/rhc/commands/git_clone.rb +29 -0
  35. data/lib/rhc/commands/logout.rb +51 -0
  36. data/lib/rhc/commands/member.rb +148 -0
  37. data/lib/rhc/commands/port_forward.rb +197 -0
  38. data/lib/rhc/commands/server.rb +40 -0
  39. data/lib/rhc/commands/setup.rb +60 -0
  40. data/lib/rhc/commands/snapshot.rb +137 -0
  41. data/lib/rhc/commands/ssh.rb +51 -0
  42. data/lib/rhc/commands/sshkey.rb +97 -0
  43. data/lib/rhc/commands/tail.rb +47 -0
  44. data/lib/rhc/commands/threaddump.rb +14 -0
  45. data/lib/rhc/commands.rb +396 -0
  46. data/lib/rhc/config.rb +320 -0
  47. data/lib/rhc/context_helper.rb +121 -0
  48. data/lib/rhc/core_ext.rb +202 -0
  49. data/lib/rhc/coverage_helper.rb +33 -0
  50. data/lib/rhc/deployment_helpers.rb +88 -0
  51. data/lib/rhc/exceptions.rb +232 -0
  52. data/lib/rhc/git_helpers.rb +91 -0
  53. data/lib/rhc/help_formatter.rb +55 -0
  54. data/lib/rhc/helpers.rb +477 -0
  55. data/lib/rhc/highline_extensions.rb +479 -0
  56. data/lib/rhc/json.rb +51 -0
  57. data/lib/rhc/output_helpers.rb +260 -0
  58. data/lib/rhc/rest/activation.rb +11 -0
  59. data/lib/rhc/rest/alias.rb +42 -0
  60. data/lib/rhc/rest/api.rb +87 -0
  61. data/lib/rhc/rest/application.rb +332 -0
  62. data/lib/rhc/rest/attributes.rb +36 -0
  63. data/lib/rhc/rest/authorization.rb +8 -0
  64. data/lib/rhc/rest/base.rb +79 -0
  65. data/lib/rhc/rest/cartridge.rb +154 -0
  66. data/lib/rhc/rest/client.rb +650 -0
  67. data/lib/rhc/rest/deployment.rb +18 -0
  68. data/lib/rhc/rest/domain.rb +98 -0
  69. data/lib/rhc/rest/environment_variable.rb +15 -0
  70. data/lib/rhc/rest/gear_group.rb +16 -0
  71. data/lib/rhc/rest/httpclient.rb +145 -0
  72. data/lib/rhc/rest/key.rb +44 -0
  73. data/lib/rhc/rest/membership.rb +105 -0
  74. data/lib/rhc/rest/mock.rb +1024 -0
  75. data/lib/rhc/rest/user.rb +32 -0
  76. data/lib/rhc/rest.rb +148 -0
  77. data/lib/rhc/ssh_helpers.rb +378 -0
  78. data/lib/rhc/tar_gz.rb +51 -0
  79. data/lib/rhc/usage_templates/command_help.erb +51 -0
  80. data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
  81. data/lib/rhc/usage_templates/help.erb +35 -0
  82. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  83. data/lib/rhc/usage_templates/options_help.erb +12 -0
  84. data/lib/rhc/vendor/okjson.rb +600 -0
  85. data/lib/rhc/vendor/parseconfig.rb +178 -0
  86. data/lib/rhc/vendor/sshkey.rb +253 -0
  87. data/lib/rhc/vendor/zliby.rb +628 -0
  88. data/lib/rhc/version.rb +5 -0
  89. data/lib/rhc/wizard.rb +633 -0
  90. data/lib/rhc.rb +34 -0
  91. data/spec/coverage_helper.rb +89 -0
  92. data/spec/direct_execution_helper.rb +338 -0
  93. data/spec/keys/example.pem +23 -0
  94. data/spec/keys/example_private.pem +27 -0
  95. data/spec/keys/server.pem +19 -0
  96. data/spec/rest_spec_helper.rb +31 -0
  97. data/spec/rhc/assets/cert.crt +22 -0
  98. data/spec/rhc/assets/cert_key_rsa +27 -0
  99. data/spec/rhc/assets/empty.txt +0 -0
  100. data/spec/rhc/assets/env_vars.txt +7 -0
  101. data/spec/rhc/assets/env_vars_2.txt +1 -0
  102. data/spec/rhc/assets/foo.txt +1 -0
  103. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  104. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  105. data/spec/rhc/auth_spec.rb +442 -0
  106. data/spec/rhc/cli_spec.rb +188 -0
  107. data/spec/rhc/command_spec.rb +435 -0
  108. data/spec/rhc/commands/account_spec.rb +42 -0
  109. data/spec/rhc/commands/alias_spec.rb +333 -0
  110. data/spec/rhc/commands/app_spec.rb +754 -0
  111. data/spec/rhc/commands/apps_spec.rb +39 -0
  112. data/spec/rhc/commands/authorization_spec.rb +145 -0
  113. data/spec/rhc/commands/cartridge_spec.rb +641 -0
  114. data/spec/rhc/commands/deployment_spec.rb +286 -0
  115. data/spec/rhc/commands/domain_spec.rb +383 -0
  116. data/spec/rhc/commands/env_spec.rb +493 -0
  117. data/spec/rhc/commands/git_clone_spec.rb +80 -0
  118. data/spec/rhc/commands/logout_spec.rb +86 -0
  119. data/spec/rhc/commands/member_spec.rb +228 -0
  120. data/spec/rhc/commands/port_forward_spec.rb +217 -0
  121. data/spec/rhc/commands/server_spec.rb +69 -0
  122. data/spec/rhc/commands/setup_spec.rb +118 -0
  123. data/spec/rhc/commands/snapshot_spec.rb +179 -0
  124. data/spec/rhc/commands/ssh_spec.rb +163 -0
  125. data/spec/rhc/commands/sshkey_spec.rb +188 -0
  126. data/spec/rhc/commands/tail_spec.rb +81 -0
  127. data/spec/rhc/commands/threaddump_spec.rb +84 -0
  128. data/spec/rhc/config_spec.rb +407 -0
  129. data/spec/rhc/helpers_spec.rb +524 -0
  130. data/spec/rhc/highline_extensions_spec.rb +314 -0
  131. data/spec/rhc/json_spec.rb +30 -0
  132. data/spec/rhc/rest_application_spec.rb +248 -0
  133. data/spec/rhc/rest_client_spec.rb +752 -0
  134. data/spec/rhc/rest_spec.rb +740 -0
  135. data/spec/rhc/targz_spec.rb +55 -0
  136. data/spec/rhc/wizard_spec.rb +756 -0
  137. data/spec/spec_helper.rb +575 -0
  138. data/spec/wizard_spec_helper.rb +330 -0
  139. metadata +435 -0
@@ -0,0 +1,102 @@
1
+ module RHC::Auth
2
+ class Token
3
+ def initialize(opt, auth=nil, store=nil)
4
+ if opt.is_a?(String)
5
+ @token = opt
6
+ else
7
+ @options = opt || Commander::Command::Options.new
8
+ @token = options[:token]
9
+ @no_interactive = options[:noprompt]
10
+ @allows_tokens = options[:use_authorization_tokens]
11
+ end
12
+ @auth = auth
13
+ @store = store
14
+ read_token
15
+ end
16
+
17
+ def to_request(request)
18
+ if token
19
+ debug "Using token authentication"
20
+ (request[:headers] ||= {})['authorization'] = "Bearer #{token}"
21
+ elsif auth and (!@allows_tokens or @can_get_token == false)
22
+ debug "Bypassing token auth"
23
+ auth.to_request(request)
24
+ end
25
+ request
26
+ end
27
+
28
+ def retry_auth?(response, client)
29
+ if response.status == 401
30
+ token_rejected(response, client)
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ def username
37
+ auth && auth.respond_to?(:username) && auth.username || options[:username]
38
+ end
39
+
40
+ def save(token)
41
+ store.put(username, protonbox_server, token) if store
42
+ @token = token
43
+ end
44
+
45
+ def can_authenticate?
46
+ token || auth && auth.can_authenticate?
47
+ end
48
+
49
+ protected
50
+ include RHC::Helpers
51
+ attr_reader :options, :token, :auth, :store
52
+
53
+ def token_rejected(response, client)
54
+ has_token = !!token
55
+ @token = nil
56
+
57
+ unless auth && auth.can_authenticate?
58
+ if has_token
59
+ raise RHC::Rest::TokenExpiredOrInvalid, "Your authorization token is expired or invalid."
60
+ end
61
+ debug "Cannot authenticate via token or password, exiting"
62
+ return false
63
+ end
64
+
65
+ if has_token
66
+ if cannot_retry?
67
+ raise RHC::Rest::TokenExpiredOrInvalid, "Your authorization token is expired or invalid."
68
+ end
69
+ if not client.supports_sessions?
70
+ raise RHC::Rest::AuthorizationsNotSupported
71
+ end
72
+ end
73
+
74
+ @can_get_token = client.supports_sessions? && @allows_tokens
75
+
76
+ if has_token
77
+ warn "Your authorization token has expired. Please sign in now to continue."
78
+ elsif @can_get_token
79
+ info "Please sign in to start a new session to #{protonbox_server}."
80
+ end
81
+
82
+ return auth.retry_auth?(response, client) unless @can_get_token
83
+
84
+ debug "Creating a new authorization token"
85
+ if auth_token = client.new_session(:auth => auth)
86
+ @fetch_once = true
87
+ save(auth_token.token)
88
+ true
89
+ else
90
+ auth.retry_auth?(response, client)
91
+ end
92
+ end
93
+
94
+ def read_token
95
+ @token ||= store.get(username, protonbox_server) if store
96
+ end
97
+
98
+ def cannot_retry?
99
+ !@fetch_once && @no_interactive
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,53 @@
1
+ require 'base64'
2
+
3
+ module RHC::Auth
4
+ class TokenStore
5
+ def initialize(dir)
6
+ @dir = dir
7
+ end
8
+
9
+ def get(login, server)
10
+ self[key(login,server)]
11
+ end
12
+
13
+ def put(login, server, token)
14
+ self[key(login,server)] = token
15
+ end
16
+
17
+ def clear
18
+ Dir[File.join(@dir, "token_*")].
19
+ each{ |f| File.delete(f) unless File.directory?(f) }.
20
+ present?
21
+ end
22
+
23
+ private
24
+ def path(key)
25
+ File.join(@dir, filename(key))
26
+ end
27
+
28
+ def filename(key)
29
+ "token_#{Base64.encode64(Digest::MD5.digest(key)).gsub(/[^\w\@]/,'')}"
30
+ end
31
+
32
+ def []=(key, value)
33
+ file = path(key)
34
+ FileUtils.mkdir_p File.dirname(file)
35
+ File.open(file, 'w'){ |f| f.write(value) }
36
+ File.chmod(0600, file)
37
+ value
38
+ end
39
+
40
+ def [](key)
41
+ s = IO.read(path(key)).presence
42
+ s = s.strip.gsub(/[\n\r\t]/,'') if s
43
+ s
44
+ rescue Errno::ENOENT
45
+ nil
46
+ end
47
+
48
+ def key(login, server)
49
+ "#{login || ''}@#{server}"
50
+ end
51
+
52
+ end
53
+ end
data/lib/rhc/auth.rb ADDED
@@ -0,0 +1,5 @@
1
+ module RHC::Auth
2
+ autoload :Basic, 'rhc/auth/basic'
3
+ autoload :Token, 'rhc/auth/token'
4
+ autoload :TokenStore, 'rhc/auth/token_store'
5
+ end
@@ -0,0 +1,66 @@
1
+ module RHC
2
+ class AutoComplete
3
+ attr_reader :runner
4
+
5
+ def initialize(runner=::Commander::Runner.instance, shell='bash')
6
+ @runner, @shell = runner, shell
7
+ end
8
+
9
+ def to_s
10
+ @s ||= template.result AutoCompleteBindings.new(self).get_binding
11
+ end
12
+
13
+ private
14
+
15
+ def template
16
+ @template ||= ERB.new(File.read(File.join(File.dirname(__FILE__), 'autocomplete_templates', "#{@shell}.erb")), nil, '-')
17
+ end
18
+ end
19
+
20
+ class AutoCompleteBindings
21
+ attr_reader :commands, :top_level_commands, :global_options
22
+
23
+ def initialize(data)
24
+ @commands = {}
25
+ @top_level_commands = []
26
+
27
+ data.runner.commands.each_pair do |name, cmd|
28
+ next if cmd.summary.nil?
29
+ next if cmd.deprecated(name)
30
+
31
+ if cmd.root?
32
+ if cmd.name == name
33
+ @top_level_commands << name
34
+ end
35
+ else
36
+ @top_level_commands << name if name == cmd.name
37
+ commands = name.split ' '
38
+ action = commands.pop
39
+ id = commands.join(' ')
40
+ v = @commands[id] || {:actions => [], :switches => []}
41
+ v[:actions] << action unless id == '' && name != cmd.name
42
+ @commands[id] = v
43
+ end
44
+
45
+ v = @commands[name.to_s] || {:actions => [], :switches => []}
46
+ v[:switches].concat(cmd.options.map do |o|
47
+ if o[:switches]
48
+ s = o[:switches][-1].split(' ')[0]
49
+ if m = /--\[no-\](.+)/.match(s)
50
+ s = ["--#{m[1]}", "--no-#{m[1]}"]
51
+ else
52
+ s
53
+ end
54
+ end
55
+ end.flatten.compact.sort)
56
+ @commands[name.to_s] = v
57
+ end
58
+ @commands.delete('')
59
+ @commands = @commands.to_a.sort{ |a,b| a[0] <=> b[0] }
60
+
61
+ @top_level_commands.sort!
62
+
63
+ @global_options = data.runner.options.map{ |o| o[:switches][-1].split(' ')[0] }.sort
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ #
2
+ # This is the bash auto completion script for the pbox command
3
+ #
4
+ _pbox()
5
+ {
6
+ local cur opts prev
7
+ COMPREPLY=()
8
+ cur="${COMP_WORDS[COMP_CWORD]}"
9
+ if [ $COMP_CWORD -eq 1 ]; then
10
+ if [[ "$cur" == -* ]]; then
11
+ opts=<%= "\"%s\"" % global_options.join(' ') %>
12
+ elif [ -z $cur ]; then
13
+ opts=<%= "\"%s\"" % top_level_commands.join(' ') %>
14
+ else
15
+ opts=<%= "\"%s\"" % commands.map{ |c| c.first }.delete_if{ |s| s.include? ' ' }.sort.join(' ') %>
16
+ fi
17
+ else
18
+ prev="${COMP_WORDS[@]:0:COMP_CWORD}"
19
+ SAVE_IFS=$IFS
20
+ IFS=" "
21
+ case "${prev[*]}" in
22
+ <%- for name, data in commands %>
23
+ <%= "\"pbox %s\")" % name %>
24
+ if [[ "$cur" == -* ]]; then
25
+ opts=<%= "\"%s\"" % data[:switches].join(" ") %>
26
+ else
27
+ opts=<%= "\"%s\"" % data[:actions].join(" ") %>
28
+ fi
29
+ ;;
30
+ <%- end %>
31
+ esac
32
+ IFS=$SAVE_IFS
33
+ fi
34
+
35
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
36
+ return 0
37
+ }
38
+
39
+ complete -o default -F _pbox pbox
@@ -0,0 +1,118 @@
1
+ module RHC
2
+ module CartridgeHelpers
3
+
4
+ protected
5
+ def check_cartridges(names, opts={}, &block)
6
+ cartridge_names = Array(names).map{ |s| s.strip if s && s.length > 0 }.compact
7
+ from = opts[:from] || all_cartridges
8
+
9
+ cartridge_names.map do |name|
10
+ next use_cart(RHC::Rest::Cartridge.for_url(name), name) if name =~ %r(\Ahttps?://)i
11
+
12
+ name = name.downcase
13
+ from.find{ |c| c.name.downcase == name } ||
14
+ begin
15
+ carts = from.select{ |c| match_cart(c, name) }
16
+ if carts.empty?
17
+ paragraph { list_cartridges(from) }
18
+ raise RHC::CartridgeNotFoundException, "There are no cartridges that match '#{name}'."
19
+ elsif carts.length == 1
20
+ use_cart(carts.first, name)
21
+ else
22
+ carts.sort!.instance_variable_set(:@for, name)
23
+ carts
24
+ end
25
+ end
26
+ end.tap do |carts|
27
+ yield carts if block_given?
28
+ end.each do |carts|
29
+ if carts.is_a? Array
30
+ name = carts.instance_variable_get(:@for)
31
+ paragraph { list_cartridges(carts) }
32
+ raise RHC::MultipleCartridgesException, "There are multiple cartridges matching '#{name}'. Please provide the short name of the correct cart."
33
+ end
34
+ end
35
+ end
36
+
37
+ def use_cart(cart, for_cartridge_name)
38
+ if cart.name.blank? and cart.custom?
39
+ info "The cartridge '#{cart.url}' will be downloaded and installed"
40
+ else
41
+ info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} for '#{for_cartridge_name}'"
42
+ end
43
+ cart
44
+ end
45
+
46
+ def match_cart(cart, search)
47
+ search = search.to_s.downcase.gsub(/[_\-\s]/,' ')
48
+ [
49
+ cart.name,
50
+ (cart.tags || []).join(' '),
51
+ ].compact.any?{ |s| s.present? && s.downcase.gsub(/[_\-\s]/,' ').include?(search) } ||
52
+ search.length > 2 && [
53
+ cart.description
54
+ ].compact.any?{ |s| s.present? && !s.downcase.match(/\b#{search}\b/).nil? }
55
+ end
56
+
57
+ def web_carts_only
58
+ lambda{ |cart|
59
+ next cart unless cart.is_a? Array
60
+ name = cart.instance_variable_get(:@for)
61
+ matching = cart.select{ |c| not c.only_in_existing? }
62
+ if matching.size == 1
63
+ use_cart(matching.first, name)
64
+ else
65
+ matching.instance_variable_set(:@for, name)
66
+ matching
67
+ end
68
+ }
69
+ end
70
+
71
+ def other_carts_only
72
+ lambda{ |cart|
73
+ next cart unless cart.is_a? Array
74
+ name = cart.instance_variable_get(:@for)
75
+ matching = cart.select{ |c| not c.only_in_new? }
76
+ if matching.size == 1
77
+ use_cart(matching.first, name)
78
+ else
79
+ matching.instance_variable_set(:@for, name)
80
+ matching
81
+ end
82
+ }
83
+ end
84
+
85
+ def standalone_cartridges
86
+ @standalone_cartridges ||= all_cartridges.select{ |c| c.type == 'standalone' }
87
+ end
88
+
89
+ def not_standalone_cartridges
90
+ @not_standalone_cartridges ||= all_cartridges.select{ |c| c.type != 'standalone' }
91
+ end
92
+
93
+ def all_cartridges
94
+ @all_cartridges = rest_client.cartridges
95
+ end
96
+
97
+ def list_cartridges(cartridges)
98
+ carts = cartridges.map{ |c| [c.name, c.display_name || ''] }.sort{ |a,b| a[1].downcase <=> b[1].downcase }
99
+ carts.unshift ['==========', '=========']
100
+ carts.unshift ['Short Name', 'Full name']
101
+ say table(carts)
102
+ end
103
+
104
+ def filter_jenkins_cartridges(tag)
105
+ cartridges = all_cartridges.select { |c| (c.tags || []).include?(tag) && c.name =~ /\Ajenkins/i }.sort
106
+ raise RHC::JenkinsNotInstalledOnServer if cartridges.empty?
107
+ cartridges
108
+ end
109
+
110
+ def jenkins_cartridges
111
+ @jenkins_cartridges ||= filter_jenkins_cartridges('ci')
112
+ end
113
+
114
+ def jenkins_client_cartridges
115
+ @jenkins_client_cartridges ||= filter_jenkins_cartridges('ci_builder')
116
+ end
117
+ end
118
+ end
data/lib/rhc/cli.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'rhc'
2
+ require 'rhc/commands'
3
+
4
+ module RHC
5
+ #
6
+ # Run and execute a command line session with the PBOX tools.
7
+ #
8
+ # You can invoke the CLI with:
9
+ # bundle exec ruby -e 'require "rhc/cli"; RHC::CLI.start(ARGV);' -- <arguments>
10
+ #
11
+ # from the gem directory.
12
+ #
13
+ module CLI
14
+ extend Commander::Delegates
15
+
16
+ def self.set_terminal
17
+ $terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first rescue 80 if $stdin.tty?
18
+ $terminal.wrap_at = nil if $terminal.wrap_at == 0
19
+ #$terminal.page_at = :auto if $stdin.tty? and $stdout.tty?
20
+ # FIXME: ANSI terminals are not default on windows but we may just be
21
+ # hitting a bug in highline if windows does support another method.
22
+ # This is a safe fix for now but needs more research.
23
+ HighLine::use_color = false if RHC::Helpers.windows? or not $stdout.tty?
24
+ end
25
+
26
+ def self.start(args)
27
+ runner = RHC::CommandRunner.new(args)
28
+ Commander::Runner.instance_variable_set :@singleton, runner
29
+
30
+ program :name, 'pbox'
31
+ program :description, 'Command line interface for ProtonBox.'
32
+ program :version, RHC::VERSION::STRING
33
+ program :help_formatter, RHC::HelpFormatter
34
+ program :int_message, " Interrupted\n"
35
+
36
+ RHC::Commands.load.to_commander
37
+ run! || 0
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,186 @@
1
+ module RHC
2
+ class CommandRunner < Commander::Runner
3
+ # regex fix from git - match on word boundries
4
+ def valid_command_names_from *args
5
+ arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
6
+ commands.keys.find_all { |name| name if /^#{name}\b/.match arg_string }
7
+ end
8
+
9
+ if Commander::VERSION == '4.0.3'
10
+ #:nocov:
11
+ def program(*args)
12
+ Array(super).first
13
+ end
14
+ #:nocov:
15
+ end
16
+
17
+ def options_parse_trace
18
+ if @args.include?("--trace")
19
+ @args.delete "--trace"
20
+ return true
21
+ end
22
+ false
23
+ end
24
+
25
+ def options_parse_debug
26
+ if @args.include?("-d") or @args.include?("--debug")
27
+ @args.delete "-d"
28
+ @args.delete "--debug"
29
+ return true
30
+ end
31
+ false
32
+ end
33
+
34
+ def options_parse_version
35
+ if @args.include? "--version"
36
+ say version
37
+ exit 0
38
+ end
39
+ end
40
+
41
+ HELP_OPTIONS = ['--help', '-h']
42
+
43
+ def options_parse_help
44
+ if (@args & HELP_OPTIONS).present?
45
+ args = (@args -= HELP_OPTIONS)
46
+ args.shift if args.first == 'help' && !command_exists?(args.join(' '))
47
+ exit run_help(args)
48
+ end
49
+ end
50
+
51
+ # override so we can do our own error handling
52
+ def run!
53
+ trace = false
54
+ require_program :version, :description
55
+
56
+ global_option('-h', '--help', 'Help on any command', :hide => true)
57
+ global_option('--version', 'Display version information', :hide => true)
58
+
59
+ # special case --debug so all commands can output relevant info on it
60
+ $terminal.debug = options_parse_debug
61
+
62
+ # special case --trace because we need to use it in the runner
63
+ trace = options_parse_trace
64
+
65
+ # special case --version so it is processed before an invalid command
66
+ options_parse_version
67
+
68
+ # help is a special branch prior to command execution
69
+ options_parse_help
70
+
71
+ unless trace
72
+ begin
73
+ run_active_command
74
+ rescue InvalidCommandError => e
75
+ return RHC::Wizard.new.run unless RHC::Wizard.has_configuration? || provided_arguments.present?
76
+ run_help(provided_arguments)
77
+ rescue \
78
+ OptionParser::InvalidOption => e
79
+ RHC::Helpers.error e.message
80
+ 1
81
+ rescue \
82
+ ArgumentError,
83
+ OptionParser::ParseError => e
84
+
85
+ help_bindings = CommandHelpBindings.new(active_command, commands, self)
86
+ usage = RHC::HelpFormatter.new(self).render_command_syntax(help_bindings)
87
+ message = case e
88
+ when OptionParser::AmbiguousOption
89
+ "The option #{e.args.join(' ')} is ambiguous. You will need to specify the entire option."
90
+ else
91
+ e.message
92
+ end
93
+
94
+ RHC::Helpers.error message
95
+ say "#{usage}"
96
+ 1
97
+ rescue RHC::Exception, RHC::Rest::Exception => e
98
+ RHC::Helpers.error e.message
99
+ e.code.nil? ? 128 : [1, (e.code || 1).to_i].max
100
+ end
101
+ else
102
+ run_active_command
103
+ end
104
+ end
105
+
106
+ def provided_arguments
107
+ @args[0, @args.find_index { |arg| arg != '--' and arg.start_with?('-') } || @args.length]
108
+ end
109
+
110
+ def global_option(*args, &block)
111
+ opts = args.pop if Hash === args.last
112
+ super(*args, &block).tap do |options|
113
+ options.last.merge!(opts) if opts
114
+ end
115
+ end
116
+
117
+ def create_default_commands
118
+ command 'help options' do |c|
119
+ c.description = "Display all global options and information about configuration"
120
+ c.when_called do |args, options|
121
+ say help_formatter.render_options self
122
+ 0
123
+ end
124
+ end
125
+ command :help do |c|
126
+ c.syntax = '<command>'
127
+ c.description = 'Display global or <command> help documentation.'
128
+ c.when_called(&method(:run_help))
129
+ end
130
+ end
131
+
132
+ def run_help(args=[], options=nil)
133
+ args.delete_if{ |a| a.start_with? '-' }
134
+ unless args[0] == 'commands'
135
+ variations = (1..args.length).reverse_each.map{ |n| args[0,n].join('-') }
136
+ cmd = variations.find{ |cmd| command_exists?(cmd) }
137
+ end
138
+
139
+ if args.empty?
140
+ say help_formatter.render
141
+ 0
142
+ else
143
+ if cmd.nil?
144
+ matches = (variations || ['']).inject(nil) do |candidates, term|
145
+ term = term.downcase
146
+ keys = commands.keys.map(&:downcase)
147
+ prefix, keys = keys.partition{ |n| n.start_with? term }
148
+ inline, keys = keys.partition{ |n| n.include? term }
149
+ break [term, prefix, inline] unless prefix.empty? && inline.empty?
150
+ end
151
+
152
+ unless matches
153
+ RHC::Helpers.error "The command '#{program :name} #{provided_arguments.join(' ')}' is not recognized.\n"
154
+ say "See '#{program :name} help' for a list of valid commands."
155
+ return 1
156
+ end
157
+
158
+ candidates = (matches[1] + matches[2]).map{ |n| commands[n] }.uniq.sort_by{ |c| c.name }
159
+ if candidates.length == 1
160
+ cmd = candidates.first.name
161
+ else
162
+ RHC::Helpers.pager
163
+ RHC::Helpers.say matches[0] != '' ? "Showing commands matching '#{matches[0]}'" : "Showing all commands"
164
+ candidates.reverse.each do |command|
165
+ RHC::Helpers.paragraph do
166
+ aliases = (commands.map{ |(k,v)| k if command == v }.compact - [command.name]).map{ |s| "'#{s}'"}
167
+ aliases[0] = "(also #{aliases[0]}" if aliases[0]
168
+ aliases[-1] << ')' if aliases[0]
169
+
170
+ RHC::Helpers.header [RHC::Helpers.color(command.name, :cyan), *aliases.join(', ')]
171
+ say command.description || command.summary
172
+ end
173
+ end
174
+ return 1
175
+ end
176
+ end
177
+
178
+ RHC::Helpers.pager
179
+ command = command(cmd)
180
+ help_bindings = CommandHelpBindings.new command, commands, self
181
+ say help_formatter.render_command help_bindings
182
+ 0
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,25 @@
1
+ module RHC::Commands
2
+ class Account < Base
3
+ suppress_wizard
4
+
5
+ summary "Display details about your ProtonBox account"
6
+ description <<-DESC
7
+ Shows who you are logged in to the server as and the capabilities
8
+ available to you on this server.
9
+
10
+ To access more details about your account please visit the website.
11
+ DESC
12
+ def run
13
+ user = rest_client.user
14
+
15
+ say format_table \
16
+ ["Login #{user.login} on #{protonbox_server}"],
17
+ get_properties(user, :id, :plan_id, :consumed_gears, :max_gears, :max_domains).
18
+ concat(get_properties(user.capabilities, :gear_sizes)).
19
+ push(['SSL Certificates:', user.capabilities.private_ssl_certificates ? 'yes' : 'no']),
20
+ :delete => true
21
+
22
+ 0
23
+ end
24
+ end
25
+ end