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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/README.md +17 -0
- data/Rakefile +1 -1
- data/exe/gitlab +1 -1
- data/lib/gitlab.rb +5 -3
- data/lib/gitlab/cli.rb +1 -1
- data/lib/gitlab/cli_helpers.rb +24 -26
- data/lib/gitlab/client.rb +1 -1
- data/lib/gitlab/client/branches.rb +2 -5
- data/lib/gitlab/client/commits.rb +5 -5
- data/lib/gitlab/client/groups.rb +8 -9
- data/lib/gitlab/client/issues.rb +7 -7
- data/lib/gitlab/client/labels.rb +3 -3
- data/lib/gitlab/client/merge_requests.rb +7 -7
- data/lib/gitlab/client/milestones.rb +5 -5
- data/lib/gitlab/client/namespaces.rb +1 -1
- data/lib/gitlab/client/notes.rb +7 -7
- data/lib/gitlab/client/projects.rb +22 -21
- data/lib/gitlab/client/repositories.rb +7 -7
- data/lib/gitlab/client/repository_files.rb +10 -10
- data/lib/gitlab/client/snippets.rb +6 -6
- data/lib/gitlab/client/system_hooks.rb +1 -1
- data/lib/gitlab/client/users.rb +5 -6
- data/lib/gitlab/configuration.rb +1 -1
- data/lib/gitlab/help.rb +19 -15
- data/lib/gitlab/objectified_hash.rb +2 -2
- data/lib/gitlab/page_links.rb +33 -0
- data/lib/gitlab/paginated_response.rb +97 -0
- data/lib/gitlab/request.rb +24 -26
- data/lib/gitlab/shell.rb +4 -5
- data/lib/gitlab/shell_history.rb +3 -5
- data/lib/gitlab/version.rb +1 -1
- data/spec/gitlab/cli_helpers_spec.rb +8 -9
- data/spec/gitlab/cli_spec.rb +0 -2
- data/spec/gitlab/client/branches_spec.rb +2 -2
- data/spec/gitlab/client/commits_spec.rb +16 -16
- data/spec/gitlab/client/groups_spec.rb +11 -14
- data/spec/gitlab/client/issues_spec.rb +9 -9
- data/spec/gitlab/client/labels_spec.rb +6 -6
- data/spec/gitlab/client/merge_requests_spec.rb +23 -23
- data/spec/gitlab/client/milestones_spec.rb +9 -9
- data/spec/gitlab/client/namespaces_spec.rb +2 -2
- data/spec/gitlab/client/notes_spec.rb +10 -10
- data/spec/gitlab/client/projects_spec.rb +29 -28
- data/spec/gitlab/client/repositories_spec.rb +7 -7
- data/spec/gitlab/client/snippets_spec.rb +7 -7
- data/spec/gitlab/client/system_hooks_spec.rb +2 -2
- data/spec/gitlab/client/users_spec.rb +20 -21
- data/spec/gitlab/help_spec.rb +1 -4
- data/spec/gitlab/objectified_hash_spec.rb +2 -2
- data/spec/gitlab/page_links_spec.rb +16 -0
- data/spec/gitlab/paginated_response_spec.rb +60 -0
- data/spec/gitlab/request_spec.rb +12 -13
- data/spec/gitlab/shell_history_spec.rb +1 -1
- data/spec/gitlab/shell_spec.rb +6 -6
- data/spec/spec_helper.rb +12 -12
- metadata +8 -2
data/lib/gitlab/help.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
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
|
-
|
79
|
-
|
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
|
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
|
data/lib/gitlab/request.rb
CHANGED
@@ -7,7 +7,7 @@ module Gitlab
|
|
7
7
|
include HTTParty
|
8
8
|
format :json
|
9
9
|
headers 'Accept' => 'application/json'
|
10
|
-
parser
|
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
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
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
|
data/lib/gitlab/shell.rb
CHANGED
@@ -62,13 +62,13 @@ class Gitlab::Shell
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# Execute a given command with arguements
|
65
|
-
def execute(cmd
|
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
|
-
|
71
|
-
|
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
|
data/lib/gitlab/shell_history.rb
CHANGED
@@ -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
|
data/lib/gitlab/version.rb
CHANGED
@@ -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}
|
11
|
-
expect(@methods.all? { |m| m.keys.sort === [:name, :owner]}
|
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(
|
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(
|
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
|