geet 0.3.3 → 0.3.4
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/bin/geet +8 -8
- data/geet.gemspec +1 -1
- data/lib/geet/commandline/editor.rb +1 -1
- data/lib/geet/github/api_interface.rb +3 -2
- data/lib/geet/github/user.rb +62 -3
- data/lib/geet/services/create_gist.rb +3 -3
- data/lib/geet/services/create_issue.rb +17 -2
- data/lib/geet/services/create_pr.rb +13 -2
- data/lib/geet/services/list_milestones.rb +1 -1
- data/lib/geet/shared/http_error.rb +13 -0
- data/lib/geet/shared/repo_permissions.rb +25 -0
- data/lib/geet/shared/{constants.rb → selection.rb} +1 -1
- data/lib/geet/utils/attributes_selection_manager.rb +2 -2
- data/lib/geet/utils/git_client.rb +2 -2
- data/lib/geet/version.rb +1 -1
- data/spec/integration/create_issue_spec.rb +28 -24
- data/spec/integration/create_pr_spec.rb +67 -30
- data/spec/vcr_cassettes/create_issue.yml +446 -70
- data/spec/vcr_cassettes/create_issue_upstream.yml +158 -11
- data/spec/vcr_cassettes/create_pr.yml +474 -103
- data/spec/vcr_cassettes/create_pr_in_auto_mode_create_upstream.yml +407 -31
- data/spec/vcr_cassettes/create_pr_in_auto_mode_with_push.yml +408 -32
- data/spec/vcr_cassettes/create_pr_upstream.yml +415 -39
- data/spec/vcr_cassettes/create_pr_upstream_without_write_permissions.yml +305 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b30416607b1d594f4f4d38dc61668a8bb4d2b33
|
4
|
+
data.tar.gz: c76019c10cc053dc38d7fdc257dbfa0f07e76f48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8be0ea5f9f1be8f4307cced46bb846b84b5f74ecfd42f93d89266c57f4cb466913f07d9b849dece1ff4ff4214714d8663fb7316032fd47e10762c5669a528aca
|
7
|
+
data.tar.gz: a57c1f035b94aa97a7393da8e818f176683fd682542a2a80d5cf39203442cceefe13eb2ffef872e07d8f315e2e461511d4ef8104531dc9bb6f7dcc95bebbaa87
|
data/bin/geet
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'simple_scripting/configuration'
|
5
|
-
require_relative '../lib/geet/commandline/configuration
|
6
|
-
require_relative '../lib/geet/commandline/commands
|
7
|
-
require_relative '../lib/geet/commandline/editor
|
8
|
-
require_relative '../lib/geet/git/repository
|
9
|
-
require_relative '../lib/geet/helpers/summary_helper
|
10
|
-
require_relative '../lib/geet/shared/
|
11
|
-
require_relative '../lib/geet/utils/git_client
|
5
|
+
require_relative '../lib/geet/commandline/configuration'
|
6
|
+
require_relative '../lib/geet/commandline/commands'
|
7
|
+
require_relative '../lib/geet/commandline/editor'
|
8
|
+
require_relative '../lib/geet/git/repository'
|
9
|
+
require_relative '../lib/geet/helpers/summary_helper'
|
10
|
+
require_relative '../lib/geet/shared/selection'
|
11
|
+
require_relative '../lib/geet/utils/git_client'
|
12
12
|
Dir[File.join(__dir__, '../lib/geet/services/*.rb')].each { |filename| require filename }
|
13
13
|
|
14
14
|
class GeetLauncher
|
@@ -84,7 +84,7 @@ class GeetLauncher
|
|
84
84
|
|
85
85
|
def default_to_manual_selection(options, *params)
|
86
86
|
params.each do |param|
|
87
|
-
options[param] ||= Shared::
|
87
|
+
options[param] ||= Shared::Selection::MANUAL_LIST_SELECTION_FLAG
|
88
88
|
end
|
89
89
|
|
90
90
|
options
|
data/geet.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.required_ruby_version = '>= 2.3.0'
|
12
12
|
s.authors = ['Saverio Miroddi']
|
13
|
-
s.date = '2018-02-
|
13
|
+
s.date = '2018-02-05'
|
14
14
|
s.email = ['saverio.pub2@gmail.com']
|
15
15
|
s.homepage = 'https://github.com/saveriomiroddi/geet'
|
16
16
|
s.summary = 'Commandline interface for performing SCM (eg. GitHub) operations (eg. PR creation).'
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'net/http'
|
5
5
|
require 'json'
|
6
|
+
require_relative '../shared/http_error'
|
6
7
|
|
7
8
|
module Geet
|
8
9
|
module Github
|
@@ -53,8 +54,8 @@ module Geet
|
|
53
54
|
parsed_response = JSON.parse(response.body) if response.body
|
54
55
|
|
55
56
|
if error?(response)
|
56
|
-
|
57
|
-
raise(
|
57
|
+
error_message = decode_and_format_error(parsed_response)
|
58
|
+
raise Geet::Shared::HttpError.new(error_message, response.code)
|
58
59
|
end
|
59
60
|
|
60
61
|
return parsed_response if !multipage
|
data/lib/geet/github/user.rb
CHANGED
@@ -1,12 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../shared/repo_permissions'
|
4
|
+
require_relative '../shared/http_error'
|
5
|
+
|
3
6
|
module Geet
|
4
7
|
module Github
|
5
8
|
class User
|
9
|
+
include Geet::Shared::RepoPermissions
|
10
|
+
|
6
11
|
attr_reader :username
|
7
12
|
|
8
|
-
def initialize(username)
|
13
|
+
def initialize(username, api_interface)
|
9
14
|
@username = username
|
15
|
+
@api_interface = api_interface
|
16
|
+
end
|
17
|
+
|
18
|
+
# See #repo_permission.
|
19
|
+
#
|
20
|
+
def has_permission?(permission)
|
21
|
+
user_permission = self.class.repo_permission(@api_interface)
|
22
|
+
|
23
|
+
permission_greater_or_equal_to?(user_permission, permission)
|
24
|
+
end
|
25
|
+
|
26
|
+
# See https://developer.github.com/v3/repos/collaborators/#check-if-a-user-is-a-collaborator
|
27
|
+
#
|
28
|
+
def is_collaborator?
|
29
|
+
api_path = "collaborators/#{@username}"
|
30
|
+
|
31
|
+
begin
|
32
|
+
@api_interface.send_request(api_path)
|
33
|
+
|
34
|
+
# 204: user is a collaborator.
|
35
|
+
true
|
36
|
+
rescue Geet::Shared::HttpError => error
|
37
|
+
# 404: not a collaborator.
|
38
|
+
# Although the documentation mentions only 404, 403 is a valid response as well; it seems
|
39
|
+
# that 404 is given on private repositories, while 403 on public ones ("Must have push
|
40
|
+
# access to view repository collaborators.").
|
41
|
+
#
|
42
|
+
(error.code == 404 || error.code == 403) ? false : raise
|
43
|
+
end
|
10
44
|
end
|
11
45
|
|
12
46
|
# See https://developer.github.com/v3/users/#get-the-authenticated-user
|
@@ -16,7 +50,7 @@ module Geet
|
|
16
50
|
|
17
51
|
response = api_interface.send_request(api_path)
|
18
52
|
|
19
|
-
new(response.fetch('login'))
|
53
|
+
new(response.fetch('login'), api_interface)
|
20
54
|
end
|
21
55
|
|
22
56
|
# Returns an array of User instances
|
@@ -25,7 +59,32 @@ module Geet
|
|
25
59
|
api_path = 'collaborators'
|
26
60
|
response = api_interface.send_request(api_path, multipage: true)
|
27
61
|
|
28
|
-
response.map { |user_entry| new(user_entry.fetch('login')) }
|
62
|
+
response.map { |user_entry| new(user_entry.fetch('login'), api_interface) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# See https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level
|
66
|
+
#
|
67
|
+
def self.repo_permission(api_interface)
|
68
|
+
username = authenticated(api_interface).username
|
69
|
+
api_path = "collaborators/#{username}/permission"
|
70
|
+
|
71
|
+
response = api_interface.send_request(api_path)
|
72
|
+
|
73
|
+
permission = response.fetch('permission')
|
74
|
+
|
75
|
+
check_permission!(permission)
|
76
|
+
|
77
|
+
permission
|
78
|
+
end
|
79
|
+
|
80
|
+
class << self
|
81
|
+
private
|
82
|
+
|
83
|
+
# Future-proofing.
|
84
|
+
#
|
85
|
+
def check_permission!(permission)
|
86
|
+
raise "Unexpected permission #{permission.inspect}!" if !self::ALL_PERMISSIONS.include?(permission)
|
87
|
+
end
|
29
88
|
end
|
30
89
|
end
|
31
90
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../helpers/os_helper
|
4
|
-
require_relative '../github/api_interface
|
5
|
-
require_relative '../github/gist
|
3
|
+
require_relative '../helpers/os_helper'
|
4
|
+
require_relative '../github/api_interface'
|
5
|
+
require_relative '../github/gist'
|
6
6
|
|
7
7
|
module Geet
|
8
8
|
module Services
|
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'abstract_create_issue'
|
4
|
+
require_relative '../shared/repo_permissions'
|
4
5
|
|
5
6
|
module Geet
|
6
7
|
module Services
|
7
8
|
class CreateIssue < AbstractCreateIssue
|
9
|
+
include Geet::Shared::RepoPermissions
|
10
|
+
|
8
11
|
# options:
|
9
12
|
# :labels
|
10
13
|
# :milestone: number or description pattern.
|
@@ -16,11 +19,23 @@ module Geet
|
|
16
19
|
labels: nil, milestone: nil, assignees: nil, no_open_issue: nil,
|
17
20
|
**
|
18
21
|
)
|
19
|
-
|
22
|
+
# Inefficient (in worst case, triples the pre issue creation waiting time: #is_collaborator?,
|
23
|
+
# #has_permissions?, and the attributes batch), but not trivial to speed up. Not difficult
|
24
|
+
# either, but currently not worth spending time.
|
25
|
+
#
|
26
|
+
# Theoretically, #is_collaborator? could be skipped, but this is cleaner.
|
27
|
+
user_has_write_permissions = @repository.authenticated_user.is_collaborator? &&
|
28
|
+
@repository.authenticated_user.has_permission?(PERMISSION_WRITE)
|
29
|
+
|
30
|
+
if user_has_write_permissions
|
31
|
+
selected_labels, selected_milestone, selected_assignees = find_and_select_attributes(labels, milestone, assignees)
|
32
|
+
end
|
20
33
|
|
21
34
|
issue = create_issue(title, description)
|
22
35
|
|
23
|
-
|
36
|
+
if user_has_write_permissions
|
37
|
+
edit_issue(issue, selected_labels, selected_milestone, selected_assignees)
|
38
|
+
end
|
24
39
|
|
25
40
|
if no_open_issue
|
26
41
|
@out.puts "Issue address: #{issue.link}"
|
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'abstract_create_issue'
|
4
|
+
require_relative '../shared/repo_permissions'
|
4
5
|
|
5
6
|
module Geet
|
6
7
|
module Services
|
7
8
|
class CreatePr < AbstractCreateIssue
|
9
|
+
include Geet::Shared::RepoPermissions
|
10
|
+
|
8
11
|
DEFAULT_GIT_CLIENT = Geet::Utils::GitClient.new
|
9
12
|
|
10
13
|
def initialize(repository, out: $stdout, git_client: DEFAULT_GIT_CLIENT)
|
@@ -24,13 +27,21 @@ module Geet
|
|
24
27
|
)
|
25
28
|
ensure_clean_tree if automated_mode
|
26
29
|
|
27
|
-
|
30
|
+
# See CreateIssue#execute for notes about performance.
|
31
|
+
user_has_write_permissions = @repository.authenticated_user.is_collaborator? &&
|
32
|
+
@repository.authenticated_user.has_permission?(PERMISSION_WRITE)
|
33
|
+
|
34
|
+
if user_has_write_permissions
|
35
|
+
selected_labels, selected_milestone, selected_reviewers = find_and_select_attributes(labels, milestone, reviewers)
|
36
|
+
end
|
28
37
|
|
29
38
|
sync_with_upstream_branch if automated_mode
|
30
39
|
|
31
40
|
pr = create_pr(title, description)
|
32
41
|
|
33
|
-
|
42
|
+
if user_has_write_permissions
|
43
|
+
edit_pr(pr, selected_labels, selected_milestone, selected_reviewers)
|
44
|
+
end
|
34
45
|
|
35
46
|
if no_open_pr
|
36
47
|
@out.puts "PR address: #{pr.link}"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Geet
|
4
|
+
module Shared
|
5
|
+
module RepoPermissions
|
6
|
+
PERMISSION_ADMIN = 'admin'
|
7
|
+
PERMISSION_WRITE = 'write'
|
8
|
+
PERMISSION_READ = 'read'
|
9
|
+
PERMISSION_NONE = 'none'
|
10
|
+
|
11
|
+
ALL_PERMISSIONS = [
|
12
|
+
PERMISSION_ADMIN,
|
13
|
+
PERMISSION_WRITE,
|
14
|
+
PERMISSION_READ,
|
15
|
+
PERMISSION_NONE,
|
16
|
+
]
|
17
|
+
|
18
|
+
# Not worth creating a Permission class at this stage.
|
19
|
+
#
|
20
|
+
def permission_greater_or_equal_to?(subject_permission, object_permission)
|
21
|
+
ALL_PERMISSIONS.index(subject_permission) <= ALL_PERMISSIONS.index(object_permission)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'manual_list_selection'
|
4
4
|
require_relative 'string_matching_selection'
|
5
|
-
require_relative '../shared/
|
5
|
+
require_relative '../shared/selection'
|
6
6
|
|
7
7
|
module Geet
|
8
8
|
module Utils
|
@@ -14,7 +14,7 @@ module Geet
|
|
14
14
|
# multiple attributes are required (typically, three).
|
15
15
|
#
|
16
16
|
class AttributesSelectionManager
|
17
|
-
include Geet::Shared::
|
17
|
+
include Geet::Shared::Selection
|
18
18
|
|
19
19
|
# Workaround for VCR not supporting multithreading; see https://github.com/vcr/vcr/issues/200.
|
20
20
|
#
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'shellwords'
|
4
|
-
require_relative '../helpers/os_helper
|
4
|
+
require_relative '../helpers/os_helper'
|
5
5
|
|
6
6
|
module Geet
|
7
7
|
module Utils
|
@@ -153,7 +153,7 @@ module Geet
|
|
153
153
|
private
|
154
154
|
|
155
155
|
def gitdir_option
|
156
|
-
"
|
156
|
+
"-C #{@location.shellescape}" if @location
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
data/lib/geet/version.rb
CHANGED
@@ -7,12 +7,12 @@ require_relative '../../lib/geet/services/create_issue'
|
|
7
7
|
|
8
8
|
describe Geet::Services::CreateIssue do
|
9
9
|
let(:git_client) { Geet::Utils::GitClient.new }
|
10
|
-
let(:repository) { Geet::Git::Repository.new(git_client: git_client) }
|
10
|
+
let(:repository) { Geet::Git::Repository.new(warnings: false, git_client: git_client) }
|
11
11
|
let(:upstream_repository) { Geet::Git::Repository.new(upstream: true, git_client: git_client) }
|
12
12
|
|
13
13
|
context 'with labels, assignees and milestones' do
|
14
14
|
it 'should create an issue' do
|
15
|
-
allow(git_client).to receive(:remote).with('origin').and_return('git@github.com:donaldduck/
|
15
|
+
allow(git_client).to receive(:remote).with('origin').and_return('git@github.com:donaldduck/testrepo_f')
|
16
16
|
|
17
17
|
expected_output = <<~STR
|
18
18
|
Finding labels...
|
@@ -21,8 +21,8 @@ describe Geet::Services::CreateIssue do
|
|
21
21
|
Creating the issue...
|
22
22
|
Adding labels bug, invalid...
|
23
23
|
Setting milestone 0.0.1...
|
24
|
-
Assigning users
|
25
|
-
Issue address: https://github.com/donaldduck/
|
24
|
+
Assigning users donaldduck, donald-fr...
|
25
|
+
Issue address: https://github.com/donaldduck/testrepo_f/issues/2
|
26
26
|
STR
|
27
27
|
|
28
28
|
actual_output = StringIO.new
|
@@ -30,40 +30,44 @@ describe Geet::Services::CreateIssue do
|
|
30
30
|
actual_created_issue = VCR.use_cassette('create_issue') do
|
31
31
|
described_class.new(repository, out: actual_output).execute(
|
32
32
|
'Title', 'Description',
|
33
|
-
labels: 'bug,invalid', milestone: '0.0.1', assignees: '
|
33
|
+
labels: 'bug,invalid', milestone: '0.0.1', assignees: 'donaldduck,donald-fr',
|
34
34
|
no_open_issue: true
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
38
38
|
expect(actual_output.string).to eql(expected_output)
|
39
39
|
|
40
|
-
expect(actual_created_issue.number).to eql(
|
40
|
+
expect(actual_created_issue.number).to eql(2)
|
41
41
|
expect(actual_created_issue.title).to eql('Title')
|
42
|
-
expect(actual_created_issue.link).to eql('https://github.com/donaldduck/
|
42
|
+
expect(actual_created_issue.link).to eql('https://github.com/donaldduck/testrepo_f/issues/2')
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
context 'without write permissions' do
|
47
|
+
context 'without labels, assignees and milestones' do
|
48
|
+
it 'should create an upstream issue' do
|
49
|
+
allow(git_client).to receive(:current_branch).and_return('mybranch')
|
50
|
+
allow(git_client).to receive(:remote).with('origin').and_return('git@github.com:donaldduck/testrepo')
|
51
|
+
allow(git_client).to receive(:remote).with('upstream').and_return('git@github.com:momcorp/therepo')
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
expected_output = <<~STR
|
54
|
+
Creating the issue...
|
55
|
+
Issue address: https://github.com/momcorp/therepo/issues/42
|
56
|
+
STR
|
55
57
|
|
56
|
-
|
58
|
+
actual_output = StringIO.new
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
actual_created_issue = VCR.use_cassette('create_issue_upstream') do
|
61
|
+
create_options = { no_open_issue: true, out: actual_output }
|
62
|
+
described_class.new(upstream_repository, out: actual_output).execute('Title', 'Description', create_options)
|
63
|
+
end
|
62
64
|
|
63
|
-
|
65
|
+
expect(actual_output.string).to eql(expected_output)
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
expect(actual_created_issue.number).to eql(42)
|
68
|
+
expect(actual_created_issue.title).to eql('Title')
|
69
|
+
expect(actual_created_issue.link).to eql('https://github.com/momcorp/therepo/issues/42')
|
70
|
+
end
|
71
|
+
end
|
68
72
|
end
|
69
73
|
end
|