nya_pr 0.2.0 → 0.3.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/README.md +43 -0
- data/lib/nya_pr/cli/command.rb +18 -10
- data/lib/nya_pr/config/global.rb +135 -0
- data/lib/nya_pr/config/loader.rb +71 -0
- data/lib/nya_pr/github/client.rb +14 -0
- data/lib/nya_pr/github/connection.rb +38 -0
- data/lib/nya_pr/github/request.rb +99 -0
- data/lib/nya_pr/progress.rb +16 -2
- data/lib/nya_pr/pull_request/config.rb +4 -2
- data/lib/nya_pr/pull_request/finder.rb +1 -1
- data/lib/nya_pr/pull_request/paths.rb +41 -0
- data/lib/nya_pr/repository/config.rb +4 -2
- data/lib/nya_pr/repository/filter.rb +3 -1
- data/lib/nya_pr/repository/finder.rb +1 -1
- data/lib/nya_pr/repository/paths.rb +27 -0
- data/lib/nya_pr/runner.rb +37 -27
- data/lib/nya_pr/version.rb +1 -1
- metadata +22 -6
- data/lib/nya_pr/client.rb +0 -12
- data/lib/nya_pr/config.rb +0 -71
- data/lib/nya_pr/connection.rb +0 -34
- data/lib/nya_pr/paths.rb +0 -60
- data/lib/nya_pr/request.rb +0 -97
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fcee9fc7fd9c910f937d04b717a08558f1400f90029aca67ae4676a90db85031
|
|
4
|
+
data.tar.gz: 97e1630078e1fb9ebb0e01aafa61c2249dd1859cb48e611823e6105f7b422b5a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bde310b1529f25c20adf0e58031d028448f75b19a7d5e601fbb18432f768fb32ea22d1f7f869703977adf6ee22f40d0cc6a428fb0ca655937fae265d1069238b
|
|
7
|
+
data.tar.gz: 2932d0bba96e2ee071020fc21f0128d0d48b2d6b08f16966622067884d3a7e2ec91f769be0466b5b1e7c45e7f1f1f1b6539fb0012ba74c456f29f07a1f0170c5
|
data/README.md
CHANGED
|
@@ -304,7 +304,9 @@ Logs are written to `stderr`, while actual pull request output goes to `stdout`.
|
|
|
304
304
|
--skip-drafts Ignore draft pull requests
|
|
305
305
|
--[no-]progress Show or hide progress bars
|
|
306
306
|
--log-level LEVEL Logging level
|
|
307
|
+
-w, --workers Number of repository scan workers
|
|
307
308
|
-v, --version Show NyaPr version
|
|
309
|
+
-c, --config Configuration file path
|
|
308
310
|
```
|
|
309
311
|
|
|
310
312
|
Example with most options enabled:
|
|
@@ -321,6 +323,47 @@ ruby run.rb \
|
|
|
321
323
|
--log-level debug
|
|
322
324
|
```
|
|
323
325
|
|
|
326
|
+
## YAML configuration
|
|
327
|
+
|
|
328
|
+
NyaPr can load options from a YAML configuration file. By default, it looks for `.nya-pr.yml` in the current directory.
|
|
329
|
+
|
|
330
|
+
```yaml
|
|
331
|
+
username: your-github-username
|
|
332
|
+
|
|
333
|
+
owners:
|
|
334
|
+
- organization-one
|
|
335
|
+
- organization-two
|
|
336
|
+
|
|
337
|
+
limit: 100
|
|
338
|
+
skip_archived: true
|
|
339
|
+
skip_drafts: true
|
|
340
|
+
progress: true
|
|
341
|
+
log_level: info
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
You can also provide a custom configuration file:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
nya-pr --config path/to/config.yml
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Command-line options override values from the YAML file. Any options not defined in either place use NyaPr's built-in defaults.
|
|
351
|
+
|
|
352
|
+
For example:
|
|
353
|
+
|
|
354
|
+
```yaml
|
|
355
|
+
limit: 200
|
|
356
|
+
progress: true
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
nya-pr --limit 50 --no-progress
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
In this case, NyaPr uses `limit: 50` and disables progress output.
|
|
364
|
+
|
|
365
|
+
Unknown configuration options and invalid values are reported as errors.
|
|
366
|
+
|
|
324
367
|
## How repository discovery works
|
|
325
368
|
|
|
326
369
|
For your own GitHub username, NyaPr collects repositories owned by your authenticated account.
|
data/lib/nya_pr/cli/command.rb
CHANGED
|
@@ -5,6 +5,10 @@ module NyaPr
|
|
|
5
5
|
class Command < Dry::CLI::Command
|
|
6
6
|
desc 'Collect open GitHub pull requests'
|
|
7
7
|
|
|
8
|
+
option :config,
|
|
9
|
+
aliases: ['-c'],
|
|
10
|
+
desc: 'Configuration file path'
|
|
11
|
+
|
|
8
12
|
option :username,
|
|
9
13
|
aliases: ['-u', '--user'],
|
|
10
14
|
desc: 'GitHub username'
|
|
@@ -12,7 +16,6 @@ module NyaPr
|
|
|
12
16
|
option :owners,
|
|
13
17
|
aliases: ['-o'],
|
|
14
18
|
type: :array,
|
|
15
|
-
default: [],
|
|
16
19
|
desc: 'Repository owners'
|
|
17
20
|
|
|
18
21
|
option :token,
|
|
@@ -26,47 +29,52 @@ module NyaPr
|
|
|
26
29
|
|
|
27
30
|
option :limit,
|
|
28
31
|
aliases: ['-l'],
|
|
29
|
-
default: PullRequest::Config::DEFAULT_LIMIT,
|
|
30
32
|
desc: 'Maximum number of pull requests'
|
|
31
33
|
|
|
32
34
|
option :refresh,
|
|
33
35
|
aliases: ['-r'],
|
|
34
36
|
type: :boolean,
|
|
35
|
-
default: false,
|
|
36
37
|
desc: 'Refresh repositories from GitHub'
|
|
37
38
|
|
|
38
39
|
option :skip_archived,
|
|
39
40
|
type: :boolean,
|
|
40
|
-
default: false,
|
|
41
41
|
desc: 'Skip archived repositories'
|
|
42
42
|
|
|
43
43
|
option :skip_drafts,
|
|
44
44
|
type: :boolean,
|
|
45
|
-
default: false,
|
|
46
45
|
desc: 'Ignore draft pull requests'
|
|
47
46
|
|
|
48
47
|
option :progress,
|
|
49
48
|
type: :boolean,
|
|
50
|
-
default: true,
|
|
51
49
|
desc: 'Show progress bars'
|
|
52
50
|
|
|
53
51
|
option :log_level,
|
|
54
|
-
values: Config::LOG_LEVELS.keys,
|
|
55
|
-
default: 'info',
|
|
52
|
+
values: Config::Global::LOG_LEVELS.keys,
|
|
56
53
|
desc: 'Log level'
|
|
57
54
|
|
|
55
|
+
option :workers,
|
|
56
|
+
aliases: ['-w'],
|
|
57
|
+
desc: 'Number of repository scan workers'
|
|
58
|
+
|
|
58
59
|
option :version,
|
|
59
60
|
aliases: ['-v'],
|
|
60
61
|
type: :flag,
|
|
61
62
|
desc: 'Show version'
|
|
62
63
|
|
|
63
|
-
def call(version: false, **options)
|
|
64
|
+
def call(version: false, config: nil, **options)
|
|
64
65
|
if version
|
|
65
66
|
puts NyaPr::VERSION
|
|
66
67
|
return
|
|
67
68
|
end
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
file_options = Config::Loader.load(config)
|
|
71
|
+
|
|
72
|
+
Runner.new(
|
|
73
|
+
Config::Global.from_options(
|
|
74
|
+
options,
|
|
75
|
+
file_options: file_options
|
|
76
|
+
)
|
|
77
|
+
).run
|
|
70
78
|
end
|
|
71
79
|
end
|
|
72
80
|
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NyaPr
|
|
4
|
+
module Config
|
|
5
|
+
class Global < Data.define(
|
|
6
|
+
:username,
|
|
7
|
+
:owners,
|
|
8
|
+
:token,
|
|
9
|
+
:repositories_csv,
|
|
10
|
+
:pull_requests_dir,
|
|
11
|
+
:limit,
|
|
12
|
+
:refresh,
|
|
13
|
+
:skip_archived,
|
|
14
|
+
:skip_drafts,
|
|
15
|
+
:progress,
|
|
16
|
+
:log_level,
|
|
17
|
+
:workers
|
|
18
|
+
)
|
|
19
|
+
LOG_LEVELS = {
|
|
20
|
+
'debug' => Logger::DEBUG,
|
|
21
|
+
'info' => Logger::INFO,
|
|
22
|
+
'warn' => Logger::WARN,
|
|
23
|
+
'error' => Logger::ERROR,
|
|
24
|
+
'fatal' => Logger::FATAL
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
DEFAULTS = {
|
|
28
|
+
owners: [],
|
|
29
|
+
token: nil,
|
|
30
|
+
repositories_csv: nil,
|
|
31
|
+
pull_requests_dir: nil,
|
|
32
|
+
limit: PullRequest::Config::DEFAULT_LIMIT,
|
|
33
|
+
refresh: false,
|
|
34
|
+
skip_archived: false,
|
|
35
|
+
skip_drafts: false,
|
|
36
|
+
progress: true,
|
|
37
|
+
log_level: 'info',
|
|
38
|
+
workers: Repository::Config::DEFAULT_WORKERS
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
BOOLEAN_KEYS = %i[
|
|
42
|
+
refresh
|
|
43
|
+
skip_archived
|
|
44
|
+
skip_drafts
|
|
45
|
+
progress
|
|
46
|
+
].freeze
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
def from_options(options, file_options: {})
|
|
50
|
+
validate_keys!(file_options)
|
|
51
|
+
|
|
52
|
+
attributes = DEFAULTS.
|
|
53
|
+
merge(file_options).
|
|
54
|
+
merge(options.compact)
|
|
55
|
+
|
|
56
|
+
build(attributes)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def build(attributes)
|
|
62
|
+
normalized = attributes.merge(
|
|
63
|
+
username: normalize_username(attributes[:username]),
|
|
64
|
+
owners: normalize_owners(attributes[:owners]),
|
|
65
|
+
limit: normalize_positive_integer(attributes[:limit], :limit),
|
|
66
|
+
workers: normalize_positive_integer(attributes[:workers], :workers),
|
|
67
|
+
log_level: normalize_log_level(attributes[:log_level]),
|
|
68
|
+
**normalize_booleans(attributes)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
new(**normalized)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def normalize_username(value)
|
|
75
|
+
username = value.to_s.strip
|
|
76
|
+
|
|
77
|
+
validate_username!(username)
|
|
78
|
+
|
|
79
|
+
username
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def normalize_booleans(attributes)
|
|
83
|
+
BOOLEAN_KEYS.to_h do |key|
|
|
84
|
+
[key, normalize_boolean(attributes[key], key)]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate_keys!(options)
|
|
89
|
+
unknown = options.keys - members
|
|
90
|
+
|
|
91
|
+
return if unknown.empty?
|
|
92
|
+
|
|
93
|
+
raise NyaPr::Error,
|
|
94
|
+
"Unknown config option(s): #{unknown.join(', ')}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def normalize_owners(owners)
|
|
98
|
+
Array(owners).
|
|
99
|
+
flat_map { |owner| owner.to_s.split(',') }.
|
|
100
|
+
map(&:strip).
|
|
101
|
+
reject(&:empty?)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def normalize_positive_integer(value, name)
|
|
105
|
+
number = Integer(value)
|
|
106
|
+
|
|
107
|
+
return number if number.positive?
|
|
108
|
+
|
|
109
|
+
raise NyaPr::Error, "#{name} must be greater than 0"
|
|
110
|
+
rescue ArgumentError, TypeError
|
|
111
|
+
raise NyaPr::Error, "#{name} must be a positive integer"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def normalize_boolean(value, name)
|
|
115
|
+
return value if [true, false].include?(value)
|
|
116
|
+
|
|
117
|
+
raise NyaPr::Error, "#{name} must be true or false"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def normalize_log_level(value)
|
|
121
|
+
LOG_LEVELS.fetch(value.to_s) do
|
|
122
|
+
raise NyaPr::Error,
|
|
123
|
+
"Invalid log level: #{value}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def validate_username!(username)
|
|
128
|
+
return unless username.empty?
|
|
129
|
+
|
|
130
|
+
raise NyaPr::Error, 'GitHub username is required'
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module NyaPr
|
|
6
|
+
module Config
|
|
7
|
+
class Loader
|
|
8
|
+
DEFAULT_PATH = '.nya-pr.yml'
|
|
9
|
+
|
|
10
|
+
def self.load(path = nil)
|
|
11
|
+
new(path).load
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :path
|
|
15
|
+
|
|
16
|
+
def initialize(path = nil)
|
|
17
|
+
@explicit_path = !path.nil?
|
|
18
|
+
@path = Pathname.new(path || DEFAULT_PATH)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load
|
|
22
|
+
unless path.exist?
|
|
23
|
+
return {} unless explicit_path?
|
|
24
|
+
|
|
25
|
+
raise NyaPr::Error, "Config file not found: #{path}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
parse
|
|
29
|
+
rescue Psych::SyntaxError => e
|
|
30
|
+
raise NyaPr::Error,
|
|
31
|
+
"Invalid YAML in #{path}: #{e.problem}"
|
|
32
|
+
rescue Errno::EACCES, Errno::ENOENT => e
|
|
33
|
+
raise NyaPr::Error,
|
|
34
|
+
"Cannot read config file #{path}: #{e.message}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def explicit_path?
|
|
40
|
+
@explicit_path
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse
|
|
44
|
+
data = YAML.safe_load_file(
|
|
45
|
+
path.to_s,
|
|
46
|
+
aliases: false
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return {} if data.nil?
|
|
50
|
+
|
|
51
|
+
unless data.is_a?(Hash)
|
|
52
|
+
raise NyaPr::Error,
|
|
53
|
+
"Config file must contain a mapping: #{path}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
symbolize_keys(data)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def symbolize_keys(data)
|
|
60
|
+
data.to_h do |key, value|
|
|
61
|
+
unless key.is_a?(String) || key.is_a?(Symbol)
|
|
62
|
+
raise NyaPr::Error,
|
|
63
|
+
"Invalid config key: #{key.inspect}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
[key.to_sym, value]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'faraday/gzip'
|
|
5
|
+
|
|
6
|
+
module NyaPr
|
|
7
|
+
module Github
|
|
8
|
+
module Connection
|
|
9
|
+
API_URL = 'https://api.github.com'
|
|
10
|
+
API_VERSION = '2026-03-10'
|
|
11
|
+
|
|
12
|
+
def connection(client)
|
|
13
|
+
Faraday.new(connection_options(client)) do |faraday|
|
|
14
|
+
faraday.request :gzip
|
|
15
|
+
faraday.adapter Faraday.default_adapter
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def connection_options(client)
|
|
22
|
+
{
|
|
23
|
+
url: API_URL,
|
|
24
|
+
headers: {
|
|
25
|
+
accept: 'application/vnd.github+json',
|
|
26
|
+
user_agent: "nya_pr gem/#{NyaPr::VERSION}",
|
|
27
|
+
authorization: "Bearer #{client.token}",
|
|
28
|
+
'X-GitHub-Api-Version': client.options.fetch(:api_version, API_VERSION)
|
|
29
|
+
},
|
|
30
|
+
request: {
|
|
31
|
+
timeout: client.options.fetch(:timeout, 60),
|
|
32
|
+
open_timeout: client.options.fetch(:open_timeout, 10)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'oj'
|
|
4
|
+
|
|
5
|
+
module NyaPr
|
|
6
|
+
module Github
|
|
7
|
+
module Request
|
|
8
|
+
include Connection
|
|
9
|
+
|
|
10
|
+
MAX_PAGES = 1_000
|
|
11
|
+
PER_PAGE = 100
|
|
12
|
+
|
|
13
|
+
def get(path, params = {})
|
|
14
|
+
_response, body = do_get(path, params)
|
|
15
|
+
|
|
16
|
+
body
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def paginate(path, params = {})
|
|
20
|
+
params = params.merge(per_page: params.fetch(:per_page, PER_PAGE))
|
|
21
|
+
|
|
22
|
+
page = 1
|
|
23
|
+
result = []
|
|
24
|
+
|
|
25
|
+
loop do
|
|
26
|
+
raise NyaPr::Error, 'Pagination limit exceeded' if page > MAX_PAGES
|
|
27
|
+
|
|
28
|
+
NyaPr.logger.debug("Fetching page #{page} for #{path}")
|
|
29
|
+
response, body = do_get(path, params.merge(page: page))
|
|
30
|
+
|
|
31
|
+
raise NyaPr::Error, 'Expected paginated response to be an array' unless body.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
result.concat(body)
|
|
34
|
+
|
|
35
|
+
break if body.empty?
|
|
36
|
+
break unless next_page?(response)
|
|
37
|
+
|
|
38
|
+
page += 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def do_get(path, params = {})
|
|
47
|
+
prepared_path = prepare(path)
|
|
48
|
+
|
|
49
|
+
NyaPr.logger.debug("GET #{prepared_path} #{params.inspect}")
|
|
50
|
+
|
|
51
|
+
response = connection(client).get(prepared_path, params)
|
|
52
|
+
body = parse_body(response)
|
|
53
|
+
|
|
54
|
+
NyaPr.logger.debug(
|
|
55
|
+
"GET #{prepared_path} -> HTTP #{response.status}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
raise_on_error!(response, body, prepared_path)
|
|
59
|
+
|
|
60
|
+
[response, body]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def parse_body(response)
|
|
64
|
+
return nil if response.body.nil? || response.body.empty?
|
|
65
|
+
|
|
66
|
+
Oj.load(response.body)
|
|
67
|
+
rescue Oj::ParseError
|
|
68
|
+
raise NyaPr::Error, "Failed to parse response: #{response.body}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def raise_on_error!(response, body, path)
|
|
72
|
+
return if response.success?
|
|
73
|
+
|
|
74
|
+
message =
|
|
75
|
+
if body.is_a?(Hash)
|
|
76
|
+
body['message'] || body
|
|
77
|
+
else
|
|
78
|
+
body
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
raise NyaPr::Error.new(
|
|
82
|
+
"GET #{path}: GitHub API error #{response.status}: #{message}",
|
|
83
|
+
status: response.status
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def next_page?(response)
|
|
88
|
+
response.headers['link']&.include?('rel="next"')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def prepare(path)
|
|
92
|
+
path.
|
|
93
|
+
delete_prefix('/').
|
|
94
|
+
gsub(%r{//}, '/').
|
|
95
|
+
gsub(%r{/+\z}, '')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
data/lib/nya_pr/progress.rb
CHANGED
|
@@ -19,7 +19,14 @@ module NyaPr
|
|
|
19
19
|
@output = output
|
|
20
20
|
@enabled = enabled
|
|
21
21
|
@current = 0
|
|
22
|
+
|
|
23
|
+
# Use a monotonic clock so elapsed time is not affected by system
|
|
24
|
+
# clock changes while the progress bar is running.
|
|
22
25
|
@started_at = monotonic_time
|
|
26
|
+
|
|
27
|
+
# Progress may be updated by multiple worker threads. The mutex keeps
|
|
28
|
+
# the counter update and terminal rendering atomic relative to each
|
|
29
|
+
# other, preventing lost increments and interleaved output.
|
|
23
30
|
@mutex = Mutex.new
|
|
24
31
|
|
|
25
32
|
render if enabled?
|
|
@@ -29,6 +36,7 @@ module NyaPr
|
|
|
29
36
|
return unless enabled?
|
|
30
37
|
|
|
31
38
|
mutex.synchronize do
|
|
39
|
+
# Never allow the displayed counter to exceed the expected total.
|
|
32
40
|
@current = [@current + step, total].min
|
|
33
41
|
render
|
|
34
42
|
end
|
|
@@ -38,6 +46,8 @@ module NyaPr
|
|
|
38
46
|
return unless enabled?
|
|
39
47
|
|
|
40
48
|
mutex.synchronize do
|
|
49
|
+
# Render the final known state, then move subsequent output
|
|
50
|
+
# to a new terminal line.
|
|
41
51
|
render
|
|
42
52
|
output.puts
|
|
43
53
|
output.flush
|
|
@@ -49,11 +59,13 @@ module NyaPr
|
|
|
49
59
|
attr_reader :current, :started_at, :mutex
|
|
50
60
|
|
|
51
61
|
def enabled?
|
|
62
|
+
# A zero-sized task does not need a progress bar.
|
|
52
63
|
@enabled && total.positive?
|
|
53
64
|
end
|
|
54
65
|
|
|
55
66
|
def render
|
|
56
67
|
output.print(
|
|
68
|
+
# Return to the beginning of the line and clear the previous render.
|
|
57
69
|
"\r\e[2K",
|
|
58
70
|
title,
|
|
59
71
|
' [',
|
|
@@ -63,7 +75,6 @@ module NyaPr
|
|
|
63
75
|
"#{percentage}% ",
|
|
64
76
|
"ETA #{eta}"
|
|
65
77
|
)
|
|
66
|
-
|
|
67
78
|
output.flush
|
|
68
79
|
end
|
|
69
80
|
|
|
@@ -99,8 +110,11 @@ module NyaPr
|
|
|
99
110
|
return "#{seconds}s" if seconds < 60
|
|
100
111
|
|
|
101
112
|
minutes, seconds = seconds.divmod(60)
|
|
113
|
+
return "#{minutes}m #{seconds.to_s.rjust(2, '0')}s" if minutes < 60
|
|
114
|
+
|
|
115
|
+
hours, minutes = minutes.divmod(60)
|
|
102
116
|
|
|
103
|
-
"#{
|
|
117
|
+
"#{hours}h #{minutes.to_s.rjust(2, '0')}m"
|
|
104
118
|
end
|
|
105
119
|
|
|
106
120
|
def monotonic_time
|
|
@@ -5,7 +5,8 @@ module NyaPr
|
|
|
5
5
|
class Config < Data.define(
|
|
6
6
|
:limit,
|
|
7
7
|
:skip_drafts,
|
|
8
|
-
:progress_enabled
|
|
8
|
+
:progress_enabled,
|
|
9
|
+
:pull_requests_dir
|
|
9
10
|
)
|
|
10
11
|
DEFAULT_LIMIT = 100
|
|
11
12
|
|
|
@@ -13,7 +14,8 @@ module NyaPr
|
|
|
13
14
|
new(
|
|
14
15
|
limit: config.limit,
|
|
15
16
|
skip_drafts: config.skip_drafts,
|
|
16
|
-
progress_enabled: config.progress
|
|
17
|
+
progress_enabled: config.progress,
|
|
18
|
+
pull_requests_dir: config.pull_requests_dir
|
|
17
19
|
)
|
|
18
20
|
end
|
|
19
21
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module NyaPr
|
|
6
|
+
module PullRequest
|
|
7
|
+
class Paths
|
|
8
|
+
DEFAULT_DIR = 'data'
|
|
9
|
+
FILENAME = 'pull_requests.csv'
|
|
10
|
+
|
|
11
|
+
attr_reader :config
|
|
12
|
+
|
|
13
|
+
def initialize(config, timestamp: Time.now)
|
|
14
|
+
@config = config
|
|
15
|
+
@timestamp = timestamp
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def csv
|
|
19
|
+
path = run_dir.join(FILENAME)
|
|
20
|
+
|
|
21
|
+
FileUtils.mkdir_p(path.dirname)
|
|
22
|
+
|
|
23
|
+
path.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :timestamp
|
|
29
|
+
|
|
30
|
+
def run_dir
|
|
31
|
+
@run_dir ||= Pathname.
|
|
32
|
+
new(config.pull_requests_dir || DEFAULT_DIR).
|
|
33
|
+
join("pull_requests_#{formatted_timestamp}")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def formatted_timestamp
|
|
37
|
+
timestamp.strftime('%Y%m%d_%H%M%S')
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -8,7 +8,8 @@ module NyaPr
|
|
|
8
8
|
:refresh,
|
|
9
9
|
:skip_archived,
|
|
10
10
|
:progress_enabled,
|
|
11
|
-
:workers
|
|
11
|
+
:workers,
|
|
12
|
+
:repositories_csv
|
|
12
13
|
)
|
|
13
14
|
DEFAULT_WORKERS = 10
|
|
14
15
|
|
|
@@ -19,7 +20,8 @@ module NyaPr
|
|
|
19
20
|
refresh: config.refresh,
|
|
20
21
|
skip_archived: config.skip_archived,
|
|
21
22
|
progress_enabled: config.progress,
|
|
22
|
-
workers:
|
|
23
|
+
workers: config.workers,
|
|
24
|
+
repositories_csv: config.repositories_csv
|
|
23
25
|
)
|
|
24
26
|
end
|
|
25
27
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module NyaPr
|
|
4
4
|
module Repository
|
|
5
5
|
class Filter
|
|
6
|
-
include NyaPr::Request
|
|
6
|
+
include NyaPr::Github::Request
|
|
7
7
|
|
|
8
8
|
attr_reader :client, :config
|
|
9
9
|
|
|
@@ -66,6 +66,8 @@ module NyaPr
|
|
|
66
66
|
def build_workers(queue, result, progress_bar)
|
|
67
67
|
Array.new(workers) do
|
|
68
68
|
Thread.new do
|
|
69
|
+
Thread.current.report_on_exception = false
|
|
70
|
+
|
|
69
71
|
process_queue(queue, result, progress_bar)
|
|
70
72
|
end
|
|
71
73
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module NyaPr
|
|
6
|
+
module Repository
|
|
7
|
+
class Paths
|
|
8
|
+
DEFAULT_PATH = 'data/repositories.csv'
|
|
9
|
+
|
|
10
|
+
attr_reader :config
|
|
11
|
+
|
|
12
|
+
def initialize(config)
|
|
13
|
+
@config = config
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def csv
|
|
17
|
+
path = Pathname.new(
|
|
18
|
+
config.repositories_csv || DEFAULT_PATH
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
FileUtils.mkdir_p(path.dirname)
|
|
22
|
+
|
|
23
|
+
path.to_s
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/nya_pr/runner.rb
CHANGED
|
@@ -15,19 +15,13 @@ module NyaPr
|
|
|
15
15
|
configure_logger
|
|
16
16
|
|
|
17
17
|
pull_requests = find_pull_requests
|
|
18
|
+
|
|
18
19
|
pull_request_printer.print(pull_requests)
|
|
19
20
|
save_pull_requests(pull_requests)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
24
|
-
def paths
|
|
25
|
-
@paths ||= Paths.new(
|
|
26
|
-
repositories_path: config.repositories_csv,
|
|
27
|
-
pull_requests_dir: config.pull_requests_dir
|
|
28
|
-
)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
25
|
def configure_logger
|
|
32
26
|
NyaPr.logger.level = config.log_level
|
|
33
27
|
end
|
|
@@ -36,23 +30,43 @@ module NyaPr
|
|
|
36
30
|
repository_collector.collect
|
|
37
31
|
end
|
|
38
32
|
|
|
39
|
-
def
|
|
40
|
-
@
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
def repository_config
|
|
34
|
+
@repository_config ||= Repository::Config.from_app(config)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pull_request_config
|
|
38
|
+
@pull_request_config ||= PullRequest::Config.from_app(config)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def repository_paths
|
|
42
|
+
@repository_paths ||= Repository::Paths.new(
|
|
43
43
|
repository_config
|
|
44
44
|
)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
@
|
|
47
|
+
def pull_request_paths
|
|
48
|
+
@pull_request_paths ||= PullRequest::Paths.new(
|
|
49
|
+
pull_request_config
|
|
50
|
+
)
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
+
def repository_store
|
|
54
|
+
@repository_store ||= Repository::CsvStore.new(
|
|
55
|
+
repository_paths.csv
|
|
56
|
+
)
|
|
57
|
+
end
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
def pull_request_store
|
|
60
|
+
@pull_request_store ||= PullRequest::CsvStore.new(
|
|
61
|
+
pull_request_paths.csv
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def repository_collector
|
|
66
|
+
@repository_collector ||= Repository::Collector.new(
|
|
67
|
+
client,
|
|
68
|
+
repository_store,
|
|
69
|
+
repository_config
|
|
56
70
|
)
|
|
57
71
|
end
|
|
58
72
|
|
|
@@ -66,16 +80,12 @@ module NyaPr
|
|
|
66
80
|
find
|
|
67
81
|
end
|
|
68
82
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def repository_store
|
|
74
|
-
@repository_store ||= Repository::CsvStore.new(paths.repositories_csv)
|
|
75
|
-
end
|
|
83
|
+
def save_pull_requests(pull_requests)
|
|
84
|
+
pull_request_store.write(pull_requests)
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
NyaPr.logger.info(
|
|
87
|
+
"Saved #{pull_requests.size} pull requests to #{pull_request_store.path}"
|
|
88
|
+
)
|
|
79
89
|
end
|
|
80
90
|
|
|
81
91
|
def pull_request_printer
|
|
@@ -83,7 +93,7 @@ module NyaPr
|
|
|
83
93
|
end
|
|
84
94
|
|
|
85
95
|
def client
|
|
86
|
-
@client ||= Client.new(token)
|
|
96
|
+
@client ||= Github::Client.new(token)
|
|
87
97
|
end
|
|
88
98
|
|
|
89
99
|
def token
|
data/lib/nya_pr/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nya_pr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Elijah S. Krukowski
|
|
@@ -65,6 +65,20 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '2.14'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: faraday-gzip
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: oj
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,23 +122,25 @@ files:
|
|
|
108
122
|
- exe/nya-pr
|
|
109
123
|
- lib/nya_pr.rb
|
|
110
124
|
- lib/nya_pr/cli/command.rb
|
|
111
|
-
- lib/nya_pr/
|
|
112
|
-
- lib/nya_pr/config.rb
|
|
113
|
-
- lib/nya_pr/connection.rb
|
|
125
|
+
- lib/nya_pr/config/global.rb
|
|
126
|
+
- lib/nya_pr/config/loader.rb
|
|
114
127
|
- lib/nya_pr/error.rb
|
|
115
|
-
- lib/nya_pr/
|
|
128
|
+
- lib/nya_pr/github/client.rb
|
|
129
|
+
- lib/nya_pr/github/connection.rb
|
|
130
|
+
- lib/nya_pr/github/request.rb
|
|
116
131
|
- lib/nya_pr/progress.rb
|
|
117
132
|
- lib/nya_pr/pull_request/config.rb
|
|
118
133
|
- lib/nya_pr/pull_request/csv_store.rb
|
|
119
134
|
- lib/nya_pr/pull_request/filter.rb
|
|
120
135
|
- lib/nya_pr/pull_request/finder.rb
|
|
136
|
+
- lib/nya_pr/pull_request/paths.rb
|
|
121
137
|
- lib/nya_pr/pull_request/printer.rb
|
|
122
138
|
- lib/nya_pr/repository/collector.rb
|
|
123
139
|
- lib/nya_pr/repository/config.rb
|
|
124
140
|
- lib/nya_pr/repository/csv_store.rb
|
|
125
141
|
- lib/nya_pr/repository/filter.rb
|
|
126
142
|
- lib/nya_pr/repository/finder.rb
|
|
127
|
-
- lib/nya_pr/
|
|
143
|
+
- lib/nya_pr/repository/paths.rb
|
|
128
144
|
- lib/nya_pr/runner.rb
|
|
129
145
|
- lib/nya_pr/version.rb
|
|
130
146
|
homepage: https://github.com/bodrovis/nya_pr
|
data/lib/nya_pr/client.rb
DELETED
data/lib/nya_pr/config.rb
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module NyaPr
|
|
4
|
-
class Config < Data.define(
|
|
5
|
-
:username,
|
|
6
|
-
:owners,
|
|
7
|
-
:token,
|
|
8
|
-
:repositories_csv,
|
|
9
|
-
:pull_requests_dir,
|
|
10
|
-
:limit,
|
|
11
|
-
:refresh,
|
|
12
|
-
:skip_archived,
|
|
13
|
-
:skip_drafts,
|
|
14
|
-
:progress,
|
|
15
|
-
:log_level
|
|
16
|
-
)
|
|
17
|
-
LOG_LEVELS = {
|
|
18
|
-
'debug' => Logger::DEBUG,
|
|
19
|
-
'info' => Logger::INFO,
|
|
20
|
-
'warn' => Logger::WARN,
|
|
21
|
-
'error' => Logger::ERROR,
|
|
22
|
-
'fatal' => Logger::FATAL
|
|
23
|
-
}.freeze
|
|
24
|
-
|
|
25
|
-
class << self
|
|
26
|
-
def from_options(options)
|
|
27
|
-
username = options[:username].to_s
|
|
28
|
-
validate_username!(username)
|
|
29
|
-
|
|
30
|
-
new(
|
|
31
|
-
username: username,
|
|
32
|
-
owners: normalize_owners(options[:owners]),
|
|
33
|
-
token: options[:token],
|
|
34
|
-
repositories_csv: options[:repositories_csv],
|
|
35
|
-
pull_requests_dir: options[:pull_requests_dir],
|
|
36
|
-
limit: normalize_limit(options[:limit]),
|
|
37
|
-
refresh: options[:refresh],
|
|
38
|
-
skip_archived: options[:skip_archived],
|
|
39
|
-
skip_drafts: options[:skip_drafts],
|
|
40
|
-
progress: options[:progress],
|
|
41
|
-
log_level: LOG_LEVELS.fetch(options[:log_level])
|
|
42
|
-
)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
def normalize_owners(owners)
|
|
48
|
-
Array(owners).
|
|
49
|
-
flat_map { |owner| owner.split(',') }.
|
|
50
|
-
map(&:strip).
|
|
51
|
-
reject(&:empty?)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def normalize_limit(value)
|
|
55
|
-
limit = Integer(value)
|
|
56
|
-
|
|
57
|
-
return limit if limit.positive?
|
|
58
|
-
|
|
59
|
-
raise NyaPr::Error, 'limit must be greater than 0'
|
|
60
|
-
rescue ArgumentError, TypeError
|
|
61
|
-
raise NyaPr::Error, 'limit must be a positive integer'
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def validate_username!(username)
|
|
65
|
-
return unless username.empty?
|
|
66
|
-
|
|
67
|
-
raise NyaPr::Error, 'GitHub username is required'
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
data/lib/nya_pr/connection.rb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'faraday'
|
|
4
|
-
|
|
5
|
-
module NyaPr
|
|
6
|
-
module Connection
|
|
7
|
-
API_URL = 'https://api.github.com'
|
|
8
|
-
API_VERSION = '2026-03-10'
|
|
9
|
-
|
|
10
|
-
def connection(client)
|
|
11
|
-
Faraday.new(connection_options(client)) do |faraday|
|
|
12
|
-
faraday.adapter Faraday.default_adapter
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def connection_options(client)
|
|
19
|
-
{
|
|
20
|
-
url: API_URL,
|
|
21
|
-
headers: {
|
|
22
|
-
accept: 'application/vnd.github+json',
|
|
23
|
-
user_agent: "nya_pr gem/#{NyaPr::VERSION}",
|
|
24
|
-
authorization: "Bearer #{client.token}",
|
|
25
|
-
'X-GitHub-Api-Version': client.options.fetch(:api_version, API_VERSION)
|
|
26
|
-
},
|
|
27
|
-
request: {
|
|
28
|
-
timeout: client.options.fetch(:timeout, 60),
|
|
29
|
-
open_timeout: client.options.fetch(:open_timeout, 10)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
data/lib/nya_pr/paths.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
|
|
5
|
-
module NyaPr
|
|
6
|
-
class Paths
|
|
7
|
-
DEFAULT_DATA_DIR = 'data'
|
|
8
|
-
REPOSITORIES_FILENAME = 'repositories.csv'
|
|
9
|
-
PULL_REQUESTS_FILENAME = 'pull_requests.csv'
|
|
10
|
-
|
|
11
|
-
attr_reader :repositories_path, :pull_requests_dir
|
|
12
|
-
|
|
13
|
-
def initialize(
|
|
14
|
-
repositories_path: nil,
|
|
15
|
-
pull_requests_dir: nil,
|
|
16
|
-
timestamp: Time.now
|
|
17
|
-
)
|
|
18
|
-
@repositories_path = repositories_path
|
|
19
|
-
@pull_requests_dir = pull_requests_dir
|
|
20
|
-
@timestamp = timestamp
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def repositories_csv
|
|
24
|
-
path = Pathname.new(
|
|
25
|
-
repositories_path || File.join(DEFAULT_DATA_DIR, REPOSITORIES_FILENAME)
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
prepare_parent(path)
|
|
29
|
-
path.to_s
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def pull_requests_csv
|
|
33
|
-
path = pull_requests_run_dir.join(PULL_REQUESTS_FILENAME)
|
|
34
|
-
|
|
35
|
-
FileUtils.mkdir_p(path.dirname)
|
|
36
|
-
|
|
37
|
-
path.to_s
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
attr_reader :timestamp
|
|
43
|
-
|
|
44
|
-
def pull_requests_run_dir
|
|
45
|
-
@pull_requests_run_dir ||= begin
|
|
46
|
-
base = Pathname.new(pull_requests_dir || DEFAULT_DATA_DIR)
|
|
47
|
-
|
|
48
|
-
base.join("pull_requests_#{formatted_timestamp}")
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def formatted_timestamp
|
|
53
|
-
timestamp.strftime('%Y%m%d_%H%M%S')
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def prepare_parent(path)
|
|
57
|
-
FileUtils.mkdir_p(path.dirname)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
data/lib/nya_pr/request.rb
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'oj'
|
|
4
|
-
|
|
5
|
-
module NyaPr
|
|
6
|
-
module Request
|
|
7
|
-
include NyaPr::Connection
|
|
8
|
-
|
|
9
|
-
MAX_PAGES = 1_000
|
|
10
|
-
PER_PAGE = 100
|
|
11
|
-
|
|
12
|
-
def get(path, params = {})
|
|
13
|
-
_response, body = do_get(path, params)
|
|
14
|
-
|
|
15
|
-
body
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def paginate(path, params = {})
|
|
19
|
-
params = params.merge(per_page: params.fetch(:per_page, PER_PAGE))
|
|
20
|
-
|
|
21
|
-
page = 1
|
|
22
|
-
result = []
|
|
23
|
-
|
|
24
|
-
loop do
|
|
25
|
-
raise NyaPr::Error, 'Pagination limit exceeded' if page > MAX_PAGES
|
|
26
|
-
|
|
27
|
-
NyaPr.logger.debug("Fetching page #{page} for #{path}")
|
|
28
|
-
response, body = do_get(path, params.merge(page: page))
|
|
29
|
-
|
|
30
|
-
raise NyaPr::Error, 'Expected paginated response to be an array' unless body.is_a?(Array)
|
|
31
|
-
|
|
32
|
-
result.concat(body)
|
|
33
|
-
|
|
34
|
-
break if body.empty?
|
|
35
|
-
break unless next_page?(response)
|
|
36
|
-
|
|
37
|
-
page += 1
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
result
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def do_get(path, params = {})
|
|
46
|
-
prepared_path = prepare(path)
|
|
47
|
-
|
|
48
|
-
NyaPr.logger.debug("GET #{prepared_path} #{params.inspect}")
|
|
49
|
-
|
|
50
|
-
response = connection(client).get(prepared_path, params)
|
|
51
|
-
body = parse_body(response)
|
|
52
|
-
|
|
53
|
-
NyaPr.logger.debug(
|
|
54
|
-
"GET #{prepared_path} -> HTTP #{response.status}"
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
raise_on_error!(response, body, prepared_path)
|
|
58
|
-
|
|
59
|
-
[response, body]
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def parse_body(response)
|
|
63
|
-
return nil if response.body.nil? || response.body.empty?
|
|
64
|
-
|
|
65
|
-
Oj.load(response.body)
|
|
66
|
-
rescue Oj::ParseError
|
|
67
|
-
raise NyaPr::Error, "Failed to parse response: #{response.body}"
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def raise_on_error!(response, body, path)
|
|
71
|
-
return if response.success?
|
|
72
|
-
|
|
73
|
-
message =
|
|
74
|
-
if body.is_a?(Hash)
|
|
75
|
-
body['message'] || body
|
|
76
|
-
else
|
|
77
|
-
body
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
raise NyaPr::Error.new(
|
|
81
|
-
"GET #{path}: GitHub API error #{response.status}: #{message}",
|
|
82
|
-
status: response.status
|
|
83
|
-
)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def next_page?(response)
|
|
87
|
-
response.headers['link']&.include?('rel="next"')
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def prepare(path)
|
|
91
|
-
path.
|
|
92
|
-
delete_prefix('/').
|
|
93
|
-
gsub(%r{//}, '/').
|
|
94
|
-
gsub(%r{/+\z}, '')
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|