gitlab-akerl 4.0.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 +7 -0
- data/.gitignore +20 -0
- data/.prospectus +11 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +229 -0
- data/CONTRIBUTING.md +195 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +24 -0
- data/README.md +192 -0
- data/Rakefile +9 -0
- data/bin/console +10 -0
- data/bin/setup +6 -0
- data/exe/gitlab +7 -0
- data/gitlab-akerl.gemspec +31 -0
- data/lib/gitlab.rb +45 -0
- data/lib/gitlab/api.rb +19 -0
- data/lib/gitlab/cli.rb +89 -0
- data/lib/gitlab/cli_helpers.rb +241 -0
- data/lib/gitlab/client.rb +48 -0
- data/lib/gitlab/client/branches.rb +91 -0
- data/lib/gitlab/client/build_triggers.rb +51 -0
- data/lib/gitlab/client/build_variables.rb +66 -0
- data/lib/gitlab/client/builds.rb +106 -0
- data/lib/gitlab/client/commits.rb +121 -0
- data/lib/gitlab/client/groups.rb +144 -0
- data/lib/gitlab/client/issues.rb +113 -0
- data/lib/gitlab/client/labels.rb +57 -0
- data/lib/gitlab/client/merge_requests.rb +168 -0
- data/lib/gitlab/client/milestones.rb +78 -0
- data/lib/gitlab/client/namespaces.rb +20 -0
- data/lib/gitlab/client/notes.rb +161 -0
- data/lib/gitlab/client/pipelines.rb +68 -0
- data/lib/gitlab/client/projects.rb +471 -0
- data/lib/gitlab/client/repositories.rb +78 -0
- data/lib/gitlab/client/repository_files.rb +88 -0
- data/lib/gitlab/client/runners.rb +115 -0
- data/lib/gitlab/client/services.rb +50 -0
- data/lib/gitlab/client/snippets.rb +91 -0
- data/lib/gitlab/client/system_hooks.rb +59 -0
- data/lib/gitlab/client/tags.rb +96 -0
- data/lib/gitlab/client/users.rb +250 -0
- data/lib/gitlab/configuration.rb +55 -0
- data/lib/gitlab/error.rb +85 -0
- data/lib/gitlab/file_response.rb +46 -0
- data/lib/gitlab/help.rb +95 -0
- data/lib/gitlab/objectified_hash.rb +34 -0
- data/lib/gitlab/page_links.rb +33 -0
- data/lib/gitlab/paginated_response.rb +97 -0
- data/lib/gitlab/request.rb +117 -0
- data/lib/gitlab/shell.rb +84 -0
- data/lib/gitlab/shell_history.rb +59 -0
- data/lib/gitlab/version.rb +3 -0
- data/spec/fixtures/branch.json +1 -0
- data/spec/fixtures/branch_delete.json +3 -0
- data/spec/fixtures/branches.json +1 -0
- data/spec/fixtures/build.json +38 -0
- data/spec/fixtures/build_artifacts.json +0 -0
- data/spec/fixtures/build_cancel.json +24 -0
- data/spec/fixtures/build_erase.json +24 -0
- data/spec/fixtures/build_retry.json +24 -0
- data/spec/fixtures/builds.json +78 -0
- data/spec/fixtures/builds_commits.json +64 -0
- data/spec/fixtures/compare_merge_request_diff.json +31 -0
- data/spec/fixtures/error_already_exists.json +1 -0
- data/spec/fixtures/error_project_not_found.json +1 -0
- data/spec/fixtures/get_repository_file.json +1 -0
- data/spec/fixtures/git_hook.json +1 -0
- data/spec/fixtures/group.json +60 -0
- data/spec/fixtures/group_create.json +1 -0
- data/spec/fixtures/group_create_with_description.json +1 -0
- data/spec/fixtures/group_delete.json +1 -0
- data/spec/fixtures/group_member.json +1 -0
- data/spec/fixtures/group_member_delete.json +1 -0
- data/spec/fixtures/group_member_edit.json +1 -0
- data/spec/fixtures/group_members.json +1 -0
- data/spec/fixtures/group_projects.json +44 -0
- data/spec/fixtures/group_search.json +2 -0
- data/spec/fixtures/groups.json +2 -0
- data/spec/fixtures/issue.json +1 -0
- data/spec/fixtures/issues.json +1 -0
- data/spec/fixtures/key.json +1 -0
- data/spec/fixtures/keys.json +1 -0
- data/spec/fixtures/label.json +1 -0
- data/spec/fixtures/labels.json +1 -0
- data/spec/fixtures/merge_request.json +1 -0
- data/spec/fixtures/merge_request_changes.json +1 -0
- data/spec/fixtures/merge_request_comment.json +1 -0
- data/spec/fixtures/merge_request_comments.json +1 -0
- data/spec/fixtures/merge_request_commits.json +1 -0
- data/spec/fixtures/merge_requests.json +1 -0
- data/spec/fixtures/milestone.json +1 -0
- data/spec/fixtures/milestone_issues.json +1 -0
- data/spec/fixtures/milestones.json +1 -0
- data/spec/fixtures/namespaces.json +1 -0
- data/spec/fixtures/note.json +1 -0
- data/spec/fixtures/notes.json +1 -0
- data/spec/fixtures/pipeline.json +23 -0
- data/spec/fixtures/pipeline_cancel.json +23 -0
- data/spec/fixtures/pipeline_create.json +23 -0
- data/spec/fixtures/pipeline_retry.json +23 -0
- data/spec/fixtures/pipelines.json +48 -0
- data/spec/fixtures/project.json +1 -0
- data/spec/fixtures/project_commit.json +13 -0
- data/spec/fixtures/project_commit_comment.json +1 -0
- data/spec/fixtures/project_commit_comments.json +1 -0
- data/spec/fixtures/project_commit_diff.json +10 -0
- data/spec/fixtures/project_commit_status.json +42 -0
- data/spec/fixtures/project_commits.json +1 -0
- data/spec/fixtures/project_edit.json +21 -0
- data/spec/fixtures/project_events.json +1 -0
- data/spec/fixtures/project_for_user.json +1 -0
- data/spec/fixtures/project_fork.json +50 -0
- data/spec/fixtures/project_fork_link.json +1 -0
- data/spec/fixtures/project_forked_for_user.json +50 -0
- data/spec/fixtures/project_hook.json +1 -0
- data/spec/fixtures/project_hooks.json +1 -0
- data/spec/fixtures/project_issues.json +1 -0
- data/spec/fixtures/project_key.json +6 -0
- data/spec/fixtures/project_keys.json +6 -0
- data/spec/fixtures/project_runner_enable.json +7 -0
- data/spec/fixtures/project_runners.json +16 -0
- data/spec/fixtures/project_search.json +1 -0
- data/spec/fixtures/project_star.json +44 -0
- data/spec/fixtures/project_tag_annotated.json +1 -0
- data/spec/fixtures/project_tag_lightweight.json +1 -0
- data/spec/fixtures/project_tags.json +1 -0
- data/spec/fixtures/project_unstar.json +44 -0
- data/spec/fixtures/project_update_commit_status.json +20 -0
- data/spec/fixtures/projects.json +1 -0
- data/spec/fixtures/raw_file.json +2 -0
- data/spec/fixtures/release_create.json +1 -0
- data/spec/fixtures/release_update.json +1 -0
- data/spec/fixtures/repository_file.json +1 -0
- data/spec/fixtures/runner.json +26 -0
- data/spec/fixtures/runner_delete.json +7 -0
- data/spec/fixtures/runner_edit.json +26 -0
- data/spec/fixtures/runners.json +16 -0
- data/spec/fixtures/runners_all.json +30 -0
- data/spec/fixtures/service.json +1 -0
- data/spec/fixtures/session.json +1 -0
- data/spec/fixtures/shell_history.json +2 -0
- data/spec/fixtures/snippet.json +1 -0
- data/spec/fixtures/snippet_content.json +3 -0
- data/spec/fixtures/snippets.json +1 -0
- data/spec/fixtures/system_hook.json +1 -0
- data/spec/fixtures/system_hooks.json +1 -0
- data/spec/fixtures/tag.json +1 -0
- data/spec/fixtures/tag_create.json +1 -0
- data/spec/fixtures/tag_create_with_description.json +1 -0
- data/spec/fixtures/tag_delete.json +1 -0
- data/spec/fixtures/tags.json +1 -0
- data/spec/fixtures/team_member.json +1 -0
- data/spec/fixtures/team_members.json +1 -0
- data/spec/fixtures/tree.json +1 -0
- data/spec/fixtures/trigger.json +7 -0
- data/spec/fixtures/triggers.json +16 -0
- data/spec/fixtures/user.json +1 -0
- data/spec/fixtures/user_block_unblock.json +1 -0
- data/spec/fixtures/user_email.json +1 -0
- data/spec/fixtures/user_emails.json +1 -0
- data/spec/fixtures/user_search.json +1 -0
- data/spec/fixtures/users.json +1 -0
- data/spec/fixtures/variable.json +4 -0
- data/spec/fixtures/variables.json +10 -0
- data/spec/gitlab/cli_helpers_spec.rb +57 -0
- data/spec/gitlab/cli_spec.rb +110 -0
- data/spec/gitlab/client/branches_spec.rb +99 -0
- data/spec/gitlab/client/build_triggers_spec.rb +67 -0
- data/spec/gitlab/client/build_variables_spec.rb +86 -0
- data/spec/gitlab/client/builds_spec.rb +148 -0
- data/spec/gitlab/client/client_spec.rb +11 -0
- data/spec/gitlab/client/commits_spec.rb +137 -0
- data/spec/gitlab/client/groups_spec.rb +197 -0
- data/spec/gitlab/client/issues_spec.rb +138 -0
- data/spec/gitlab/client/labels_spec.rb +68 -0
- data/spec/gitlab/client/merge_requests_spec.rb +177 -0
- data/spec/gitlab/client/milestones_spec.rb +82 -0
- data/spec/gitlab/client/namespaces_spec.rb +22 -0
- data/spec/gitlab/client/notes_spec.rb +205 -0
- data/spec/gitlab/client/pipelines_spec.rb +95 -0
- data/spec/gitlab/client/projects_spec.rb +603 -0
- data/spec/gitlab/client/repositories_spec.rb +109 -0
- data/spec/gitlab/client/repository_files_spec.rb +62 -0
- data/spec/gitlab/client/runners_spec.rb +185 -0
- data/spec/gitlab/client/services_spec.rb +55 -0
- data/spec/gitlab/client/snippets_spec.rb +100 -0
- data/spec/gitlab/client/system_hooks_spec.rb +69 -0
- data/spec/gitlab/client/tags_spec.rb +109 -0
- data/spec/gitlab/client/users_spec.rb +418 -0
- data/spec/gitlab/error_spec.rb +45 -0
- data/spec/gitlab/file_response_spec.rb +33 -0
- data/spec/gitlab/help_spec.rb +46 -0
- data/spec/gitlab/objectified_hash_spec.rb +48 -0
- data/spec/gitlab/page_links_spec.rb +16 -0
- data/spec/gitlab/paginated_response_spec.rb +60 -0
- data/spec/gitlab/request_spec.rb +73 -0
- data/spec/gitlab/shell_history_spec.rb +53 -0
- data/spec/gitlab/shell_spec.rb +80 -0
- data/spec/gitlab_spec.rb +97 -0
- data/spec/spec_helper.rb +74 -0
- metadata +476 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Gitlab
|
|
2
|
+
# Wrapper class of file response.
|
|
3
|
+
class FileResponse
|
|
4
|
+
HEADER_CONTENT_DISPOSITION = 'Content-Disposition'.freeze
|
|
5
|
+
|
|
6
|
+
attr_reader :filename
|
|
7
|
+
|
|
8
|
+
def initialize(file)
|
|
9
|
+
@file = file
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [bool] Always false
|
|
13
|
+
def empty?
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [Hash] A hash consisting of filename and io object
|
|
18
|
+
def to_hash
|
|
19
|
+
{ filename: @filename, data: @file }
|
|
20
|
+
end
|
|
21
|
+
alias_method :to_h, :to_hash
|
|
22
|
+
|
|
23
|
+
# @return [String] Formatted string with the class name, object id and filename.
|
|
24
|
+
def inspect
|
|
25
|
+
"#<#{self.class}:#{object_id} {filename: #{filename.inspect}}>"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(name, *args, &block)
|
|
29
|
+
if @file.respond_to?(name)
|
|
30
|
+
@file.send(name, *args, &block)
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
37
|
+
super || @file.respond_to?(method_name, include_private)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Parse filename from the 'Content Disposition' header.
|
|
41
|
+
def parse_headers!(headers)
|
|
42
|
+
@filename = headers[HEADER_CONTENT_DISPOSITION].split("filename=")[1]
|
|
43
|
+
@filename = @filename[1...-1] if @filename[0] == '"' # Unquote filenames
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/gitlab/help.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'gitlab'
|
|
2
|
+
require 'gitlab/cli_helpers'
|
|
3
|
+
|
|
4
|
+
module Gitlab::Help
|
|
5
|
+
extend Gitlab::CLI::Helpers
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
# Returns the (modified) help from the 'ri' command or returns an error.
|
|
9
|
+
#
|
|
10
|
+
# @return [String]
|
|
11
|
+
def get_help(cmd)
|
|
12
|
+
cmd_namespace = namespace cmd
|
|
13
|
+
|
|
14
|
+
if cmd_namespace
|
|
15
|
+
ri_output = `#{ri_cmd} -T #{cmd_namespace} 2>&1`.chomp
|
|
16
|
+
|
|
17
|
+
if $CHILD_STATUS == 0
|
|
18
|
+
change_help_output! cmd, ri_output
|
|
19
|
+
yield ri_output if block_given?
|
|
20
|
+
|
|
21
|
+
ri_output
|
|
22
|
+
else
|
|
23
|
+
"Ri docs not found for #{cmd}, please install the docs to use 'help'."
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
"Unknown command: #{cmd}."
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Finds the location of 'ri' on a system.
|
|
31
|
+
#
|
|
32
|
+
# @return [String]
|
|
33
|
+
def ri_cmd
|
|
34
|
+
which_ri = `which ri`.chomp
|
|
35
|
+
if which_ri.empty?
|
|
36
|
+
fail "'ri' tool not found in $PATH. Please install it to use the help."
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
which_ri
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# A hash map that contains help topics (Branches, Groups, etc.)
|
|
43
|
+
# and a list of commands that are defined under a topic (create_branch,
|
|
44
|
+
# branches, protect_branch, etc.).
|
|
45
|
+
#
|
|
46
|
+
# @return [Hash<Array>]
|
|
47
|
+
def help_map
|
|
48
|
+
@help_map ||= begin
|
|
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
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Table with available commands.
|
|
59
|
+
#
|
|
60
|
+
# @return [Terminal::Table]
|
|
61
|
+
def actions_table(topic=nil)
|
|
62
|
+
rows = topic ? help_map[topic] : help_map.keys
|
|
63
|
+
table do |t|
|
|
64
|
+
t.title = topic || "Help Topics"
|
|
65
|
+
|
|
66
|
+
# add_row expects an array and we have strings hence the map.
|
|
67
|
+
rows.sort.map { |r| [r] }.each_with_index do |row, index|
|
|
68
|
+
t.add_row row
|
|
69
|
+
t.add_separator unless rows.size - 1 == index
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns full namespace of a command (e.g. Gitlab::Client::Branches.cmd)
|
|
75
|
+
def namespace(cmd)
|
|
76
|
+
method_owners.select { |method| method[:name] === cmd }.
|
|
77
|
+
map { |method| method[:owner] + '.' + method[:name] }.
|
|
78
|
+
shift
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Massage output from 'ri'.
|
|
82
|
+
def change_help_output!(cmd, output_str)
|
|
83
|
+
output_str.gsub!(/#{cmd}\((.*?)\)/m, cmd + ' \1')
|
|
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
|
+
output_str.gsub!(/\{(.+)\}/, '"{\1}"')
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end # class << self
|
|
95
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Gitlab
|
|
2
|
+
# Converts hashes to the objects.
|
|
3
|
+
class ObjectifiedHash
|
|
4
|
+
# Creates a new ObjectifiedHash object.
|
|
5
|
+
def initialize(hash)
|
|
6
|
+
@hash = hash
|
|
7
|
+
@data = hash.inject({}) do |data, (key, value)|
|
|
8
|
+
value = ObjectifiedHash.new(value) if value.is_a? Hash
|
|
9
|
+
data[key.to_s] = value
|
|
10
|
+
data
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [Hash] The original hash.
|
|
15
|
+
def to_hash
|
|
16
|
+
@hash
|
|
17
|
+
end
|
|
18
|
+
alias_method :to_h, :to_hash
|
|
19
|
+
|
|
20
|
+
# @return [String] Formatted string with the class name, object id and original hash.
|
|
21
|
+
def inspect
|
|
22
|
+
"#<#{self.class}:#{object_id} {hash: #{@hash.inspect}}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Delegate to ObjectifiedHash.
|
|
26
|
+
def method_missing(key)
|
|
27
|
+
@data.key?(key.to_s) ? @data[key.to_s] : nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
31
|
+
@hash.keys.map(&:to_sym).include?(method_name.to_sym) || super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
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_missing?(method_name, include_private = false)
|
|
27
|
+
super || @array.respond_to?(method_name, include_private)
|
|
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
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'httparty'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Gitlab
|
|
5
|
+
# @private
|
|
6
|
+
class Request
|
|
7
|
+
include HTTParty
|
|
8
|
+
format :json
|
|
9
|
+
headers 'Accept' => 'application/json'
|
|
10
|
+
parser proc { |body, _| parse(body) }
|
|
11
|
+
|
|
12
|
+
attr_accessor :private_token, :endpoint
|
|
13
|
+
|
|
14
|
+
# Converts the response body to an ObjectifiedHash.
|
|
15
|
+
def self.parse(body)
|
|
16
|
+
body = decode(body)
|
|
17
|
+
|
|
18
|
+
if body.is_a? Hash
|
|
19
|
+
ObjectifiedHash.new body
|
|
20
|
+
elsif body.is_a? Array
|
|
21
|
+
PaginatedResponse.new(body.collect! { |e| ObjectifiedHash.new(e) })
|
|
22
|
+
elsif body
|
|
23
|
+
true
|
|
24
|
+
elsif !body
|
|
25
|
+
false
|
|
26
|
+
elsif body.nil?
|
|
27
|
+
false
|
|
28
|
+
else
|
|
29
|
+
raise Error::Parsing.new "Couldn't parse a response body"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Decodes a JSON response into Ruby object.
|
|
34
|
+
def self.decode(response)
|
|
35
|
+
JSON.load response
|
|
36
|
+
rescue JSON::ParserError
|
|
37
|
+
raise Error::Parsing.new "The response is not a valid JSON"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get(path, options={})
|
|
41
|
+
set_httparty_config(options)
|
|
42
|
+
set_authorization_header(options)
|
|
43
|
+
validate self.class.get(@endpoint + path, options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def post(path, options={})
|
|
47
|
+
set_httparty_config(options)
|
|
48
|
+
set_authorization_header(options, path)
|
|
49
|
+
validate self.class.post(@endpoint + path, options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def put(path, options={})
|
|
53
|
+
set_httparty_config(options)
|
|
54
|
+
set_authorization_header(options)
|
|
55
|
+
validate self.class.put(@endpoint + path, options)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def delete(path, options={})
|
|
59
|
+
set_httparty_config(options)
|
|
60
|
+
set_authorization_header(options)
|
|
61
|
+
validate self.class.delete(@endpoint + path, options)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Checks the response code for common errors.
|
|
65
|
+
# Returns parsed response for successful requests.
|
|
66
|
+
def validate(response)
|
|
67
|
+
error_klass = case response.code
|
|
68
|
+
when 400 then Error::BadRequest
|
|
69
|
+
when 401 then Error::Unauthorized
|
|
70
|
+
when 403 then Error::Forbidden
|
|
71
|
+
when 404 then Error::NotFound
|
|
72
|
+
when 405 then Error::MethodNotAllowed
|
|
73
|
+
when 409 then Error::Conflict
|
|
74
|
+
when 422 then Error::Unprocessable
|
|
75
|
+
when 500 then Error::InternalServerError
|
|
76
|
+
when 502 then Error::BadGateway
|
|
77
|
+
when 503 then Error::ServiceUnavailable
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
fail error_klass.new(response) if error_klass
|
|
81
|
+
|
|
82
|
+
parsed = response.parsed_response
|
|
83
|
+
parsed.client = self if parsed.respond_to?(:client=)
|
|
84
|
+
parsed.parse_headers!(response.headers) if parsed.respond_to?(:parse_headers!)
|
|
85
|
+
parsed
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Sets a base_uri and default_params for requests.
|
|
89
|
+
# @raise [Error::MissingCredentials] if endpoint not set.
|
|
90
|
+
def set_request_defaults(sudo=nil)
|
|
91
|
+
self.class.default_params sudo: sudo
|
|
92
|
+
raise Error::MissingCredentials.new("Please set an endpoint to API") unless @endpoint
|
|
93
|
+
self.class.default_params.delete(:sudo) if sudo.nil?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
# Sets a PRIVATE-TOKEN or Authorization header for requests.
|
|
99
|
+
# @raise [Error::MissingCredentials] if private_token and auth_token are not set.
|
|
100
|
+
def set_authorization_header(options, path=nil)
|
|
101
|
+
unless path == '/session'
|
|
102
|
+
raise Error::MissingCredentials.new("Please provide a private_token or auth_token for user") unless @private_token
|
|
103
|
+
if @private_token.length <= 20
|
|
104
|
+
options[:headers] = { 'PRIVATE-TOKEN' => @private_token }
|
|
105
|
+
else
|
|
106
|
+
options[:headers] = { 'Authorization' => "Bearer #{@private_token}" }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Set HTTParty configuration
|
|
112
|
+
# @see https://github.com/jnunemaker/httparty
|
|
113
|
+
def set_httparty_config(options)
|
|
114
|
+
options.merge!(httparty) if httparty
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
data/lib/gitlab/shell.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'gitlab'
|
|
2
|
+
require 'gitlab/help'
|
|
3
|
+
require 'gitlab/cli_helpers'
|
|
4
|
+
require 'gitlab/shell_history'
|
|
5
|
+
require 'readline'
|
|
6
|
+
require 'shellwords'
|
|
7
|
+
|
|
8
|
+
class Gitlab::Shell
|
|
9
|
+
extend Gitlab::CLI::Helpers
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
attr_reader :arguments, :command
|
|
13
|
+
|
|
14
|
+
def start
|
|
15
|
+
trap('INT') { quit_shell } # capture ctrl-c
|
|
16
|
+
setup
|
|
17
|
+
|
|
18
|
+
while buffer = Readline.readline('gitlab> ')
|
|
19
|
+
begin
|
|
20
|
+
parse_input buffer
|
|
21
|
+
|
|
22
|
+
@arguments.map! { |arg| symbolize_keys(yaml_load(arg)) }
|
|
23
|
+
|
|
24
|
+
case buffer
|
|
25
|
+
when nil, ''
|
|
26
|
+
next
|
|
27
|
+
when 'exit'
|
|
28
|
+
quit_shell
|
|
29
|
+
when /^\bhelp\b+/
|
|
30
|
+
puts help(arguments[0]) { |out| out.gsub!(/Gitlab\./, 'gitlab> ') }
|
|
31
|
+
else
|
|
32
|
+
history << buffer
|
|
33
|
+
|
|
34
|
+
data = execute command, arguments
|
|
35
|
+
output_table command, arguments, data
|
|
36
|
+
end
|
|
37
|
+
rescue => e
|
|
38
|
+
puts e.message
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
quit_shell # save history if user presses ctrl-d
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse_input(buffer)
|
|
46
|
+
buf = Shellwords.shellwords(buffer)
|
|
47
|
+
|
|
48
|
+
@command = buf.shift
|
|
49
|
+
@arguments = buf.count > 0 ? buf : []
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def setup
|
|
53
|
+
history.load
|
|
54
|
+
|
|
55
|
+
Readline.completion_proc = completion
|
|
56
|
+
Readline.completion_append_character = ' '
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Gets called when user hits TAB key to do completion
|
|
60
|
+
def completion
|
|
61
|
+
proc { |str| actions.map(&:to_s).grep(/^#{Regexp.escape(str)}/) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Execute a given command with arguements
|
|
65
|
+
def execute(cmd=command, args=arguments)
|
|
66
|
+
if actions.include?(cmd.to_sym)
|
|
67
|
+
confirm_command(cmd)
|
|
68
|
+
gitlab_helper(cmd, args)
|
|
69
|
+
else
|
|
70
|
+
fail "Unknown command: #{cmd}. " \
|
|
71
|
+
"See the 'help' for a list of valid commands."
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def quit_shell
|
|
76
|
+
history.save
|
|
77
|
+
exit
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def history
|
|
81
|
+
@history ||= History.new
|
|
82
|
+
end
|
|
83
|
+
end # class << self
|
|
84
|
+
end
|