gitlab 3.5.0 → 3.6.0

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -1
  3. data/README.md +17 -0
  4. data/Rakefile +1 -1
  5. data/exe/gitlab +1 -1
  6. data/lib/gitlab.rb +5 -3
  7. data/lib/gitlab/cli.rb +1 -1
  8. data/lib/gitlab/cli_helpers.rb +24 -26
  9. data/lib/gitlab/client.rb +1 -1
  10. data/lib/gitlab/client/branches.rb +2 -5
  11. data/lib/gitlab/client/commits.rb +5 -5
  12. data/lib/gitlab/client/groups.rb +8 -9
  13. data/lib/gitlab/client/issues.rb +7 -7
  14. data/lib/gitlab/client/labels.rb +3 -3
  15. data/lib/gitlab/client/merge_requests.rb +7 -7
  16. data/lib/gitlab/client/milestones.rb +5 -5
  17. data/lib/gitlab/client/namespaces.rb +1 -1
  18. data/lib/gitlab/client/notes.rb +7 -7
  19. data/lib/gitlab/client/projects.rb +22 -21
  20. data/lib/gitlab/client/repositories.rb +7 -7
  21. data/lib/gitlab/client/repository_files.rb +10 -10
  22. data/lib/gitlab/client/snippets.rb +6 -6
  23. data/lib/gitlab/client/system_hooks.rb +1 -1
  24. data/lib/gitlab/client/users.rb +5 -6
  25. data/lib/gitlab/configuration.rb +1 -1
  26. data/lib/gitlab/help.rb +19 -15
  27. data/lib/gitlab/objectified_hash.rb +2 -2
  28. data/lib/gitlab/page_links.rb +33 -0
  29. data/lib/gitlab/paginated_response.rb +97 -0
  30. data/lib/gitlab/request.rb +24 -26
  31. data/lib/gitlab/shell.rb +4 -5
  32. data/lib/gitlab/shell_history.rb +3 -5
  33. data/lib/gitlab/version.rb +1 -1
  34. data/spec/gitlab/cli_helpers_spec.rb +8 -9
  35. data/spec/gitlab/cli_spec.rb +0 -2
  36. data/spec/gitlab/client/branches_spec.rb +2 -2
  37. data/spec/gitlab/client/commits_spec.rb +16 -16
  38. data/spec/gitlab/client/groups_spec.rb +11 -14
  39. data/spec/gitlab/client/issues_spec.rb +9 -9
  40. data/spec/gitlab/client/labels_spec.rb +6 -6
  41. data/spec/gitlab/client/merge_requests_spec.rb +23 -23
  42. data/spec/gitlab/client/milestones_spec.rb +9 -9
  43. data/spec/gitlab/client/namespaces_spec.rb +2 -2
  44. data/spec/gitlab/client/notes_spec.rb +10 -10
  45. data/spec/gitlab/client/projects_spec.rb +29 -28
  46. data/spec/gitlab/client/repositories_spec.rb +7 -7
  47. data/spec/gitlab/client/snippets_spec.rb +7 -7
  48. data/spec/gitlab/client/system_hooks_spec.rb +2 -2
  49. data/spec/gitlab/client/users_spec.rb +20 -21
  50. data/spec/gitlab/help_spec.rb +1 -4
  51. data/spec/gitlab/objectified_hash_spec.rb +2 -2
  52. data/spec/gitlab/page_links_spec.rb +16 -0
  53. data/spec/gitlab/paginated_response_spec.rb +60 -0
  54. data/spec/gitlab/request_spec.rb +12 -13
  55. data/spec/gitlab/shell_history_spec.rb +1 -1
  56. data/spec/gitlab/shell_spec.rb +6 -6
  57. data/spec/spec_helper.rb +12 -12
  58. metadata +8 -2
@@ -5,7 +5,6 @@ module Gitlab::Help
5
5
  extend Gitlab::CLI::Helpers
6
6
 
7
7
  class << self
8
-
9
8
  # Returns the (modified) help from the 'ri' command or returns an error.
10
9
  #
11
10
  # @return [String]
@@ -15,7 +14,7 @@ module Gitlab::Help
15
14
  if cmd_namespace
16
15
  ri_output = `#{ri_cmd} -T #{cmd_namespace} 2>&1`.chomp
17
16
 
18
- if $? == 0
17
+ if $CHILD_STATUS == 0
19
18
  change_help_output! cmd, ri_output
20
19
  yield ri_output if block_given?
21
20
 
