geet 0.1.8 → 0.1.9
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/.rspec +2 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +31 -0
- data/README.md +0 -2
- data/bin/geet +50 -39
- data/geet.gemspec +1 -1
- data/lib/geet/commandline/commands.rb +14 -0
- data/lib/geet/{helpers/configuration_helper.rb → commandline/configuration.rb} +4 -12
- data/lib/geet/git/repository.rb +93 -44
- data/lib/geet/{git_hub → github}/abstract_issue.rb +13 -13
- data/lib/geet/github/account.rb +19 -0
- data/lib/geet/{git_hub/api_helper.rb → github/api_interface.rb} +21 -19
- data/lib/geet/github/collaborator.rb +17 -0
- data/lib/geet/{git_hub → github}/gist.rb +4 -4
- data/lib/geet/{git_hub → github}/issue.rb +10 -10
- data/lib/geet/github/label.rb +17 -0
- data/lib/geet/{git_hub → github}/milestone.rb +11 -11
- data/lib/geet/github/pr.rb +61 -0
- data/lib/geet/services/create_gist.rb +4 -4
- data/lib/geet/services/create_issue.rb +30 -25
- data/lib/geet/services/create_pr.rb +28 -27
- data/lib/geet/services/list_issues.rb +2 -2
- data/lib/geet/services/list_labels.rb +2 -2
- data/lib/geet/services/list_milestones.rb +10 -10
- data/lib/geet/services/list_prs.rb +2 -2
- data/lib/geet/services/merge_pr.rb +8 -7
- data/lib/geet/version.rb +1 -1
- data/spec/integration/create_gist_spec.rb +46 -0
- data/spec/integration/create_issue_spec.rb +66 -0
- data/spec/integration/create_pr_spec.rb +68 -0
- data/spec/integration/list_issues_spec.rb +52 -0
- data/spec/integration/list_labels_spec.rb +28 -0
- data/spec/integration/list_milestones_spec.rb +43 -0
- data/spec/integration/list_prs_spec.rb +52 -0
- data/spec/integration/merge_pr_spec.rb +30 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/vcr_cassettes/create_gist_private.yml +80 -0
- data/spec/vcr_cassettes/create_gist_public.yml +80 -0
- data/spec/vcr_cassettes/create_issue.yml +534 -0
- data/spec/vcr_cassettes/create_issue_upstream.yml +235 -0
- data/spec/vcr_cassettes/create_pr.yml +693 -0
- data/spec/vcr_cassettes/create_pr_upstream.yml +313 -0
- data/spec/vcr_cassettes/list_issues.yml +82 -0
- data/spec/vcr_cassettes/list_issues_upstream.yml +84 -0
- data/spec/vcr_cassettes/list_labels.yml +78 -0
- data/spec/vcr_cassettes/list_milestones.yml +468 -0
- data/spec/vcr_cassettes/list_prs.yml +84 -0
- data/spec/vcr_cassettes/list_prs_upstream.yml +80 -0
- data/spec/vcr_cassettes/merge_pr.yml +156 -0
- metadata +36 -11
- data/lib/geet/git_hub/account.rb +0 -19
- data/lib/geet/git_hub/pr.rb +0 -57
- data/lib/geet/git_hub/remote_repository.rb +0 -65
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Geet
|
4
|
+
module Github
|
5
|
+
class Account
|
6
|
+
def initialize(api_interface)
|
7
|
+
@api_interface = api_interface
|
8
|
+
end
|
9
|
+
|
10
|
+
def authenticated_user
|
11
|
+
api_path = '/user'
|
12
|
+
|
13
|
+
response = @api_interface.send_request(api_path)
|
14
|
+
|
15
|
+
response.fetch('login')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -6,27 +6,17 @@ require 'json'
|
|
6
6
|
require 'shellwords'
|
7
7
|
|
8
8
|
module Geet
|
9
|
-
module
|
10
|
-
class
|
11
|
-
|
9
|
+
module Github
|
10
|
+
class ApiInterface
|
11
|
+
API_AUTH_USER = '' # We don't need the login, as the API key uniquely identifies the user
|
12
|
+
API_BASE_URL = 'https://api.github.com'
|
13
|
+
|
14
|
+
def initialize(api_token, repository_path, upstream)
|
12
15
|
@api_token = api_token
|
13
|
-
@user = user
|
14
16
|
@repository_path = repository_path
|
15
17
|
@upstream = upstream
|
16
18
|
end
|
17
19
|
|
18
|
-
def api_base_link
|
19
|
-
'https://api.github.com'
|
20
|
-
end
|
21
|
-
|
22
|
-
def api_repo_link
|
23
|
-
"#{api_base_link}/repos/#{@repository_path}"
|
24
|
-
end
|
25
|
-
|
26
|
-
def repo_link
|
27
|
-
"https://github.com/#{@repository_path}"
|
28
|
-
end
|
29
|
-
|
30
20
|
def upstream?
|
31
21
|
@upstream
|
32
22
|
end
|
@@ -36,14 +26,20 @@ module Geet
|
|
36
26
|
# Returns the parsed response, or an Array, in case of multipage.
|
37
27
|
#
|
38
28
|
# params:
|
29
|
+
# :api_path: api path, will be appended to the API URL.
|
30
|
+
# for root path, prepend a `/`:
|
31
|
+
# - use `/gists` for `https://api.github.com/gists`
|
32
|
+
# when owner/project/repos is included, don't prepend `/`:
|
33
|
+
# - use `issues` for `https://api.github.com/myowner/myproject/repos/issues`
|
39
34
|
# :params: (Hash)
|
40
35
|
# :data: (Hash) if present, will generate a POST request, otherwise, a GET
|
41
|
-
# :multipage: set true for paged
|
36
|
+
# :multipage: set true for paged Github responses (eg. issues); it will make the method
|
42
37
|
# return an array, with the concatenated (parsed) responses
|
43
38
|
# :http_method: :get, :patch, :post and :put are accepted, but only :patch/:put are meaningful,
|
44
39
|
# since the others are automatically inferred by :data.
|
45
40
|
#
|
46
|
-
def send_request(
|
41
|
+
def send_request(api_path, params: nil, data: nil, multipage: false, http_method: nil)
|
42
|
+
address = api_url(api_path)
|
47
43
|
# filled only on :multipage
|
48
44
|
parsed_responses = []
|
49
45
|
|
@@ -69,6 +65,12 @@ module Geet
|
|
69
65
|
|
70
66
|
private
|
71
67
|
|
68
|
+
def api_url(api_path)
|
69
|
+
url = API_BASE_URL
|
70
|
+
url += "/repos/#{@repository_path}/" if !api_path.start_with?('/')
|
71
|
+
url + api_path
|
72
|
+
end
|
73
|
+
|
72
74
|
def send_http_request(address, params: nil, data: nil, http_method: nil)
|
73
75
|
uri = encode_uri(address, params)
|
74
76
|
http_class = find_http_class(http_method, data)
|
@@ -76,8 +78,8 @@ module Geet
|
|
76
78
|
Net::HTTP.start(uri.host, use_ssl: true) do |http|
|
77
79
|
request = http_class.new(uri)
|
78
80
|
|
81
|
+
request.basic_auth API_AUTH_USER, @api_token
|
79
82
|
request.body = data.to_json if data
|
80
|
-
request.basic_auth @user, @api_token
|
81
83
|
request['Accept'] = 'application/vnd.github.v3+json'
|
82
84
|
|
83
85
|
http.request(request)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Geet
|
6
|
+
module Github
|
7
|
+
class Collaborator
|
8
|
+
# Returns a flat list of names in string form.
|
9
|
+
def self.list(api_interface)
|
10
|
+
api_path = 'collaborators'
|
11
|
+
response = api_interface.send_request(api_path, multipage: true)
|
12
|
+
|
13
|
+
response.map { |user_entry| user_entry.fetch('login') }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -3,14 +3,14 @@
|
|
3
3
|
require_relative 'abstract_issue'
|
4
4
|
|
5
5
|
module Geet
|
6
|
-
module
|
6
|
+
module Github
|
7
7
|
class Gist
|
8
|
-
def self.create(filename, content,
|
9
|
-
|
8
|
+
def self.create(filename, content, api_interface, description: nil, publik: false)
|
9
|
+
api_path = "/gists"
|
10
10
|
|
11
11
|
request_data = prepare_request_data(filename, content, description, publik)
|
12
12
|
|
13
|
-
response =
|
13
|
+
response = api_interface.send_request(api_path, data: request_data)
|
14
14
|
|
15
15
|
id = response.fetch('id')
|
16
16
|
|
@@ -1,28 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Geet
|
4
|
-
module
|
4
|
+
module Github
|
5
5
|
# See AbstractIssue for the circular dependency issue notes.
|
6
6
|
autoload :AbstractIssue, File.expand_path('abstract_issue', __dir__)
|
7
7
|
|
8
|
-
class Issue < Geet::
|
9
|
-
def self.create(title, description,
|
10
|
-
|
8
|
+
class Issue < Geet::Github::AbstractIssue
|
9
|
+
def self.create(title, description, api_interface)
|
10
|
+
api_path = 'issues'
|
11
11
|
request_data = { title: title, body: description, base: 'master' }
|
12
12
|
|
13
|
-
response =
|
13
|
+
response = api_interface.send_request(api_path, data: request_data)
|
14
14
|
|
15
15
|
issue_number, title, link = response.fetch_values('number', 'title', 'html_url')
|
16
16
|
|
17
|
-
new(issue_number,
|
17
|
+
new(issue_number, api_interface, title, link)
|
18
18
|
end
|
19
19
|
|
20
20
|
# See https://developer.github.com/v3/issues/#list-issues-for-a-repository
|
21
21
|
#
|
22
|
-
def self.list(
|
23
|
-
|
22
|
+
def self.list(api_interface)
|
23
|
+
api_path = 'issues'
|
24
24
|
|
25
|
-
response =
|
25
|
+
response = api_interface.send_request(api_path, multipage: true)
|
26
26
|
|
27
27
|
response.each_with_object([]) do |issue_data, result|
|
28
28
|
if !issue_data.key?('pull_request')
|
@@ -30,7 +30,7 @@ module Geet
|
|
30
30
|
title = issue_data.fetch('title')
|
31
31
|
link = issue_data.fetch('html_url')
|
32
32
|
|
33
|
-
result << new(number,
|
33
|
+
result << new(number, api_interface, title, link)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Geet
|
6
|
+
module Github
|
7
|
+
class Label
|
8
|
+
# Returns a flat list of names in string form.
|
9
|
+
def self.list(api_interface)
|
10
|
+
api_path = 'labels'
|
11
|
+
response = api_interface.send_request(api_path, multipage: true)
|
12
|
+
|
13
|
+
response.map { |label_entry| label_entry['name'] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -3,45 +3,45 @@
|
|
3
3
|
require 'date'
|
4
4
|
|
5
5
|
module Geet
|
6
|
-
module
|
6
|
+
module Github
|
7
7
|
class Milestone
|
8
8
|
attr_reader :number, :title, :due_on
|
9
9
|
|
10
|
-
def initialize(number, title, due_on,
|
10
|
+
def initialize(number, title, due_on, api_interface)
|
11
11
|
@number = number
|
12
12
|
@title = title
|
13
13
|
@due_on = due_on
|
14
14
|
|
15
|
-
@
|
15
|
+
@api_interface = api_interface
|
16
16
|
end
|
17
17
|
|
18
18
|
# See https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
|
19
19
|
#
|
20
|
-
def self.find(number,
|
21
|
-
|
20
|
+
def self.find(number, api_interface)
|
21
|
+
api_path = "milestones/#{number}"
|
22
22
|
|
23
|
-
response =
|
23
|
+
response = api_interface.send_request(api_path)
|
24
24
|
|
25
25
|
number = response.fetch('number')
|
26
26
|
title = response.fetch('title')
|
27
27
|
due_on = parse_due_on(response.fetch('due_on'))
|
28
28
|
|
29
|
-
new(number, title, due_on,
|
29
|
+
new(number, title, due_on, api_interface)
|
30
30
|
end
|
31
31
|
|
32
32
|
# See https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
|
33
33
|
#
|
34
|
-
def self.list(
|
35
|
-
|
34
|
+
def self.list(api_interface)
|
35
|
+
api_path = 'milestones'
|
36
36
|
|
37
|
-
response =
|
37
|
+
response = api_interface.send_request(api_path, multipage: true)
|
38
38
|
|
39
39
|
response.map do |milestone_data|
|
40
40
|
number = milestone_data.fetch('number')
|
41
41
|
title = milestone_data.fetch('title')
|
42
42
|
due_on = parse_due_on(milestone_data.fetch('due_on'))
|
43
43
|
|
44
|
-
new(number, title, due_on,
|
44
|
+
new(number, title, due_on, api_interface)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Geet
|
4
|
+
module Github
|
5
|
+
# See AbstractIssue for the circular dependency issue notes.
|
6
|
+
autoload :AbstractIssue, File.expand_path('abstract_issue', __dir__)
|
7
|
+
|
8
|
+
class PR < AbstractIssue
|
9
|
+
# See https://developer.github.com/v3/pulls/#create-a-pull-request
|
10
|
+
#
|
11
|
+
def self.create(title, description, head, api_interface)
|
12
|
+
api_path = 'pulls'
|
13
|
+
|
14
|
+
if api_interface.upstream?
|
15
|
+
account = Geet::Github::Account.new(api_interface)
|
16
|
+
head = "#{account.authenticated_user}:#{head}"
|
17
|
+
end
|
18
|
+
|
19
|
+
request_data = { title: title, body: description, head: head, base: 'master' }
|
20
|
+
|
21
|
+
response = api_interface.send_request(api_path, data: request_data)
|
22
|
+
|
23
|
+
number, title, link = response.fetch_values('number', 'title', 'html_url')
|
24
|
+
|
25
|
+
new(number, api_interface, title, link)
|
26
|
+
end
|
27
|
+
|
28
|
+
# See https://developer.github.com/v3/pulls/#list-pull-requests
|
29
|
+
#
|
30
|
+
def self.list(api_interface, head: nil)
|
31
|
+
api_path = 'pulls'
|
32
|
+
request_params = { head: head } if head
|
33
|
+
|
34
|
+
response = api_interface.send_request(api_path, params: request_params, multipage: true)
|
35
|
+
|
36
|
+
response.map do |issue_data|
|
37
|
+
number = issue_data.fetch('number')
|
38
|
+
title = issue_data.fetch('title')
|
39
|
+
link = issue_data.fetch('html_url')
|
40
|
+
|
41
|
+
new(number, api_interface, title, link)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# See https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button
|
46
|
+
#
|
47
|
+
def merge
|
48
|
+
api_path = "pulls/#{number}/merge"
|
49
|
+
|
50
|
+
@api_interface.send_request(api_path, http_method: :put)
|
51
|
+
end
|
52
|
+
|
53
|
+
def request_review(reviewers)
|
54
|
+
api_path = "pulls/#{number}/requested_reviewers"
|
55
|
+
request_data = { reviewers: reviewers }
|
56
|
+
|
57
|
+
@api_interface.send_request(api_path, data: request_data)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../helpers/os_helper.rb'
|
4
|
-
require_relative '../git/repository.rb'
|
5
4
|
|
6
5
|
module Geet
|
7
6
|
module Services
|
@@ -13,16 +12,17 @@ module Geet
|
|
13
12
|
# :publik: defaults to false
|
14
13
|
# :no_browse defaults to false
|
15
14
|
#
|
16
|
-
def execute(repository, full_filename, description: nil, publik: false, no_browse: false)
|
15
|
+
def execute(repository, full_filename, description: nil, publik: false, no_browse: false, output: $stdout)
|
17
16
|
content = IO.read(full_filename)
|
18
17
|
|
19
|
-
|
18
|
+
gist_access = publik ? 'public' : 'private'
|
19
|
+
output.puts "Creating a #{gist_access} gist..."
|
20
20
|
|
21
21
|
filename = File.basename(full_filename)
|
22
22
|
gist = repository.create_gist(filename, content, description: description, publik: publik)
|
23
23
|
|
24
24
|
if no_browse
|
25
|
-
puts "Gist address: #{gist.link}"
|
25
|
+
output.puts "Gist address: #{gist.link}"
|
26
26
|
else
|
27
27
|
os_open(gist.link)
|
28
28
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../helpers/os_helper.rb'
|
4
|
-
require_relative '../git/repository.rb'
|
5
4
|
|
6
5
|
module Geet
|
7
6
|
module Services
|
@@ -14,26 +13,30 @@ module Geet
|
|
14
13
|
# :assignee_patterns
|
15
14
|
# :no_open_issue
|
16
15
|
#
|
17
|
-
def execute(
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def execute(
|
17
|
+
repository, title, description,
|
18
|
+
label_patterns: nil, milestone_pattern: nil, assignee_patterns: nil, no_open_issue: nil,
|
19
|
+
output: $stdout, **
|
20
|
+
)
|
21
|
+
labels_thread = select_labels(repository, label_patterns, output) if label_patterns
|
22
|
+
milestone_thread = find_milestone(repository, milestone_pattern, output) if milestone_pattern
|
23
|
+
assignees_thread = select_assignees(repository, assignee_patterns, output) if assignee_patterns
|
21
24
|
|
22
25
|
selected_labels = labels_thread&.join&.value
|
23
26
|
assignees = assignees_thread&.join&.value
|
24
27
|
milestone = milestone_thread&.join&.value
|
25
28
|
|
26
|
-
puts 'Creating the issue...'
|
29
|
+
output.puts 'Creating the issue...'
|
27
30
|
|
28
31
|
issue = repository.create_issue(title, description)
|
29
32
|
|
30
|
-
add_labels_thread = add_labels(issue, selected_labels) if selected_labels
|
31
|
-
set_milestone_thread = set_milestone(issue, milestone) if milestone
|
33
|
+
add_labels_thread = add_labels(issue, selected_labels, output) if selected_labels
|
34
|
+
set_milestone_thread = set_milestone(issue, milestone, output) if milestone
|
32
35
|
|
33
36
|
if assignees
|
34
|
-
assign_users_thread = assign_users(issue, assignees)
|
37
|
+
assign_users_thread = assign_users(issue, assignees, output)
|
35
38
|
else
|
36
|
-
assign_users_thread = assign_authenticated_user(repository, issue)
|
39
|
+
assign_users_thread = assign_authenticated_user(repository, issue, output)
|
37
40
|
end
|
38
41
|
|
39
42
|
add_labels_thread&.join
|
@@ -41,18 +44,20 @@ module Geet
|
|
41
44
|
assign_users_thread.join
|
42
45
|
|
43
46
|
if no_open_issue
|
44
|
-
puts "Issue address: #{issue.link}"
|
47
|
+
output.puts "Issue address: #{issue.link}"
|
45
48
|
else
|
46
49
|
os_open(issue.link)
|
47
50
|
end
|
51
|
+
|
52
|
+
issue
|
48
53
|
end
|
49
54
|
|
50
55
|
private
|
51
56
|
|
52
57
|
# Internal actions
|
53
58
|
|
54
|
-
def select_labels(repository, label_patterns)
|
55
|
-
puts 'Finding labels...'
|
59
|
+
def select_labels(repository, label_patterns, output)
|
60
|
+
output.puts 'Finding labels...'
|
56
61
|
|
57
62
|
Thread.new do
|
58
63
|
all_labels = repository.labels
|
@@ -61,8 +66,8 @@ module Geet
|
|
61
66
|
end
|
62
67
|
end
|
63
68
|
|
64
|
-
def find_milestone(repository, milestone_pattern)
|
65
|
-
puts 'Finding milestone...'
|
69
|
+
def find_milestone(repository, milestone_pattern, output)
|
70
|
+
output.puts 'Finding milestone...'
|
66
71
|
|
67
72
|
Thread.new do
|
68
73
|
if milestone_pattern =~ /\A\d+\Z/
|
@@ -75,8 +80,8 @@ module Geet
|
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
78
|
-
def select_assignees(repository, assignee_patterns)
|
79
|
-
puts 'Finding collaborators...'
|
83
|
+
def select_assignees(repository, assignee_patterns, output)
|
84
|
+
output.puts 'Finding collaborators...'
|
80
85
|
|
81
86
|
Thread.new do
|
82
87
|
all_collaborators = repository.collaborators
|
@@ -85,32 +90,32 @@ module Geet
|
|
85
90
|
end
|
86
91
|
end
|
87
92
|
|
88
|
-
def add_labels(issue, selected_labels)
|
89
|
-
puts "Adding labels #{selected_labels.join(', ')}..."
|
93
|
+
def add_labels(issue, selected_labels, output)
|
94
|
+
output.puts "Adding labels #{selected_labels.join(', ')}..."
|
90
95
|
|
91
96
|
Thread.new do
|
92
97
|
issue.add_labels(selected_labels)
|
93
98
|
end
|
94
99
|
end
|
95
100
|
|
96
|
-
def set_milestone(issue, milestone)
|
97
|
-
puts "Setting milestone #{milestone.title}..."
|
101
|
+
def set_milestone(issue, milestone, output)
|
102
|
+
output.puts "Setting milestone #{milestone.title}..."
|
98
103
|
|
99
104
|
Thread.new do
|
100
105
|
issue.edit(milestone: milestone.number)
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
104
|
-
def assign_users(issue, users)
|
105
|
-
puts "Assigning users #{users.join(', ')}..."
|
109
|
+
def assign_users(issue, users, output)
|
110
|
+
output.puts "Assigning users #{users.join(', ')}..."
|
106
111
|
|
107
112
|
Thread.new do
|
108
113
|
issue.assign_users(users)
|
109
114
|
end
|
110
115
|
end
|
111
116
|
|
112
|
-
def assign_authenticated_user(repository, issue)
|
113
|
-
puts 'Assigning authenticated user...'
|
117
|
+
def assign_authenticated_user(repository, issue, output)
|
118
|
+
output.puts 'Assigning authenticated user...'
|
114
119
|
|
115
120
|
Thread.new do
|
116
121
|
issue.assign_users(repository.authenticated_user)
|