geet 0.27.7 → 0.28.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/.github/workflows/ci.yml +0 -1
- data/README.md +8 -17
- data/geet.gemspec +1 -1
- data/lib/geet/commandline/configuration.rb +1 -1
- data/lib/geet/git/repository.rb +36 -70
- data/lib/geet/helpers/services_workflow_helper.rb +1 -1
- data/lib/geet/services/add_upstream_repo.rb +1 -1
- data/lib/geet/services/close_milestones.rb +1 -1
- data/lib/geet/services/comment_pr.rb +1 -1
- data/lib/geet/services/create_issue.rb +27 -28
- data/lib/geet/services/create_label.rb +2 -2
- data/lib/geet/services/create_milestone.rb +2 -2
- data/lib/geet/services/create_pr.rb +20 -27
- data/lib/geet/services/list_issues.rb +3 -3
- data/lib/geet/services/list_labels.rb +1 -1
- data/lib/geet/services/list_milestones.rb +7 -7
- data/lib/geet/services/list_prs.rb +1 -1
- data/lib/geet/services/merge_pr.rb +4 -3
- data/lib/geet/services/open_pr.rb +1 -1
- data/lib/geet/utils/git_client.rb +0 -11
- data/lib/geet/version.rb +1 -1
- data/spec/integration/create_label_spec.rb +0 -21
- data/spec/integration/create_pr_spec.rb +0 -1
- data/spec/integration/list_issues_spec.rb +0 -46
- data/spec/integration/list_labels_spec.rb +0 -27
- data/spec/integration/list_milestones_spec.rb +0 -27
- data/spec/integration/merge_pr_spec.rb +1 -31
- data/spec/spec_helper.rb +0 -3
- metadata +3 -17
- data/lib/geet/github/remote_repository.rb +0 -53
- data/lib/geet/gitlab/api_interface.rb +0 -212
- data/lib/geet/gitlab/issue.rb +0 -62
- data/lib/geet/gitlab/label.rb +0 -66
- data/lib/geet/gitlab/milestone.rb +0 -78
- data/lib/geet/gitlab/pr.rb +0 -114
- data/lib/geet/gitlab/user.rb +0 -52
- data/lib/geet/services/abstract_create_issue.rb +0 -21
- data/spec/vcr_cassettes/gitlab_com/create_label.yml +0 -64
- data/spec/vcr_cassettes/gitlab_com/list_issues.yml +0 -84
- data/spec/vcr_cassettes/gitlab_com/list_issues_with_assignee.yml +0 -162
- data/spec/vcr_cassettes/gitlab_com/list_labels.yml +0 -80
- data/spec/vcr_cassettes/gitlab_com/list_milestones.yml +0 -397
- data/spec/vcr_cassettes/gitlab_com/merge_pr.yml +0 -144
|
@@ -23,7 +23,7 @@ module Geet
|
|
|
23
23
|
params(
|
|
24
24
|
assignee: T.nilable(String)
|
|
25
25
|
)
|
|
26
|
-
.returns(T
|
|
26
|
+
.returns(T::Array[Github::AbstractIssue])
|
|
27
27
|
}
|
|
28
28
|
def execute(assignee: nil)
|
|
29
29
|
selected_assignee = find_and_select_attributes(assignee) if assignee
|
|
@@ -40,14 +40,14 @@ module Geet
|
|
|
40
40
|
sig {
|
|
41
41
|
params(
|
|
42
42
|
assignee: String
|
|
43
|
-
).returns(
|
|
43
|
+
).returns(Github::User)
|
|
44
44
|
}
|
|
45
45
|
def find_and_select_attributes(assignee)
|
|
46
46
|
selection_manager = Geet::Utils::AttributesSelectionManager.new(@repository, out: @out)
|
|
47
47
|
|
|
48
48
|
selection_manager.add_attribute(:collaborators, "assignee", assignee, SELECTION_SINGLE, name_method: :username)
|
|
49
49
|
|
|
50
|
-
T.cast(selection_manager.select_attributes[0],
|
|
50
|
+
T.cast(selection_manager.select_attributes[0], Github::User)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -18,7 +18,7 @@ module Geet
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
sig {
|
|
21
|
-
returns(T::Array[
|
|
21
|
+
returns(T::Array[Github::Milestone])
|
|
22
22
|
}
|
|
23
23
|
def execute
|
|
24
24
|
milestones = find_milestones
|
|
@@ -47,7 +47,7 @@ module Geet
|
|
|
47
47
|
# are considered formatters, conceptually external to the class.
|
|
48
48
|
sig {
|
|
49
49
|
params(
|
|
50
|
-
milestone:
|
|
50
|
+
milestone: Github::Milestone
|
|
51
51
|
).returns(String)
|
|
52
52
|
}
|
|
53
53
|
def milestone_description(milestone)
|
|
@@ -57,7 +57,7 @@ module Geet
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
sig {
|
|
60
|
-
returns(T::Array[
|
|
60
|
+
returns(T::Array[Github::Milestone])
|
|
61
61
|
}
|
|
62
62
|
def find_milestones
|
|
63
63
|
@out.puts "Finding milestones..."
|
|
@@ -67,13 +67,13 @@ module Geet
|
|
|
67
67
|
|
|
68
68
|
sig {
|
|
69
69
|
params(
|
|
70
|
-
milestones: T::Array[
|
|
70
|
+
milestones: T::Array[Github::Milestone]
|
|
71
71
|
).returns(
|
|
72
72
|
T::Hash[
|
|
73
|
-
|
|
73
|
+
Github::Milestone,
|
|
74
74
|
{
|
|
75
|
-
issues: T::Array[
|
|
76
|
-
prs: T::Array[
|
|
75
|
+
issues: T::Array[Github::Issue],
|
|
76
|
+
prs: T::Array[Github::PR],
|
|
77
77
|
}
|
|
78
78
|
]
|
|
79
79
|
)
|
|
@@ -29,7 +29,7 @@ module Geet
|
|
|
29
29
|
delete_branch: T::Boolean,
|
|
30
30
|
squash: T::Boolean
|
|
31
31
|
)
|
|
32
|
-
.returns(
|
|
32
|
+
.returns(Github::PR)
|
|
33
33
|
}
|
|
34
34
|
def execute(delete_branch: false, squash: false)
|
|
35
35
|
merge_method = "squash" if squash
|
|
@@ -75,12 +75,13 @@ module Geet
|
|
|
75
75
|
|
|
76
76
|
sig { void }
|
|
77
77
|
def check_no_missing_upstream_commits
|
|
78
|
-
|
|
78
|
+
remote_main_branch = "#{Utils::GitClient::ORIGIN_NAME}/#{@git_client.main_branch}"
|
|
79
|
+
missing_upstream_commits = @git_client.cherry("HEAD", head: remote_main_branch)
|
|
79
80
|
|
|
80
81
|
raise "Found #{missing_upstream_commits.size} missing upstream commits!" if missing_upstream_commits.any?
|
|
81
82
|
end
|
|
82
83
|
|
|
83
|
-
sig { params(pr:
|
|
84
|
+
sig { params(pr: Github::PR, merge_method: T.nilable(String)).void }
|
|
84
85
|
def merge_pr(pr, merge_method: nil)
|
|
85
86
|
@out.puts "Merging PR ##{pr.number}..."
|
|
86
87
|
|
|
@@ -209,17 +209,6 @@ module Geet
|
|
|
209
209
|
T.must(path.split("/")[0])
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
sig { returns(String) }
|
|
213
|
-
def provider_domain
|
|
214
|
-
# We assume that it's not possible to have origin and upstream on different providers.
|
|
215
|
-
|
|
216
|
-
domain = T.must(remote()[REMOTE_URL_REGEX, 2])
|
|
217
|
-
|
|
218
|
-
raise "Can't identify domain in the provider domain string: #{domain}" if domain !~ /\w+\.\w+/
|
|
219
|
-
|
|
220
|
-
domain
|
|
221
|
-
end
|
|
222
|
-
|
|
223
212
|
# Returns the URL of the remote with the given name.
|
|
224
213
|
# Sanity checks are performed.
|
|
225
214
|
#
|
data/lib/geet/version.rb
CHANGED
|
@@ -85,25 +85,4 @@ describe Geet::Services::CreateLabel do
|
|
|
85
85
|
end
|
|
86
86
|
end # context 'with github.com'
|
|
87
87
|
|
|
88
|
-
context "with gitlab.com" do
|
|
89
|
-
it "should create a label" do
|
|
90
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:donaldduck/testproject")
|
|
91
|
-
|
|
92
|
-
expected_output = <<~STR
|
|
93
|
-
Creating label...
|
|
94
|
-
Created with color #123456
|
|
95
|
-
STR
|
|
96
|
-
|
|
97
|
-
actual_output = StringIO.new
|
|
98
|
-
|
|
99
|
-
actual_created_label = VCR.use_cassette("gitlab_com/create_label") do
|
|
100
|
-
described_class.new(repository, out: actual_output).execute("my_label", color: "123456")
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
expect(actual_output.string).to eql(expected_output)
|
|
104
|
-
|
|
105
|
-
expect(actual_created_label.name).to eql("my_label")
|
|
106
|
-
expect(actual_created_label.color).to eql("123456")
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
88
|
end
|
|
@@ -264,7 +264,6 @@ describe Geet::Services::CreatePr do
|
|
|
264
264
|
allow(git_client).to receive(:push)
|
|
265
265
|
allow(git_client).to receive(:remote).with(no_args).and_return("git@github.com:donaldduck/testrepo_f")
|
|
266
266
|
|
|
267
|
-
# Mock the repository and PR without enable_automerge method (simulating GitLab)
|
|
268
267
|
allow(repository).to receive(:authenticated_user).and_return(
|
|
269
268
|
double(is_collaborator?: true, has_permission?: true)
|
|
270
269
|
)
|
|
@@ -79,50 +79,4 @@ describe Geet::Services::ListIssues do
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
context "with gitlab.com" do
|
|
83
|
-
it "should list the issues" do
|
|
84
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:donaldduck/testproject")
|
|
85
|
-
|
|
86
|
-
expected_output = <<~STR
|
|
87
|
-
2. I like more pizza (https://gitlab.com/donaldduck/testproject/issues/2)
|
|
88
|
-
1. I like pizza (https://gitlab.com/donaldduck/testproject/issues/1)
|
|
89
|
-
STR
|
|
90
|
-
expected_issue_numbers = [2, 1]
|
|
91
|
-
|
|
92
|
-
actual_output = StringIO.new
|
|
93
|
-
|
|
94
|
-
service_result = VCR.use_cassette("gitlab_com/list_issues") do
|
|
95
|
-
described_class.new(repository, out: actual_output).execute
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
actual_issue_numbers = service_result.map(&:number)
|
|
99
|
-
|
|
100
|
-
expect(actual_output.string).to eql(expected_output)
|
|
101
|
-
expect(actual_issue_numbers).to eql(expected_issue_numbers)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
context "with assignee filtering" do
|
|
105
|
-
it "should list the issues" do
|
|
106
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:donaldduck/testproject")
|
|
107
|
-
|
|
108
|
-
expected_output = <<~STR
|
|
109
|
-
Finding collaborators...
|
|
110
|
-
3. This is a test issue (https://gitlab.com/donaldduck/testproject/issues/3)
|
|
111
|
-
STR
|
|
112
|
-
expected_issue_numbers = [3]
|
|
113
|
-
|
|
114
|
-
actual_output = StringIO.new
|
|
115
|
-
|
|
116
|
-
service_result = VCR.use_cassette("gitlab_com/list_issues_with_assignee") do
|
|
117
|
-
described_class.new(repository, out: actual_output).execute(assignee: "donald-fr")
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
actual_issue_numbers = service_result.map(&:number)
|
|
121
|
-
|
|
122
|
-
expect(actual_output.string).to eql(expected_output)
|
|
123
|
-
expect(actual_issue_numbers).to eql(expected_issue_numbers)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
end # with gitlab.com
|
|
128
82
|
end
|
|
@@ -55,31 +55,4 @@ describe Geet::Services::ListLabels do
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
context "with gitlab.com" do
|
|
59
|
-
it "should list the labels" do
|
|
60
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:donaldduck/testproject")
|
|
61
|
-
|
|
62
|
-
expected_output = <<~STR
|
|
63
|
-
- bug (#d9534f)
|
|
64
|
-
- confirmed (#d9534f)
|
|
65
|
-
- critical (#d9534f)
|
|
66
|
-
- discussion (#428bca)
|
|
67
|
-
- documentation (#f0ad4e)
|
|
68
|
-
- enhancement (#5cb85c)
|
|
69
|
-
- suggestion (#428bca)
|
|
70
|
-
- support (#f0ad4e)
|
|
71
|
-
STR
|
|
72
|
-
expected_label_names = %w[bug confirmed critical discussion documentation enhancement suggestion support]
|
|
73
|
-
|
|
74
|
-
actual_output = StringIO.new
|
|
75
|
-
actual_labels = VCR.use_cassette("gitlab.com/list_labels") do
|
|
76
|
-
described_class.new(repository, out: actual_output).execute
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
actual_label_names = actual_labels.map(&:name)
|
|
80
|
-
|
|
81
|
-
expect(actual_output.string).to eql(expected_output)
|
|
82
|
-
expect(actual_label_names).to eql(expected_label_names)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
58
|
end
|
|
@@ -78,31 +78,4 @@ describe Geet::Services::ListMilestones do
|
|
|
78
78
|
end
|
|
79
79
|
end # context 'with github.com'
|
|
80
80
|
|
|
81
|
-
context "with gitlab.com" do
|
|
82
|
-
it "should list the milestones" do
|
|
83
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:donaldduck/testproject")
|
|
84
|
-
|
|
85
|
-
expected_output = <<~STR
|
|
86
|
-
Finding milestones...
|
|
87
|
-
Finding issues and PRs...
|
|
88
|
-
|
|
89
|
-
2. Milestone 2
|
|
90
|
-
1. Milestone 1
|
|
91
|
-
3. This is a test issue (https://gitlab.com/donaldduck/testproject/issues/3)
|
|
92
|
-
1. Merge request 1 (https://gitlab.com/donaldduck/testproject/merge_requests/1)
|
|
93
|
-
STR
|
|
94
|
-
expected_milestone_numbers = [2, 1]
|
|
95
|
-
|
|
96
|
-
actual_output = StringIO.new
|
|
97
|
-
|
|
98
|
-
service_result = VCR.use_cassette("gitlab_com/list_milestones") do
|
|
99
|
-
described_class.new(repository, out: actual_output).execute
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
actual_milestone_numbers = service_result.map(&:number)
|
|
103
|
-
|
|
104
|
-
expect(actual_output.string).to eql(expected_output)
|
|
105
|
-
expect(actual_milestone_numbers).to eql(expected_milestone_numbers)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
81
|
end
|
|
@@ -20,7 +20,7 @@ describe Geet::Services::MergePr do
|
|
|
20
20
|
before :each do
|
|
21
21
|
expect(git_client).to receive(:fetch).twice
|
|
22
22
|
expect(git_client).to receive(:push)
|
|
23
|
-
expect(git_client).to receive(:cherry).with("HEAD", head:
|
|
23
|
+
expect(git_client).to receive(:cherry).with("HEAD", head: "origin/#{main_branch}").and_return([])
|
|
24
24
|
expect(git_client).to receive(:remote_branch_gone?).and_return(true)
|
|
25
25
|
expect(git_client).to receive(:checkout).with(main_branch)
|
|
26
26
|
expect(git_client).to receive(:rebase)
|
|
@@ -86,34 +86,4 @@ describe Geet::Services::MergePr do
|
|
|
86
86
|
end
|
|
87
87
|
end # context 'with github.com'
|
|
88
88
|
|
|
89
|
-
context "with gitlab.com" do
|
|
90
|
-
let(:repository_name) { "testproject" }
|
|
91
|
-
|
|
92
|
-
it "should merge the PR for the current branch" do
|
|
93
|
-
allow(git_client).to receive(:current_branch).and_return(branch)
|
|
94
|
-
allow(git_client).to receive(:main_branch).and_return(main_branch)
|
|
95
|
-
allow(git_client).to receive(:remote).with(no_args).and_return("git@gitlab.com:#{owner}/#{repository_name}")
|
|
96
|
-
|
|
97
|
-
expected_pr_number = 4
|
|
98
|
-
expected_output = <<~STR
|
|
99
|
-
Finding PR with head (#{owner}:#{branch})...
|
|
100
|
-
Merging PR ##{expected_pr_number}...
|
|
101
|
-
Fetching repository...
|
|
102
|
-
Checking out #{main_branch}...
|
|
103
|
-
Rebasing...
|
|
104
|
-
Deleting local branch mybranch...
|
|
105
|
-
STR
|
|
106
|
-
|
|
107
|
-
actual_output = StringIO.new
|
|
108
|
-
|
|
109
|
-
service_result = VCR.use_cassette("gitlab_com/merge_pr") do
|
|
110
|
-
described_class.new(repository, out: actual_output, git_client: git_client).execute
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
actual_pr_number = service_result.number
|
|
114
|
-
|
|
115
|
-
expect(actual_output.string).to eql(expected_output)
|
|
116
|
-
expect(actual_pr_number).to eql(expected_pr_number)
|
|
117
|
-
end
|
|
118
|
-
end # context 'with gitlab.com'
|
|
119
89
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -23,9 +23,6 @@ VCR.configure do |config|
|
|
|
23
23
|
Base64.strict_encode64("#{user}:#{api_token}")
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
config.filter_sensitive_data("<GITLAB_CREDENTIALS>") do
|
|
27
|
-
ENV.fetch("GITLAB_API_TOKEN")
|
|
28
|
-
end
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
Geet::Utils::AttributesSelectionManager.serialize_requests = true
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: geet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.28.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Saverio Miroddi
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-02-
|
|
10
|
+
date: 2026-02-21 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -145,20 +145,12 @@ files:
|
|
|
145
145
|
- lib/geet/github/label.rb
|
|
146
146
|
- lib/geet/github/milestone.rb
|
|
147
147
|
- lib/geet/github/pr.rb
|
|
148
|
-
- lib/geet/github/remote_repository.rb
|
|
149
148
|
- lib/geet/github/user.rb
|
|
150
|
-
- lib/geet/gitlab/api_interface.rb
|
|
151
|
-
- lib/geet/gitlab/issue.rb
|
|
152
|
-
- lib/geet/gitlab/label.rb
|
|
153
|
-
- lib/geet/gitlab/milestone.rb
|
|
154
|
-
- lib/geet/gitlab/pr.rb
|
|
155
|
-
- lib/geet/gitlab/user.rb
|
|
156
149
|
- lib/geet/helpers/json_helper.rb
|
|
157
150
|
- lib/geet/helpers/os_helper.rb
|
|
158
151
|
- lib/geet/helpers/services_workflow_helper.rb
|
|
159
152
|
- lib/geet/helpers/summary_helper.rb
|
|
160
153
|
- lib/geet/resources/templates/edit_summary.md
|
|
161
|
-
- lib/geet/services/abstract_create_issue.rb
|
|
162
154
|
- lib/geet/services/add_upstream_repo.rb
|
|
163
155
|
- lib/geet/services/close_milestones.rb
|
|
164
156
|
- lib/geet/services/comment_pr.rb
|
|
@@ -283,12 +275,6 @@ files:
|
|
|
283
275
|
- spec/vcr_cassettes/github_com/merge_pr.yml
|
|
284
276
|
- spec/vcr_cassettes/github_com/merge_pr_with_branch_deletion.yml
|
|
285
277
|
- spec/vcr_cassettes/github_com/open_pr.yml
|
|
286
|
-
- spec/vcr_cassettes/gitlab_com/create_label.yml
|
|
287
|
-
- spec/vcr_cassettes/gitlab_com/list_issues.yml
|
|
288
|
-
- spec/vcr_cassettes/gitlab_com/list_issues_with_assignee.yml
|
|
289
|
-
- spec/vcr_cassettes/gitlab_com/list_labels.yml
|
|
290
|
-
- spec/vcr_cassettes/gitlab_com/list_milestones.yml
|
|
291
|
-
- spec/vcr_cassettes/gitlab_com/merge_pr.yml
|
|
292
278
|
- spec/vcr_cassettes/list_milestones_upstream.yml
|
|
293
279
|
- spec/vcr_cassettes/list_prs.yml
|
|
294
280
|
- spec/vcr_cassettes/list_prs_upstream.yml
|
|
@@ -310,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
310
296
|
- !ruby/object:Gem::Version
|
|
311
297
|
version: '0'
|
|
312
298
|
requirements: []
|
|
313
|
-
rubygems_version:
|
|
299
|
+
rubygems_version: 3.6.9
|
|
314
300
|
specification_version: 4
|
|
315
301
|
summary: Commandline interface for performing SCM host operations, eg. create a PR
|
|
316
302
|
on GitHub
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
# typed: strict
|
|
3
|
-
|
|
4
|
-
module Geet
|
|
5
|
-
module Github
|
|
6
|
-
# A remote repository. Currently only provides the parent path.
|
|
7
|
-
#
|
|
8
|
-
# It's a difficult choice whether to independently use the repository path, or relying on the one
|
|
9
|
-
# stored in the ApiInterface.
|
|
10
|
-
# The former design is conceptually cleaner, but it practically (as of the current design) introduces
|
|
11
|
-
# duplication. All in all, for simplicity, the latter design is chosen, but is subject to redesign.
|
|
12
|
-
#
|
|
13
|
-
class RemoteRepository
|
|
14
|
-
extend T::Sig
|
|
15
|
-
|
|
16
|
-
# Nil if the repository is not a fork.
|
|
17
|
-
#
|
|
18
|
-
sig { returns(T.nilable(String)) }
|
|
19
|
-
attr_reader :parent_path
|
|
20
|
-
|
|
21
|
-
sig {
|
|
22
|
-
params(
|
|
23
|
-
api_interface: Geet::Github::ApiInterface,
|
|
24
|
-
parent_path: T.nilable(String)
|
|
25
|
-
).void
|
|
26
|
-
}
|
|
27
|
-
def initialize(api_interface, parent_path: nil)
|
|
28
|
-
@api_interface = api_interface
|
|
29
|
-
@parent_path = parent_path
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Get the repository parent path.
|
|
33
|
-
#
|
|
34
|
-
# https://docs.github.com/en/rest/reference/repos#get-a-repository
|
|
35
|
-
#
|
|
36
|
-
sig {
|
|
37
|
-
params(
|
|
38
|
-
api_interface: Geet::Github::ApiInterface
|
|
39
|
-
).returns(Geet::Github::RemoteRepository)
|
|
40
|
-
}
|
|
41
|
-
def self.find(api_interface)
|
|
42
|
-
api_path = "/repos/#{api_interface.repository_path}"
|
|
43
|
-
|
|
44
|
-
response = T.cast(api_interface.send_request(api_path), T::Hash[String, T.untyped])
|
|
45
|
-
|
|
46
|
-
parent_hash = T.cast(response["parent"], T.nilable(T::Hash[String, T.untyped]))
|
|
47
|
-
parent_path = T.cast(parent_hash&.fetch("full_name"), T.nilable(String))
|
|
48
|
-
|
|
49
|
-
new(api_interface, parent_path:)
|
|
50
|
-
end
|
|
51
|
-
end # module RemoteRepository
|
|
52
|
-
end # module GitHub
|
|
53
|
-
end # module Geet
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
# typed: strict
|
|
3
|
-
|
|
4
|
-
require "cgi"
|
|
5
|
-
require "uri"
|
|
6
|
-
require "net/http"
|
|
7
|
-
require "json"
|
|
8
|
-
|
|
9
|
-
module Geet
|
|
10
|
-
module Gitlab
|
|
11
|
-
class ApiInterface
|
|
12
|
-
extend T::Sig
|
|
13
|
-
|
|
14
|
-
API_BASE_URL = "https://gitlab.com/api/v4"
|
|
15
|
-
|
|
16
|
-
sig { returns(T.nilable(String)) }
|
|
17
|
-
attr_reader :repository_path
|
|
18
|
-
|
|
19
|
-
# repo_path: "path/namespace"; required for the current GitLab operations.
|
|
20
|
-
# upstream: boolean; required for the current GitLab operations.
|
|
21
|
-
#
|
|
22
|
-
sig {
|
|
23
|
-
params(
|
|
24
|
-
api_token: String,
|
|
25
|
-
repo_path: String,
|
|
26
|
-
upstream: T::Boolean
|
|
27
|
-
).void
|
|
28
|
-
}
|
|
29
|
-
def initialize(api_token, repo_path:, upstream:)
|
|
30
|
-
@api_token = api_token
|
|
31
|
-
@path_with_namespace = repo_path
|
|
32
|
-
@upstream = upstream
|
|
33
|
-
@repository_path = T.let(nil, T.nilable(String))
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
sig { returns(T::Boolean) }
|
|
37
|
-
def upstream?
|
|
38
|
-
@upstream
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
sig {
|
|
42
|
-
params(
|
|
43
|
-
encoded: T::Boolean
|
|
44
|
-
).returns(String)
|
|
45
|
-
}
|
|
46
|
-
def path_with_namespace(encoded: false)
|
|
47
|
-
encoded ? CGI.escape(@path_with_namespace) : @path_with_namespace
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Send a request.
|
|
51
|
-
#
|
|
52
|
-
sig {
|
|
53
|
-
params(
|
|
54
|
-
# Appended to the API URL.
|
|
55
|
-
# for root path, prepend a `/`:
|
|
56
|
-
# - use `/gists` for `https://api.github.com/gists`
|
|
57
|
-
# when owner/project/repos is included, don't prepend `/`:
|
|
58
|
-
# - use `issues` for `https://api.github.com/myowner/myproject/repos/issues`
|
|
59
|
-
api_path: String,
|
|
60
|
-
params: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
61
|
-
# If present, will generate a POST request, otherwise, a GET
|
|
62
|
-
data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
|
|
63
|
-
# Set true for paged Github responses (eg. issues); it will make the method
|
|
64
|
-
multipage: T::Boolean,
|
|
65
|
-
# Method (:get, :patch, :post, :put and :delete)
|
|
66
|
-
# :get and :post are automatically inferred by the presence of :data; the other cases must be specified.
|
|
67
|
-
http_method: T.nilable(Symbol)
|
|
68
|
-
# Returns the parsed response, or an Array, in case of multipage.
|
|
69
|
-
# Where no body is present in the response, nil is returned.
|
|
70
|
-
).returns(T.nilable(T.any(T::Hash[String, T.untyped], T::Array[T::Hash[String, T.untyped]])))
|
|
71
|
-
}
|
|
72
|
-
def send_request(api_path, params: nil, data: nil, multipage: false, http_method: nil)
|
|
73
|
-
address = T.let(api_url(api_path), T.nilable(String))
|
|
74
|
-
# filled only on :multipage
|
|
75
|
-
parsed_responses = []
|
|
76
|
-
|
|
77
|
-
loop do
|
|
78
|
-
response = send_http_request(T.must(address), params:, data:, http_method:)
|
|
79
|
-
|
|
80
|
-
if response_body = response.body
|
|
81
|
-
parsed_response = JSON.parse(response_body)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
if error?(response)
|
|
85
|
-
formatted_error = decode_and_format_error(T.cast(parsed_response, T::Hash[String, T.untyped]))
|
|
86
|
-
raise(formatted_error)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
return parsed_response if !multipage
|
|
90
|
-
|
|
91
|
-
parsed_responses.concat(T.cast(parsed_response, T::Array[T::Hash[String, T.untyped]]))
|
|
92
|
-
|
|
93
|
-
address = link_next_page(response.to_hash)
|
|
94
|
-
|
|
95
|
-
return parsed_responses if address.nil?
|
|
96
|
-
|
|
97
|
-
# Gitlab's next link address already includes all the params, so we remove
|
|
98
|
-
# the passed ones (if there's any).
|
|
99
|
-
params = nil
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
sig {
|
|
106
|
-
params(
|
|
107
|
-
api_path: String
|
|
108
|
-
).returns(String)
|
|
109
|
-
}
|
|
110
|
-
def api_url(api_path)
|
|
111
|
-
"#{API_BASE_URL}/#{api_path}"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
sig {
|
|
115
|
-
params(
|
|
116
|
-
address: String,
|
|
117
|
-
params: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
118
|
-
data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
|
|
119
|
-
http_method: T.nilable(Symbol)
|
|
120
|
-
).returns(Net::HTTPResponse)
|
|
121
|
-
}
|
|
122
|
-
def send_http_request(address, params: nil, data: nil, http_method: nil)
|
|
123
|
-
uri = encode_uri(address, params)
|
|
124
|
-
http_class = find_http_class(http_method, data)
|
|
125
|
-
|
|
126
|
-
Net::HTTP.start(uri.host, use_ssl: true) do |http|
|
|
127
|
-
request = http_class.new(uri)
|
|
128
|
-
|
|
129
|
-
request["Private-Token"] = @api_token
|
|
130
|
-
request.body = URI.encode_www_form(data) if data
|
|
131
|
-
|
|
132
|
-
http.request(request)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
sig {
|
|
137
|
-
params(
|
|
138
|
-
address: String,
|
|
139
|
-
params: T.nilable(T::Hash[Symbol, T.untyped])
|
|
140
|
-
).returns(URI::Generic)
|
|
141
|
-
}
|
|
142
|
-
def encode_uri(address, params)
|
|
143
|
-
address += "?" + URI.encode_www_form(params) if params
|
|
144
|
-
|
|
145
|
-
URI(address)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
sig {
|
|
149
|
-
params(
|
|
150
|
-
response: Net::HTTPResponse
|
|
151
|
-
).returns(T::Boolean)
|
|
152
|
-
}
|
|
153
|
-
def error?(response)
|
|
154
|
-
!response.code.start_with?("2")
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
sig {
|
|
158
|
-
params(
|
|
159
|
-
parsed_response: T::Hash[String, T.untyped]
|
|
160
|
-
).returns(String)
|
|
161
|
-
}
|
|
162
|
-
def decode_and_format_error(parsed_response)
|
|
163
|
-
if parsed_response.key?("error")
|
|
164
|
-
parsed_response.fetch("error")
|
|
165
|
-
elsif parsed_response.key?("message")
|
|
166
|
-
parsed_response.fetch("message")
|
|
167
|
-
else
|
|
168
|
-
"Unrecognized response: #{parsed_response}"
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
sig {
|
|
173
|
-
params(
|
|
174
|
-
response_headers: T::Hash[String, T.untyped]
|
|
175
|
-
).returns(T.nilable(String))
|
|
176
|
-
}
|
|
177
|
-
def link_next_page(response_headers)
|
|
178
|
-
# An array (or nil) is returned.
|
|
179
|
-
link_header = Array(response_headers["link"])
|
|
180
|
-
|
|
181
|
-
return nil if link_header.empty?
|
|
182
|
-
|
|
183
|
-
link_header[0][/<(\S+)>; rel="next"/, 1]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
sig {
|
|
187
|
-
params(
|
|
188
|
-
http_method: T.nilable(Symbol),
|
|
189
|
-
data: T.nilable(Object)
|
|
190
|
-
).returns(T.class_of(Net::HTTPRequest))
|
|
191
|
-
}
|
|
192
|
-
def find_http_class(http_method, data)
|
|
193
|
-
http_method ||= data ? :post : :get
|
|
194
|
-
|
|
195
|
-
case http_method
|
|
196
|
-
when :get
|
|
197
|
-
Net::HTTP::Get
|
|
198
|
-
when :delete
|
|
199
|
-
Net::HTTP::Delete
|
|
200
|
-
when :patch
|
|
201
|
-
Net::HTTP::Patch
|
|
202
|
-
when :post
|
|
203
|
-
Net::HTTP::Post
|
|
204
|
-
when :put
|
|
205
|
-
Net::HTTP::Put
|
|
206
|
-
else
|
|
207
|
-
raise "Unsupported HTTP method: #{http_method.inspect}"
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|