gitlab 3.5.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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