@@ -34,7 +33,7 @@ module Gitlab::Help
34
33
  def ri_cmd
35
34
  which_ri = `which ri`.chomp
36
35
  if which_ri.empty?
37
- raise "'ri' tool not found in $PATH. Please install it to use the help."
36
+ fail "'ri' tool not found in $PATH. Please install it to use the help."
38
37
  end
39
38
 
40
39
  which_ri
@@ -47,19 +46,19 @@ module Gitlab::Help
47
46
  # @return [Hash<Array>]
48
47
  def help_map
49
48
  @help_map ||= begin
50
- actions.each_with_object({}) do |action, hsh|
51
- key = client.method(action).
52
- owner.to_s.gsub(/Gitlab::(?:Client::)?/, '')
53
- hsh[key] ||= []
54
- hsh[key] << action.to_s
55
- end
49
+ actions.each_with_object({}) do |action, hsh|
50
+ key = client.method(action).
51
+ owner.to_s.gsub(/Gitlab::(?:Client::)?/, '')
52
+ hsh[key] ||= []
53
+ hsh[key] << action.to_s
54
+ end
56
55
  end
57
56
  end
58
57
 
59
58
  # Table with available commands.
60
59
  #
61
60
  # @return [Terminal::Table]
62
- def actions_table(topic = nil)
61
+ def actions_table(topic=nil)
63
62
  rows = topic ? help_map[topic] : help_map.keys
64
63
  table do |t|
65
64
  t.title = topic || "Help Topics"
@@ -75,16 +74,21 @@ module Gitlab::Help
75
74
  # Returns full namespace of a command (e.g. Gitlab::Client::Branches.cmd)
76
75
  def namespace(cmd)
77
76
  method_owners.select { |method| method[:name] === cmd }.
78
- map { |method| method[:owner] + '.' + method[:name] }.
79
- shift
77
+ map { |method| method[:owner] + '.' + method[:name] }.
78
+ shift
80
79
  end
81
80
 
82
81
  # Massage output from 'ri'.
83
82
  def change_help_output!(cmd, output_str)
