geet 0.26.0 → 0.27.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/Gemfile +1 -0
- data/geet.gemspec +1 -1
- data/lib/geet/git/repository.rb +2 -2
- data/lib/geet/github/abstract_issue.rb +38 -12
- data/lib/geet/github/api_interface.rb +86 -16
- data/lib/geet/github/branch.rb +5 -1
- data/lib/geet/github/gist.rb +26 -2
- data/lib/geet/github/issue.rb +31 -5
- data/lib/geet/github/label.rb +32 -8
- data/lib/geet/github/milestone.rb +56 -11
- data/lib/geet/github/pr.rb +105 -21
- data/lib/geet/github/remote_repository.rb +19 -3
- data/lib/geet/github/user.rb +47 -11
- data/lib/geet/gitlab/api_interface.rb +85 -22
- data/lib/geet/gitlab/issue.rb +32 -5
- data/lib/geet/gitlab/label.rb +37 -8
- data/lib/geet/gitlab/milestone.rb +41 -6
- data/lib/geet/gitlab/pr.rb +60 -8
- data/lib/geet/gitlab/user.rb +27 -5
- data/lib/geet/helpers/services_workflow_helper.rb +1 -1
- data/lib/geet/services/comment_pr.rb +1 -1
- data/lib/geet/services/create_gist.rb +1 -1
- data/lib/geet/services/create_issue.rb +70 -8
- data/lib/geet/services/create_pr.rb +14 -5
- data/lib/geet/services/list_issues.rb +20 -1
- data/lib/geet/services/merge_pr.rb +1 -1
- data/lib/geet/services/open_pr.rb +16 -1
- data/lib/geet/version.rb +1 -1
- data/spec/integration/create_issue_spec.rb +1 -2
- data/spec/integration/create_pr_spec.rb +10 -10
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/github/pr_spec.rb +165 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 930e360b5ecee4ddf7ef77018c0e1288dc5dbcd0fea306cc99ebc862954989bd
|
|
4
|
+
data.tar.gz: e3a3d4d6f3cfacffe95c1701677e4a95f8ba3825dbbf03b9f11dfa6e2e8987cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d2be595720c7e1288d256931cf4c27e91b5cdfe76641a9a2c87b73060567f363cfbb427dfaea7a2a3f492770710755f3f9f11b9af531f688671f5356f67b0d07
|
|
7
|
+
data.tar.gz: 4c6d651f84651a75434260dc6d743f06204130d65245d6f27ea8a72831789b3e023d7b8f74138e64fccf2a944f991bef5b8b475703bdcfd29a6170ed07314b80
|
data/Gemfile
CHANGED
data/geet.gemspec
CHANGED
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
|
9
9
|
s.required_ruby_version = '>= 3.2.0'
|
|
10
10
|
s.authors = ['Saverio Miroddi']
|
|
11
|
-
s.date = '2026-01-
|
|
11
|
+
s.date = '2026-01-15'
|
|
12
12
|
s.email = ['saverio.pub2@gmail.com']
|
|
13
13
|
s.homepage = 'https://github.com/saveriomiroddi/geet'
|
|
14
14
|
s.summary = 'Commandline interface for performing SCM host operations, eg. create a PR on GitHub'
|
data/lib/geet/git/repository.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Geet
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def issues(assignee: nil, milestone: nil)
|
|
56
|
-
attempt_provider_call(:Issue, :list, api_interface, assignee
|
|
56
|
+
attempt_provider_call(:Issue, :list, api_interface, assignee:, milestone:)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def create_milestone(title)
|
|
@@ -76,7 +76,7 @@ module Geet
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def prs(owner: nil, head: nil, milestone: nil)
|
|
79
|
-
attempt_provider_call(:PR, :list, api_interface, owner
|
|
79
|
+
attempt_provider_call(:PR, :list, api_interface, owner:, head:, milestone:)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# Returns the RemoteRepository instance.
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Github
|
|
5
6
|
# For clarity, in this class we keep only the identical logic between the subclasses, but
|
|
6
7
|
# other methods could be moved in here at some complexity cost.
|
|
7
8
|
class AbstractIssue
|
|
8
|
-
|
|
9
|
+
extend T::Sig
|
|
9
10
|
|
|
11
|
+
sig { returns(Integer) }
|
|
12
|
+
attr_reader :number
|
|
13
|
+
|
|
14
|
+
sig { returns(String) }
|
|
15
|
+
attr_reader :title
|
|
16
|
+
|
|
17
|
+
sig { returns(String) }
|
|
18
|
+
attr_reader :link
|
|
19
|
+
|
|
20
|
+
sig {
|
|
21
|
+
overridable.params(
|
|
22
|
+
number: Integer,
|
|
23
|
+
api_interface: Geet::Github::ApiInterface,
|
|
24
|
+
title: String,
|
|
25
|
+
link: String
|
|
26
|
+
).void
|
|
27
|
+
}
|
|
10
28
|
def initialize(number, api_interface, title, link)
|
|
11
29
|
@number = number
|
|
12
30
|
@api_interface = api_interface
|
|
@@ -19,6 +37,16 @@ module Geet
|
|
|
19
37
|
# This works both for Issues and PRs, however, when the `/pulls` API (path) is used, additional
|
|
20
38
|
# information is provided (e.g. `head`).
|
|
21
39
|
#
|
|
40
|
+
sig {
|
|
41
|
+
overridable.params(
|
|
42
|
+
api_interface: Geet::Github::ApiInterface,
|
|
43
|
+
milestone: T.nilable(Geet::Github::Milestone),
|
|
44
|
+
assignee: T.nilable(Geet::Github::User),
|
|
45
|
+
type_filter:
|
|
46
|
+
T.nilable(
|
|
47
|
+
T.proc.params(issue_data: T::Hash[String, T.untyped]).returns(T::Boolean)
|
|
48
|
+
)
|
|
49
|
+
).returns(T::Array[Geet::Github::AbstractIssue]) }
|
|
22
50
|
def self.list(api_interface, milestone: nil, assignee: nil, &type_filter)
|
|
23
51
|
api_path = 'issues'
|
|
24
52
|
|
|
@@ -26,12 +54,12 @@ module Geet
|
|
|
26
54
|
request_params[:milestone] = milestone.number if milestone
|
|
27
55
|
request_params[:assignee] = assignee.username if assignee
|
|
28
56
|
|
|
29
|
-
response = api_interface.send_request(api_path, params: request_params, multipage: true)
|
|
57
|
+
response = T.cast(api_interface.send_request(api_path, params: request_params, multipage: true), T::Array[T::Hash[String, T.untyped]])
|
|
30
58
|
|
|
31
59
|
abstract_issues_list = response.map do |issue_data|
|
|
32
|
-
number = issue_data.fetch('number')
|
|
33
|
-
title = issue_data.fetch('title')
|
|
34
|
-
link = issue_data.fetch('html_url')
|
|
60
|
+
number = T.cast(issue_data.fetch('number'), Integer)
|
|
61
|
+
title = T.cast(issue_data.fetch('title'), String)
|
|
62
|
+
link = T.cast(issue_data.fetch('html_url'), String)
|
|
35
63
|
|
|
36
64
|
new(number, api_interface, title, link) if type_filter.nil? || type_filter.call(issue_data)
|
|
37
65
|
end
|
|
@@ -39,9 +67,7 @@ module Geet
|
|
|
39
67
|
abstract_issues_list.compact
|
|
40
68
|
end
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
# users: String, or Array of strings.
|
|
44
|
-
#
|
|
70
|
+
sig { params(users: T.any(String, T::Array[String])).void }
|
|
45
71
|
def assign_users(users)
|
|
46
72
|
api_path = "issues/#{@number}/assignees"
|
|
47
73
|
request_data = { assignees: Array(users) }
|
|
@@ -49,17 +75,16 @@ module Geet
|
|
|
49
75
|
@api_interface.send_request(api_path, data: request_data)
|
|
50
76
|
end
|
|
51
77
|
|
|
52
|
-
|
|
53
|
-
#
|
|
78
|
+
sig { params(labels: T::Array[String]).void }
|
|
54
79
|
def add_labels(labels)
|
|
55
80
|
api_path = "issues/#{@number}/labels"
|
|
56
|
-
request_data = labels
|
|
57
81
|
|
|
58
|
-
@api_interface.send_request(api_path, data:
|
|
82
|
+
@api_interface.send_request(api_path, data: labels)
|
|
59
83
|
end
|
|
60
84
|
|
|
61
85
|
# See https://developer.github.com/v3/issues/comments/#create-a-comment
|
|
62
86
|
#
|
|
87
|
+
sig { params(comment: String).void }
|
|
63
88
|
def comment(comment)
|
|
64
89
|
api_path = "issues/#{@number}/comments"
|
|
65
90
|
request_data = { body: comment }
|
|
@@ -69,6 +94,7 @@ module Geet
|
|
|
69
94
|
|
|
70
95
|
# See https://developer.github.com/v3/issues/#edit-an-issue
|
|
71
96
|
#
|
|
97
|
+
sig { params(milestone: Integer).void }
|
|
72
98
|
def edit(milestone:)
|
|
73
99
|
request_data = { milestone: milestone }
|
|
74
100
|
api_path = "issues/#{@number}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
require 'uri'
|
|
4
5
|
require 'net/http'
|
|
@@ -7,21 +8,32 @@ require 'json'
|
|
|
7
8
|
module Geet
|
|
8
9
|
module Github
|
|
9
10
|
class ApiInterface
|
|
10
|
-
|
|
11
|
-
API_BASE_URL = 'https://api.github.com'
|
|
12
|
-
GRAPHQL_API_URL = 'https://api.github.com/graphql'
|
|
11
|
+
extend T::Sig
|
|
13
12
|
|
|
13
|
+
API_AUTH_USER = T.let('', String) # We don't need the login, as the API key uniquely identifies the user
|
|
14
|
+
API_BASE_URL = T.let('https://api.github.com', String)
|
|
15
|
+
GRAPHQL_API_URL = T.let('https://api.github.com/graphql', String)
|
|
16
|
+
|
|
17
|
+
sig { returns(T.nilable(String)) }
|
|
14
18
|
attr_reader :repository_path
|
|
15
19
|
|
|
16
20
|
# repo_path: optional for operations that don't require a repository, eg. gist creation.
|
|
17
21
|
# upstream: boolean; makes sense only when :repo_path is set.
|
|
18
22
|
#
|
|
23
|
+
sig {
|
|
24
|
+
params(
|
|
25
|
+
api_token: String,
|
|
26
|
+
repo_path: T.nilable(String),
|
|
27
|
+
upstream: T.nilable(T::Boolean)
|
|
28
|
+
).void
|
|
29
|
+
}
|
|
19
30
|
def initialize(api_token, repo_path: nil, upstream: nil)
|
|
20
|
-
@api_token = api_token
|
|
21
|
-
@repository_path = repo_path
|
|
22
|
-
@upstream = upstream
|
|
31
|
+
@api_token = T.let(api_token, String)
|
|
32
|
+
@repository_path = T.let(repo_path, T.nilable(String))
|
|
33
|
+
@upstream = T.let(upstream, T.nilable(T::Boolean))
|
|
23
34
|
end
|
|
24
35
|
|
|
36
|
+
sig { returns(T.nilable(T::Boolean)) }
|
|
25
37
|
def upstream?
|
|
26
38
|
@upstream
|
|
27
39
|
end
|
|
@@ -45,24 +57,33 @@ module Geet
|
|
|
45
57
|
# :get and :post are automatically inferred by the present of :data; the other
|
|
46
58
|
# cases must be specified.
|
|
47
59
|
#
|
|
60
|
+
sig {
|
|
61
|
+
params(
|
|
62
|
+
api_path: String,
|
|
63
|
+
params: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
64
|
+
data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
|
|
65
|
+
multipage: T::Boolean,
|
|
66
|
+
http_method: T.nilable(Symbol)
|
|
67
|
+
).returns(T.any(T::Hash[String, T.untyped], T::Array[T::Hash[String, T.untyped]], NilClass))
|
|
68
|
+
}
|
|
48
69
|
def send_request(api_path, params: nil, data: nil, multipage: false, http_method: nil)
|
|
49
|
-
address = api_url(api_path)
|
|
70
|
+
address = T.let(api_url(api_path), T.nilable(String))
|
|
50
71
|
# filled only on :multipage
|
|
51
|
-
parsed_responses = []
|
|
72
|
+
parsed_responses = T.let([], T::Array[T::Hash[String, T.untyped]])
|
|
52
73
|
|
|
53
74
|
loop do
|
|
54
|
-
response = send_http_request(address, params
|
|
75
|
+
response = send_http_request(T.must(address), params:, data:, http_method:)
|
|
55
76
|
|
|
56
|
-
parsed_response = JSON.parse(response.body) if response.body
|
|
77
|
+
parsed_response = T.unsafe(JSON.parse(T.must(response.body))) if response.body
|
|
57
78
|
|
|
58
79
|
if error?(response)
|
|
59
|
-
error_message = decode_and_format_error(parsed_response)
|
|
80
|
+
error_message = decode_and_format_error(T.cast(parsed_response, T::Hash[String, T.untyped]))
|
|
60
81
|
raise Geet::Shared::HttpError.new(error_message, response.code)
|
|
61
82
|
end
|
|
62
83
|
|
|
63
84
|
return parsed_response if !multipage
|
|
64
85
|
|
|
65
|
-
parsed_responses.concat(parsed_response)
|
|
86
|
+
parsed_responses.concat(T.cast(parsed_response, T::Array[T::Hash[String, T.untyped]]))
|
|
66
87
|
|
|
67
88
|
address = link_next_page(response.to_hash)
|
|
68
89
|
|
|
@@ -78,6 +99,12 @@ module Geet
|
|
|
78
99
|
# :query: GraphQL query string
|
|
79
100
|
# :variables: (Hash) GraphQL variables
|
|
80
101
|
#
|
|
102
|
+
sig {
|
|
103
|
+
params(
|
|
104
|
+
query: String,
|
|
105
|
+
variables: T::Hash[Symbol, T.untyped]
|
|
106
|
+
).returns(T::Hash[String, T.untyped])
|
|
107
|
+
}
|
|
81
108
|
def send_graphql_request(query, variables: {})
|
|
82
109
|
uri = URI(GRAPHQL_API_URL)
|
|
83
110
|
|
|
@@ -90,24 +117,32 @@ module Geet
|
|
|
90
117
|
|
|
91
118
|
response = http.request(request)
|
|
92
119
|
|
|
93
|
-
parsed_response =
|
|
120
|
+
parsed_response = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
|
121
|
+
if response.body
|
|
122
|
+
parsed_response = T.cast(T.unsafe(JSON.parse(response.body)), T::Hash[String, T.untyped])
|
|
123
|
+
end
|
|
94
124
|
|
|
95
125
|
if error?(response)
|
|
96
|
-
error_message = decode_and_format_error(parsed_response)
|
|
126
|
+
error_message = decode_and_format_error(T.must(parsed_response))
|
|
97
127
|
raise Geet::Shared::HttpError.new(error_message, response.code)
|
|
98
128
|
end
|
|
99
129
|
|
|
100
130
|
if parsed_response&.key?('errors')
|
|
101
|
-
error_messages = parsed_response['errors'].map { |err| err['message'] }.join(', ')
|
|
131
|
+
error_messages = T.cast(parsed_response['errors'], T::Array[T::Hash[String, T.untyped]]).map { |err| T.cast(err['message'], String) }.join(', ')
|
|
102
132
|
raise Geet::Shared::HttpError.new("GraphQL errors: #{error_messages}", response.code)
|
|
103
133
|
end
|
|
104
134
|
|
|
105
|
-
parsed_response
|
|
135
|
+
T.cast(T.must(parsed_response).fetch('data'), T::Hash[String, T.untyped])
|
|
106
136
|
end
|
|
107
137
|
end
|
|
108
138
|
|
|
109
139
|
private
|
|
110
140
|
|
|
141
|
+
sig {
|
|
142
|
+
params(
|
|
143
|
+
api_path: String
|
|
144
|
+
).returns(String)
|
|
145
|
+
}
|
|
111
146
|
def api_url(api_path)
|
|
112
147
|
url = API_BASE_URL
|
|
113
148
|
|
|
@@ -119,6 +154,14 @@ module Geet
|
|
|
119
154
|
url + api_path
|
|
120
155
|
end
|
|
121
156
|
|
|
157
|
+
sig {
|
|
158
|
+
params(
|
|
159
|
+
address: String,
|
|
160
|
+
params: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
161
|
+
data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped])),
|
|
162
|
+
http_method: T.nilable(Symbol)
|
|
163
|
+
).returns(Net::HTTPResponse)
|
|
164
|
+
}
|
|
122
165
|
def send_http_request(address, params: nil, data: nil, http_method: nil)
|
|
123
166
|
uri = encode_uri(address, params)
|
|
124
167
|
http_class = find_http_class(http_method, data)
|
|
@@ -134,16 +177,32 @@ module Geet
|
|
|
134
177
|
end
|
|
135
178
|
end
|
|
136
179
|
|
|
180
|
+
sig {
|
|
181
|
+
params(
|
|
182
|
+
address: String,
|
|
183
|
+
params: T.nilable(T::Hash[Symbol, T.untyped])
|
|
184
|
+
).returns(URI::Generic)
|
|
185
|
+
}
|
|
137
186
|
def encode_uri(address, params)
|
|
138
187
|
address += '?' + URI.encode_www_form(params) if params
|
|
139
188
|
|
|
140
189
|
URI(address)
|
|
141
190
|
end
|
|
142
191
|
|
|
192
|
+
sig {
|
|
193
|
+
params(
|
|
194
|
+
response: Net::HTTPResponse
|
|
195
|
+
).returns(T::Boolean)
|
|
196
|
+
}
|
|
143
197
|
def error?(response)
|
|
144
198
|
!response.code.start_with?('2')
|
|
145
199
|
end
|
|
146
200
|
|
|
201
|
+
sig {
|
|
202
|
+
params(
|
|
203
|
+
parsed_response: T::Hash[String, T.untyped]
|
|
204
|
+
).returns(String)
|
|
205
|
+
}
|
|
147
206
|
def decode_and_format_error(parsed_response)
|
|
148
207
|
message = parsed_response['message']
|
|
149
208
|
|
|
@@ -166,6 +225,11 @@ module Geet
|
|
|
166
225
|
message
|
|
167
226
|
end
|
|
168
227
|
|
|
228
|
+
sig {
|
|
229
|
+
params(
|
|
230
|
+
response_headers: T::Hash[String, T.untyped]
|
|
231
|
+
).returns(T.nilable(String))
|
|
232
|
+
}
|
|
169
233
|
def link_next_page(response_headers)
|
|
170
234
|
# An array (or nil) is returned.
|
|
171
235
|
link_header = Array(response_headers['link'])
|
|
@@ -175,6 +239,12 @@ module Geet
|
|
|
175
239
|
link_header[0][/<(\S+)>; rel="next"/, 1]
|
|
176
240
|
end
|
|
177
241
|
|
|
242
|
+
sig {
|
|
243
|
+
params(
|
|
244
|
+
http_method: T.nilable(Symbol),
|
|
245
|
+
data: T.nilable(T.any(T::Hash[Symbol, T.untyped], T::Array[T.untyped]))
|
|
246
|
+
).returns(T.class_of(Net::HTTPRequest))
|
|
247
|
+
}
|
|
178
248
|
def find_http_class(http_method, data)
|
|
179
249
|
http_method ||= data ? :post : :get
|
|
180
250
|
|
data/lib/geet/github/branch.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Github
|
|
5
6
|
class Branch
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
6
9
|
# See https://developer.github.com/v3/git/refs/#delete-a-reference
|
|
7
10
|
#
|
|
8
|
-
|
|
11
|
+
sig { params(name: String, api_interface: Geet::Github::ApiInterface).void }
|
|
12
|
+
def self.delete(name, api_interface)
|
|
9
13
|
api_path = "git/refs/heads/#{name}"
|
|
10
14
|
|
|
11
15
|
api_interface.send_request(api_path, http_method: :delete)
|
data/lib/geet/github/gist.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
require_relative 'abstract_issue'
|
|
4
5
|
require_relative '../github/gist'
|
|
@@ -6,29 +7,52 @@ require_relative '../github/gist'
|
|
|
6
7
|
module Geet
|
|
7
8
|
module Github
|
|
8
9
|
class Gist
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig {
|
|
13
|
+
params(
|
|
14
|
+
filename: String,
|
|
15
|
+
content: String,
|
|
16
|
+
api_interface: Geet::Github::ApiInterface,
|
|
17
|
+
description: T.nilable(String),
|
|
18
|
+
publik: T::Boolean
|
|
19
|
+
).returns(Geet::Github::Gist)
|
|
20
|
+
}
|
|
9
21
|
def self.create(filename, content, api_interface, description: nil, publik: false)
|
|
10
22
|
api_path = '/gists'
|
|
11
23
|
|
|
12
24
|
request_data = prepare_request_data(filename, content, description, publik)
|
|
13
25
|
|
|
14
|
-
response = api_interface.send_request(api_path, data: request_data)
|
|
26
|
+
response = T.cast(api_interface.send_request(api_path, data: T.unsafe(request_data)), T::Hash[String, T.untyped])
|
|
15
27
|
|
|
16
|
-
id = response.fetch('id')
|
|
28
|
+
id = T.cast(response.fetch('id'), String)
|
|
17
29
|
|
|
18
30
|
new(id)
|
|
19
31
|
end
|
|
20
32
|
|
|
33
|
+
sig { params(id: String).void }
|
|
21
34
|
def initialize(id)
|
|
22
35
|
@id = id
|
|
23
36
|
end
|
|
24
37
|
|
|
38
|
+
sig { returns(String) }
|
|
25
39
|
def link
|
|
26
40
|
"https://gist.github.com/#{@id}"
|
|
27
41
|
end
|
|
28
42
|
|
|
29
43
|
class << self
|
|
44
|
+
extend T::Sig
|
|
45
|
+
|
|
30
46
|
private
|
|
31
47
|
|
|
48
|
+
sig {
|
|
49
|
+
params(
|
|
50
|
+
filename: String,
|
|
51
|
+
content: String,
|
|
52
|
+
description: T.nilable(String),
|
|
53
|
+
publik: T::Boolean
|
|
54
|
+
).returns(T::Hash[String, T.untyped])
|
|
55
|
+
}
|
|
32
56
|
def prepare_request_data(filename, content, description, publik)
|
|
33
57
|
request_data = {
|
|
34
58
|
'public' => publik,
|
data/lib/geet/github/issue.rb
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Github
|
|
5
6
|
class Issue < Geet::Github::AbstractIssue
|
|
6
|
-
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
9
|
+
sig {
|
|
10
|
+
params(
|
|
11
|
+
title: String,
|
|
12
|
+
description: String,
|
|
13
|
+
api_interface: Geet::Github::ApiInterface,
|
|
14
|
+
).returns(Geet::Github::Issue)
|
|
15
|
+
}
|
|
16
|
+
def self.create(title, description, api_interface)
|
|
7
17
|
api_path = 'issues'
|
|
8
|
-
request_data = { title
|
|
18
|
+
request_data = { title:, body: description }
|
|
9
19
|
|
|
10
|
-
response =
|
|
20
|
+
response = T.cast(
|
|
21
|
+
api_interface.send_request(api_path, data: request_data),
|
|
22
|
+
T::Hash[String, T.untyped]
|
|
23
|
+
)
|
|
11
24
|
|
|
12
|
-
issue_number
|
|
25
|
+
issue_number = T.cast(response.fetch('number'), Integer)
|
|
26
|
+
title = T.cast(response.fetch('title'), String)
|
|
27
|
+
link = T.cast(response.fetch('html_url'), String)
|
|
13
28
|
|
|
14
29
|
new(issue_number, api_interface, title, link)
|
|
15
30
|
end
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
sig {
|
|
33
|
+
override.params(
|
|
34
|
+
api_interface: Geet::Github::ApiInterface,
|
|
35
|
+
assignee: T.nilable(Geet::Github::User),
|
|
36
|
+
milestone: T.nilable(Geet::Github::Milestone),
|
|
37
|
+
type_filter:
|
|
38
|
+
T.nilable(
|
|
39
|
+
T.proc.params(issue_data: T::Hash[String, T.untyped]).returns(T::Boolean)
|
|
40
|
+
)
|
|
41
|
+
).returns(T::Array[Geet::Github::AbstractIssue])
|
|
42
|
+
}
|
|
43
|
+
def self.list(api_interface, assignee: nil, milestone: nil, &type_filter)
|
|
18
44
|
super do |issue_data|
|
|
19
45
|
!issue_data.key?('pull_request')
|
|
20
46
|
end
|
data/lib/geet/github/label.rb
CHANGED
|
@@ -1,32 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Github
|
|
5
6
|
class Label
|
|
6
|
-
|
|
7
|
+
extend T::Sig
|
|
7
8
|
|
|
9
|
+
sig { returns(String) }
|
|
10
|
+
attr_reader :name
|
|
11
|
+
|
|
12
|
+
sig { returns(String) }
|
|
13
|
+
attr_reader :color
|
|
14
|
+
|
|
15
|
+
sig {
|
|
16
|
+
params(
|
|
17
|
+
name: String,
|
|
18
|
+
color: String
|
|
19
|
+
).void
|
|
20
|
+
}
|
|
8
21
|
def initialize(name, color)
|
|
9
22
|
@name = name
|
|
10
23
|
@color = color
|
|
11
24
|
end
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
sig {
|
|
27
|
+
params(
|
|
28
|
+
api_interface: Geet::Github::ApiInterface
|
|
29
|
+
).returns(T::Array[Geet::Github::Label])
|
|
30
|
+
}
|
|
31
|
+
def self.list(api_interface)
|
|
15
32
|
api_path = 'labels'
|
|
16
|
-
response = api_interface.send_request(api_path, multipage: true)
|
|
33
|
+
response = T.cast(api_interface.send_request(api_path, multipage: true), T::Array[T::Hash[String, T.untyped]])
|
|
17
34
|
|
|
18
35
|
response.map do |label_entry|
|
|
19
|
-
name = label_entry.fetch('name')
|
|
20
|
-
color = label_entry.fetch('color')
|
|
36
|
+
name = T.cast(label_entry.fetch('name'), String)
|
|
37
|
+
color = T.cast(label_entry.fetch('color'), String)
|
|
21
38
|
|
|
22
39
|
new(name, color)
|
|
23
40
|
end
|
|
24
41
|
end
|
|
25
42
|
|
|
26
43
|
# See https://developer.github.com/v3/issues/labels/#create-a-label
|
|
27
|
-
|
|
44
|
+
sig {
|
|
45
|
+
params(
|
|
46
|
+
name: String,
|
|
47
|
+
color: String,
|
|
48
|
+
api_interface: Geet::Github::ApiInterface
|
|
49
|
+
).returns(Geet::Github::Label)
|
|
50
|
+
}
|
|
51
|
+
def self.create(name, color, api_interface)
|
|
28
52
|
api_path = 'labels'
|
|
29
|
-
request_data = { name
|
|
53
|
+
request_data = { name:, color: }
|
|
30
54
|
|
|
31
55
|
api_interface.send_request(api_path, data: request_data)
|
|
32
56
|
|
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Github
|
|
5
6
|
class Milestone
|
|
6
|
-
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
9
|
+
sig { returns(Integer) }
|
|
10
|
+
attr_reader :number
|
|
11
|
+
|
|
12
|
+
sig { returns(String) }
|
|
13
|
+
attr_reader :title
|
|
14
|
+
|
|
15
|
+
sig { returns(T.nilable(Date)) }
|
|
16
|
+
attr_reader :due_on
|
|
7
17
|
|
|
8
18
|
STATE_CLOSED = 'closed'
|
|
9
19
|
|
|
10
20
|
class << self
|
|
21
|
+
extend T::Sig
|
|
22
|
+
|
|
11
23
|
private
|
|
12
24
|
|
|
13
25
|
include Helpers::JsonHelper
|
|
14
26
|
end
|
|
15
27
|
|
|
28
|
+
sig {
|
|
29
|
+
params(
|
|
30
|
+
number: Integer,
|
|
31
|
+
title: String,
|
|
32
|
+
due_on: T.nilable(Date),
|
|
33
|
+
api_interface: Geet::Github::ApiInterface
|
|
34
|
+
).void
|
|
35
|
+
}
|
|
16
36
|
def initialize(number, title, due_on, api_interface)
|
|
17
37
|
@number = number
|
|
18
38
|
@title = title
|
|
@@ -22,14 +42,23 @@ module Geet
|
|
|
22
42
|
end
|
|
23
43
|
|
|
24
44
|
# See https://developer.github.com/v3/issues/milestones/#create-a-milestone
|
|
25
|
-
|
|
45
|
+
sig {
|
|
46
|
+
params(
|
|
47
|
+
title: String,
|
|
48
|
+
api_interface: Geet::Github::ApiInterface
|
|
49
|
+
).returns(Geet::Github::Milestone)
|
|
50
|
+
}
|
|
51
|
+
def self.create(title, api_interface)
|
|
26
52
|
api_path = 'milestones'
|
|
27
53
|
request_data = { title: title }
|
|
28
54
|
|
|
29
|
-
response =
|
|
55
|
+
response = T.cast(
|
|
56
|
+
api_interface.send_request(api_path, data: request_data),
|
|
57
|
+
T::Hash[String, T.untyped]
|
|
58
|
+
)
|
|
30
59
|
|
|
31
|
-
number = response.fetch('number')
|
|
32
|
-
title = response.fetch('title')
|
|
60
|
+
number = T.cast(response.fetch('number'), Integer)
|
|
61
|
+
title = T.cast(response.fetch('title'), String)
|
|
33
62
|
due_on = nil
|
|
34
63
|
|
|
35
64
|
new(number, title, due_on, api_interface)
|
|
@@ -37,15 +66,25 @@ module Geet
|
|
|
37
66
|
|
|
38
67
|
# See https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
|
|
39
68
|
#
|
|
40
|
-
|
|
69
|
+
sig {
|
|
70
|
+
params(
|
|
71
|
+
api_interface: Geet::Github::ApiInterface
|
|
72
|
+
).returns(T::Array[Geet::Github::Milestone])
|
|
73
|
+
}
|
|
74
|
+
def self.list(api_interface)
|
|
41
75
|
api_path = 'milestones'
|
|
42
76
|
|
|
43
|
-
response =
|
|
77
|
+
response = T.cast(
|
|
78
|
+
api_interface.send_request(api_path, multipage: true),
|
|
79
|
+
T::Array[T::Hash[String, T.untyped]]
|
|
80
|
+
)
|
|
44
81
|
|
|
45
82
|
response.map do |milestone_data|
|
|
46
|
-
number = milestone_data.fetch('number')
|
|
47
|
-
title = milestone_data.fetch('title')
|
|
48
|
-
due_on = parse_iso_8601_timestamp(
|
|
83
|
+
number = T.cast(milestone_data.fetch('number'), Integer)
|
|
84
|
+
title = T.cast(milestone_data.fetch('title'), String)
|
|
85
|
+
due_on = parse_iso_8601_timestamp(
|
|
86
|
+
T.cast(milestone_data.fetch('due_on'), T.nilable(String))
|
|
87
|
+
)
|
|
49
88
|
|
|
50
89
|
new(number, title, due_on, api_interface)
|
|
51
90
|
end
|
|
@@ -55,7 +94,13 @@ module Geet
|
|
|
55
94
|
#
|
|
56
95
|
# This is a convenience method; the underlying operation is a generic update.
|
|
57
96
|
#
|
|
58
|
-
|
|
97
|
+
sig {
|
|
98
|
+
params(
|
|
99
|
+
number: Integer,
|
|
100
|
+
api_interface: Geet::Github::ApiInterface
|
|
101
|
+
).void
|
|
102
|
+
}
|
|
103
|
+
def self.close(number, api_interface)
|
|
59
104
|
api_path = "milestones/#{number}"
|
|
60
105
|
request_data = { state: STATE_CLOSED }
|
|
61
106
|
|