84
- output_str.gsub!(/#{cmd}\((.*?)\)/m, cmd+' \1')
83
+ output_str.gsub!(/#{cmd}\((.*?)\)/m, cmd + ' \1')
85
84
  output_str.gsub!(/\,[\s]*/, ' ')
85
+
86
+ # Ensure @option descriptions are on a single line
87
+ output_str.gsub!(/\n\[/, " \[")
88
+ output_str.gsub!(/\s(@)/, "\n@")
89
+ output_str.gsub!(/(\])\n(\:)/, '\1 \2')
90
+ output_str.gsub!(/(\:.*)(\n)(.*\.)/, '\1 \3')
91
+
86
92
  end
87
-
88
93
  end # class << self
89
94
  end
90
-
@@ -4,7 +4,7 @@ module Gitlab
4
4
  # Creates a new ObjectifiedHash object.
5
5
  def initialize(hash)
6
6
  @hash = hash
7
- @data = hash.inject({}) do |data, (key,value)|
7
+ @data = hash.inject({}) do |data, (key, value)|
8
8
  value = ObjectifiedHash.new(value) if value.is_a? Hash
9
9
  data[key.to_s] = value
10
10
  data
@@ -27,7 +27,7 @@ module Gitlab
27
27
  @data.key?(key.to_s) ? @data[key.to_s] : nil
28
28
  end
29
29
 
30
- def respond_to?(method_name, include_private = false)
30
+ def respond_to?(method_name, include_private=false)
31
31
  @hash.keys.map(&:to_sym).include?(method_name.to_sym) || super
32
32
  end
33
33
  end
@@ -0,0 +1,33 @@
1
+ module Gitlab
2
+ # Parses link header.
3
+ #
4
+ # @private
5
+ class PageLinks
6
+ HEADER_LINK = 'Link'.freeze
7
+ DELIM_LINKS = ','.freeze
8
+ LINK_REGEX = /<([^>]+)>; rel=\"([^\"]+)\"/
9
+ METAS = %w(last next first prev)
10
+
11
+ attr_accessor(*METAS)
12
+
13
+ def initialize(headers)
14
+ link_header = headers[HEADER_LINK]
15
+
16
+ if link_header && link_header =~ /(next|first|last|prev)/
17
+ extract_links(link_header)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def extract_links(header)
24
+ header.split(DELIM_LINKS).each do |link|
25
+ LINK_REGEX.match(link.strip) do |match|
26
+ url, meta = match[1], match[2]
27
+ next if !url || !meta || METAS.index(meta).nil?
28
+ self.send("#{meta}=", url)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,97 @@
1
+ module Gitlab
2
+ # Wrapper class of paginated response.
3
+ class PaginatedResponse
4
+ attr_accessor :client
5
+
6
+ def initialize(array)
7
+ @array = array
8
+ end
9
+
10
+ def ==(other)
11
+ @array == other
12
+ end
13
+
14
+ def inspect
15
+ @array.inspect
16
+ end
17
+
18
+ def method_missing(name, *args, &block)
19
+ if @array.respond_to?(name)
20
+ @array.send(name, *args, &block)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def respond_to?(method, include_all=false)
27
+ super || @array.respond_to?(method, include_all)
28
+ end
29
+
30
+ def parse_headers!(headers)
31
+ @links = PageLinks.new headers
32
+ end
33
+
34
+ def each_page
35
+ current = self
36
+ yield current
37
+ while current.has_next_page?
38
+ current = current.next_page
39
+ yield current
40
+ end
41
+ end
42
+
43
+ def auto_paginate
44
+ response = block_given? ? nil : []
45
+ each_page do |page|
46
+ if block_given?
47
+ page.each do |item|
48
+ yield item
49
+ end
50
+ else
51
+ response += page
52
+ end
53
+ end
54
+ response
55
+ end
56
+
57
+ def has_last_page?
58
+ !(@links.nil? || @links.last.nil?)
59
+ end
60
+
61
+ def last_page
62
+ return nil if @client.nil? || !has_last_page?
63
+ path = @links.last.sub(/#{@client.endpoint}/, '')
64
+ @client.get(path)
65
+ end
66
+
67
+ def has_first_page?
68
+ !(@links.nil? || @links.first.nil?)
69
+ end
70
+
71
+ def first_page
72
+ return nil if @client.nil? || !has_first_page?
73
+ path = @links.first.sub(/#{@client.endpoint}/, '')
74
+ @client.get(path)
75
+ end
76
+
77
+ def has_next_page?
78
+ !(@links.nil? || @links.next.nil?)
79
+ end
80
+
81
+ def next_page
82
+ return nil if @client.nil? || !has_next_page?
83
+ path = @links.next.sub(/#{@client.endpoint}/, '')
84
+ @client.get(path)
85
+ end
86
+
87
+ def has_prev_page?
88
+ !(@links.nil? || @links.prev.nil?)
89
+ end
90
+
91
+ def prev_page
92
+ return nil if @client.nil? || !has_prev_page?
93
+ path = @links.prev.sub(/#{@client.endpoint}/, '')
94
+ @client.get(path)
95
+ end
96
+ end
97
+ end
@@ -7,7 +7,7 @@ module Gitlab
7
7
  include HTTParty
8
8
  format :json
9
9
  headers 'Accept' => 'application/json'
10
- parser Proc.new { |body, _| parse(body) }
10
+ parser proc { |body, _| parse(body) }
11
11
 
12
12
  attr_accessor :private_token, :endpoint
13
13
 
@@ -18,7 +18,7 @@ module Gitlab
18
18
  if body.is_a? Hash
19
19
  ObjectifiedHash.new body
20
20
  elsif body.is_a? Array
21
- body.collect! { |e| ObjectifiedHash.new(e) }
21
+ PaginatedResponse.new(body.collect! { |e| ObjectifiedHash.new(e) })
22
22
  elsif body
23
23
  true
24
24
  elsif !body
@@ -32,11 +32,9 @@ module Gitlab
32
32
 
33
33
  # Decodes a JSON response into Ruby object.
34
34
  def self.decode(response)
35
- begin
36
- JSON.load response
37
- rescue JSON::ParserError
38
- raise Error::Parsing.new "The response is not a valid JSON"
39
- end
35
+ JSON.load response
36
+ rescue JSON::ParserError
37
+ raise Error::Parsing.new "The response is not a valid JSON"
40
38
  end
41
39
 
42
40
  def get(path, options={})
@@ -67,26 +65,29 @@ module Gitlab
67
65
  # Returns parsed response for successful requests.
68
66
  def validate(response)
69
67
  case response.code
70
- when 400; raise Error::BadRequest.new error_message(response)
71
- when 401; raise Error::Unauthorized.new error_message(response)
72
- when 403; raise Error::Forbidden.new error_message(response)
73
- when 404; raise Error::NotFound.new error_message(response)
74
- when 405; raise Error::MethodNotAllowed.new error_message(response)
75
- when 409; raise Error::Conflict.new error_message(response)
76
- when 422; raise Error::Unprocessable.new error_message(response)
77
- when 500; raise Error::InternalServerError.new error_message(response)
78
- when 502; raise Error::BadGateway.new error_message(response)
79
- when 503; raise Error::ServiceUnavailable.new error_message(response)
68
+ when 400 then fail Error::BadRequest.new error_message(response)
69
+ when 401 then fail Error::Unauthorized.new error_message(response)
70
+ when 403 then fail Error::Forbidden.new error_message(response)
71
+ when 404 then fail Error::NotFound.new error_message(response)
72
+ when 405 then fail Error::MethodNotAllowed.new error_message(response)
73
+ when 409 then fail Error::Conflict.new error_message(response)
74
+ when 422 then fail Error::Unprocessable.new error_message(response)
75
+ when 500 then fail Error::InternalServerError.new error_message(response)
76
+ when 502 then fail Error::BadGateway.new error_message(response)
77
+ when 503 then fail Error::ServiceUnavailable.new error_message(response)
80
78
  end
81
79
 
82
- response.parsed_response
80
+ parsed = response.parsed_response
81
+ parsed.client = self if parsed.respond_to?(:client=)
82
+ parsed.parse_headers!(response.headers) if parsed.respond_to?(:parse_headers!)
83
+ parsed
83
84
  end
84
85
 
85
86
  # Sets a base_uri and default_params for requests.
86
87
  # @raise [Error::MissingCredentials] if endpoint not set.
87
88
  def set_request_defaults(sudo=nil)
89
+ self.class.default_params sudo: sudo
88
90
  raise Error::MissingCredentials.new("Please set an endpoint to API") unless @endpoint
89
- self.class.default_params :sudo => sudo
90
91
  self.class.default_params.delete(:sudo) if sudo.nil?
91
92
  end
92
93
 
@@ -98,9 +99,9 @@ module Gitlab
98
99
  unless path == '/session'
99
100
  raise Error::MissingCredentials.new("Please provide a private_token or auth_token for user") unless @private_token
100
101
  if @private_token.length <= 20
101
- options[:headers] = {'PRIVATE-TOKEN' => @private_token}
102
+ options[:headers] = { 'PRIVATE-TOKEN' => @private_token }
102
103
  else
103
- options[:headers] = {'Authorization' => "Bearer #{@private_token}"}
104
+ options[:headers] = { 'Authorization' => "Bearer #{@private_token}" }
104
105
  end
105
106
  end
106
107
  end
@@ -108,9 +109,7 @@ module Gitlab
108
109
  # Set HTTParty configuration
109
110
  # @see https://github.com/jnunemaker/httparty
110
111
  def set_httparty_config(options)
111
- if self.httparty
112
- options.merge!(self.httparty)
113
- end
112
+ options.merge!(httparty) if httparty
114
113
  end
115
114
 
116
115
  def error_message(response)
@@ -127,7 +126,7 @@ module Gitlab
127
126
  case message
128
127
  when Gitlab::ObjectifiedHash
129
128
  message.to_h.sort.map do |key, val|
130
- "'#{key}' #{(val.is_a?(Hash) ? val.sort.map { |k,v| "(#{k}: #{v.join(' ')})"} : val).join(' ')}"
129
+ "'#{key}' #{(val.is_a?(Hash) ? val.sort.map { |k, v| "(#{k}: #{v.join(' ')})" } : val).join(' ')}"
131
130
  end.join(', ')
132
131
  when Array
133
132
  message.join(' ')
@@ -135,6 +134,5 @@ module Gitlab
135
134
  message
136
135
  end
137
136
  end
138
-
139
137
  end
140
138
  end
@@ -62,13 +62,13 @@ class Gitlab::Shell
62
62
  end
63
63
 
64
64
  # Execute a given command with arguements
65
- def execute(cmd = command, args = arguments)
65
+ def execute(cmd=command, args=arguments)
66
66
  if actions.include?(cmd.to_sym)
67
67
  confirm_command(cmd)
68
68
  gitlab_helper(cmd, args)
69
69
  else
70
- raise "Unknown command: #{cmd}. " +
71
- "See the 'help' for a list of valid commands."
70
+ fail "Unknown command: #{cmd}. " \
71
+ "See the 'help' for a list of valid commands."
72
72
  end
73
73
  end
74
74
 
@@ -80,6 +80,5 @@ class Gitlab::Shell
80
80
  def history
81
81
  @history ||= History.new
82
82
  end
83
-
84
- end # class << self
83
+ end # class << self
85
84
  end
@@ -3,7 +3,7 @@ class Gitlab::Shell
3
3
  DEFAULT_HISTFILESIZE = 200
4
4
  DEFAULT_FILE_PATH = File.join(Dir.home, '.gitlab_shell_history')
5
5
 
6
- def initialize(options = {})
6
+ def initialize(options={})
7
7
  @file_path = options[:file_path] || DEFAULT_FILE_PATH
8
8
  Readline::HISTORY.clear
9
9
  end
@@ -47,15 +47,13 @@ class Gitlab::Shell
47
47
  def read_from_file
48
48
  path = history_file_path
49
49
 
50
- if File.exist?(path)
51
- File.foreach(path) { |line| yield(line) }
52
- end
50
+ File.foreach(path) { |line| yield(line) } if File.exist?(path)
53
51
  rescue => error
54
52
  warn "History file not loaded: #{error.message}"
55
53
  end
56
54
 
57
55
  def max_lines
58
- (ENV['GITLAB_HISTFILESIZE']|| DEFAULT_HISTFILESIZE).to_i
56
+ (ENV['GITLAB_HISTFILESIZE'] || DEFAULT_HISTFILESIZE).to_i
59
57
  end
60
58
  end
61
59
  end
@@ -1,3 +1,3 @@
1
1
  module Gitlab
2
- VERSION = "3.5.0"
2
+ VERSION = "3.6.0"
3
3
  end
@@ -7,8 +7,8 @@ describe Gitlab::CLI::Helpers do
7
7
  end
8
8
  it "should return Array of Hashes containing method names and owners" do
9
9
  expect(@methods).to be_a Array
10
- expect(@methods.all? { |m| m.is_a? Hash} ).to be true
11
- expect(@methods.all? { |m| m.keys.sort === [:name, :owner]} ).to be true
10
+ expect(@methods.all? { |m| m.is_a? Hash }).to be true
11
+ expect(@methods.all? { |m| m.keys.sort === [:name, :owner] }).to be true
12
12
  end
13
13
  end
14
14
 
@@ -24,15 +24,15 @@ describe Gitlab::CLI::Helpers do
24
24
  describe ".symbolize_keys" do
25
25
  context "when input is a Hash" do
26
26
  it "should return a Hash with symbols for keys" do
27
- hash = {'key1' => 'val1', 'key2' => 'val2'}
27
+ hash = { 'key1' => 'val1', 'key2' => 'val2' }
28
28
  symbolized_hash = Gitlab::CLI::Helpers.symbolize_keys(hash)
29
- expect(symbolized_hash).to eq({key1: 'val1', key2: 'val2'})
29
+ expect(symbolized_hash).to eq(key1: 'val1', key2: 'val2')
30
30
  end
31
31
  end
32
32
  context "when input is NOT a Hash" do
33
33
  it "should return input untouched" do
34
34
  array = [1, 2, 3]
35
- new_array = Gitlab::CLI::Helpers.symbolize_keys(array)
35
+ new_array = Gitlab::CLI::Helpers.symbolize_keys(array)
36
36
  expect(new_array).to eq([1, 2, 3])
37
37
  end
38
38
  end
@@ -41,18 +41,17 @@ describe Gitlab::CLI::Helpers do
41
41
  describe ".yaml_load" do
42
42
  context "when argument is a YAML string" do
43
43
  it "should return Ruby objects" do
44
- argument = "{foo: bar, sna: fu}"
44
+ argument = "{foo: bar, sna: fu}"
45
45
  output = Gitlab::CLI::Helpers.yaml_load argument
46
- expect(output).to eq({'foo' => 'bar', 'sna' => 'fu'})
46
+ expect(output).to eq('foo' => 'bar', 'sna' => 'fu')
47
47
  end
48
48
  end
49
49
 
50
50
  context "when input is NOT valid YAML" do
51
51
  it "should raise" do
52
52
  ruby_array = [1, 2, 3, 4]
53
- expect { Gitlab::CLI::Helpers.yaml_load ruby_array}.to raise_error TypeError
53
+ expect { Gitlab::CLI::Helpers.yaml_load ruby_array }.to raise_error TypeError
54
54
  end
55
55
  end
56
56
  end
57
-
58
57
  end