nya_pr 0.1.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 +7 -0
- data/LICENSE.md +22 -0
- data/README.md +314 -0
- data/exe/nya-pr +14 -0
- data/lib/nya_pr/cli/command.rb +110 -0
- data/lib/nya_pr/client.rb +12 -0
- data/lib/nya_pr/connection.rb +34 -0
- data/lib/nya_pr/error.rb +13 -0
- data/lib/nya_pr/paths.rb +60 -0
- data/lib/nya_pr/progress.rb +110 -0
- data/lib/nya_pr/pull_request/csv_store.rb +32 -0
- data/lib/nya_pr/pull_request/finder.rb +136 -0
- data/lib/nya_pr/pull_request/printer.rb +42 -0
- data/lib/nya_pr/repository/collector.rb +85 -0
- data/lib/nya_pr/repository/csv_store.rb +49 -0
- data/lib/nya_pr/repository/filter.rb +117 -0
- data/lib/nya_pr/repository/finder.rb +89 -0
- data/lib/nya_pr/request.rb +97 -0
- data/lib/nya_pr/runner.rb +101 -0
- data/lib/nya_pr/version.rb +5 -0
- data/lib/nya_pr.rb +24 -0
- metadata +151 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 74fef078ebf20c6a46a8596b5001a66b744436a33ee407d14168699e577fa7e1
|
|
4
|
+
data.tar.gz: 3e686721cb075eead9c7cd7070cf49ee6d0b9eca7ce879b8555380c6d1a52626
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 40aa1079ac60ac7d0de2d79135eeb75f7b1854e4299277a94ec424268d0fb59719be1f8832546fb6fd28abeac63786b4043a2128ea60749daa183839850658f3
|
|
7
|
+
data.tar.gz: 0377b72b765df89b9da1ff33d92224431a1ebe16c10c70e457f4ebdb93381479d9c1a6e0122366075363f1d550ca1491647d9f0cd9b44347d9cca6304249806e
|
data/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Elijah S. Krukowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# NyaPr ฅ^•ﻌ•^ฅ
|
|
2
|
+
|
|
3
|
+
**NyaPr** is a tiny Ruby tool for people who have too many GitHub repositories, too many organizations, too many pull requests, and absolutely no desire to click through all that manually.
|
|
4
|
+
|
|
5
|
+
It collects repositories you own or contribute to, finds their open pull requests, and puts everything into one place.
|
|
6
|
+
|
|
7
|
+
For maximum kawaii.
|
|
8
|
+
|
|
9
|
+
## Why NyaPr?
|
|
10
|
+
|
|
11
|
+
GitHub is perfectly happy to show you:
|
|
12
|
+
|
|
13
|
+
* all repositories in a giant organization;
|
|
14
|
+
* pull requests you authored;
|
|
15
|
+
* pull requests where somebody mentioned you;
|
|
16
|
+
* approximately seventeen billion other things.
|
|
17
|
+
|
|
18
|
+
What it does not make particularly convenient is:
|
|
19
|
+
|
|
20
|
+
> Show me open pull requests from my own repositories **plus** repositories in these organizations where I actually contribute.
|
|
21
|
+
|
|
22
|
+
NyaPr does exactly that.
|
|
23
|
+
|
|
24
|
+
The basic flow is:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
GitHub users / organizations
|
|
28
|
+
↓
|
|
29
|
+
collect repositories
|
|
30
|
+
↓
|
|
31
|
+
keep repositories you actually contribute to
|
|
32
|
+
↓
|
|
33
|
+
optionally skip archived repositories
|
|
34
|
+
↓
|
|
35
|
+
cache the repository list
|
|
36
|
+
↓
|
|
37
|
+
find open pull requests
|
|
38
|
+
↓
|
|
39
|
+
terminal + timestamped CSV
|
|
40
|
+
↓
|
|
41
|
+
nya ฅ^•ﻌ•^ฅ
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
* Ruby 4+
|
|
47
|
+
* A GitHub personal access token
|
|
48
|
+
* A reasonable appreciation of cats
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
Install the dependencies:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bundle install
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Create a `.env` file:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
GITHUB_TOKEN=your_github_token
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For private repositories, make sure the token has access to them. If an organization uses SSO, the token may also need to be authorized for that organization.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
Run NyaPr with your GitHub username:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ruby run.rb --user bodrovis
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can also provide additional users or organizations whose repositories should be inspected:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ruby run.rb \
|
|
78
|
+
--user bodrovis \
|
|
79
|
+
--owners org1,org2
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Your own repositories are always included automatically.
|
|
83
|
+
|
|
84
|
+
For other users and organizations, NyaPr checks which repositories contain commits authored by the specified GitHub user.
|
|
85
|
+
|
|
86
|
+
## Data directory
|
|
87
|
+
|
|
88
|
+
By default, NyaPr stores generated data inside the `data` directory.
|
|
89
|
+
|
|
90
|
+
The repository cache is stored as:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
data/repositories.csv
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Each pull request scan gets its own timestamped directory:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
data/
|
|
100
|
+
├── repositories.csv
|
|
101
|
+
├── pull_requests_20260912_201845/
|
|
102
|
+
│ └── pull_requests.csv
|
|
103
|
+
└── pull_requests_20260912_204112/
|
|
104
|
+
└── pull_requests.csv
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This keeps repository discovery cached while preserving the results of individual pull request scans.
|
|
108
|
+
|
|
109
|
+
Missing directories are created automatically.
|
|
110
|
+
|
|
111
|
+
## Repository cache
|
|
112
|
+
|
|
113
|
+
Repository discovery can involve a lot of GitHub API requests, especially for large organizations.
|
|
114
|
+
|
|
115
|
+
NyaPr therefore stores discovered repositories in:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
data/repositories.csv
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
On subsequent runs, this file is used as a cache instead of scanning GitHub again.
|
|
122
|
+
|
|
123
|
+
To force a fresh repository scan:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ruby run.rb \
|
|
127
|
+
--user bodrovis \
|
|
128
|
+
--owners org1,org2 \
|
|
129
|
+
--refresh
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
You can use another repository cache file:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
ruby run.rb \
|
|
136
|
+
--user bodrovis \
|
|
137
|
+
--repositories-csv "C:\Users\me\repositories.csv"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Both relative and absolute paths are supported.
|
|
141
|
+
|
|
142
|
+
Repository contribution checks are performed concurrently using a small thread pool, because waiting for hundreds of HTTP requests one by one is not particularly kawaii.
|
|
143
|
+
|
|
144
|
+
## Skipping archived repositories
|
|
145
|
+
|
|
146
|
+
Archived repositories are usually ancient tombs where pull requests go to sleep forever.
|
|
147
|
+
|
|
148
|
+
You can ignore them completely:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
ruby run.rb \
|
|
152
|
+
--user bodrovis \
|
|
153
|
+
--owners org1,org2 \
|
|
154
|
+
--skip-archived
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
When this option is enabled, archived repositories are removed before expensive contribution checks are performed.
|
|
158
|
+
|
|
159
|
+
Maximum efficiency. Minimum archaeology.
|
|
160
|
+
|
|
161
|
+
## Pull requests
|
|
162
|
+
|
|
163
|
+
After preparing the repository list, NyaPr searches each repository for open pull requests.
|
|
164
|
+
|
|
165
|
+
Results are grouped by repository and printed to the terminal:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
bodrovis/example
|
|
169
|
+
----------------
|
|
170
|
+
|
|
171
|
+
#42 Fix pagination
|
|
172
|
+
Author: @someone
|
|
173
|
+
https://github.com/bodrovis/example/pull/42
|
|
174
|
+
|
|
175
|
+
#43 [DRAFT] Add nya support
|
|
176
|
+
Author: @someone_else
|
|
177
|
+
https://github.com/bodrovis/example/pull/43
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
They are also written to a timestamped CSV file:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
data/pull_requests_20260912_201845/pull_requests.csv
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The CSV contains:
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
repository
|
|
190
|
+
number
|
|
191
|
+
title
|
|
192
|
+
author
|
|
193
|
+
draft
|
|
194
|
+
created_at
|
|
195
|
+
updated_at
|
|
196
|
+
html_url
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
By default, NyaPr stops after collecting **100 open pull requests**.
|
|
200
|
+
|
|
201
|
+
You can change the limit with `--limit`:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
ruby run.rb \
|
|
205
|
+
--user bodrovis \
|
|
206
|
+
--limit 250
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
You can also change the base directory where pull request runs are stored:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
ruby run.rb \
|
|
213
|
+
--user bodrovis \
|
|
214
|
+
--pull-requests-dir "C:\Users\me\nya-results"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
This produces something like:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
C:\Users\me\nya-results\pull_requests_20260912_201845\pull_requests.csv
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Each run still gets its own timestamped directory.
|
|
224
|
+
|
|
225
|
+
## Logging
|
|
226
|
+
|
|
227
|
+
NyaPr uses normal log levels instead of spraying random `puts` statements everywhere like an animal.
|
|
228
|
+
|
|
229
|
+
The default level is `info`.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
ruby run.rb \
|
|
235
|
+
--user bodrovis \
|
|
236
|
+
--owners org1,org2 \
|
|
237
|
+
--log-level info
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
At the `info` level, NyaPr reports the major stages of the run: loading or discovering repositories, filtering contributions, searching for pull requests, and saving results.
|
|
241
|
+
|
|
242
|
+
For HTTP-level details:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
ruby run.rb ... --log-level debug
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
This also shows requests, responses, skipped empty repositories, detected contributions, and other useful diagnostic information.
|
|
249
|
+
|
|
250
|
+
Available levels:
|
|
251
|
+
|
|
252
|
+
```text
|
|
253
|
+
debug
|
|
254
|
+
info
|
|
255
|
+
warn
|
|
256
|
+
error
|
|
257
|
+
fatal
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Logs are written to `stderr`, while actual pull request output goes to `stdout`.
|
|
261
|
+
|
|
262
|
+
## CLI options
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
-u, --user USERNAME GitHub username
|
|
266
|
+
-o, --owners LIST Additional users or organizations, comma-separated
|
|
267
|
+
--token TOKEN GitHub token (GITHUB_TOKEN env variable is recommended)
|
|
268
|
+
--repositories-csv PATH Repository cache CSV path
|
|
269
|
+
--pull-requests-dir PATH Directory for pull request runs
|
|
270
|
+
-l, --limit N Maximum number of pull requests to collect
|
|
271
|
+
-r, --refresh Refresh repositories from GitHub
|
|
272
|
+
--skip-archived Ignore archived repositories
|
|
273
|
+
--[no-]progress Show or hide progress bars
|
|
274
|
+
--log-level LEVEL Logging level
|
|
275
|
+
-v, --version Show NyaPr version
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Example with most options enabled:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
ruby run.rb \
|
|
282
|
+
--user bodrovis \
|
|
283
|
+
--owners org1,org2 \
|
|
284
|
+
--repositories-csv "data/repositories.csv" \
|
|
285
|
+
--pull-requests-dir "data" \
|
|
286
|
+
--limit 250 \
|
|
287
|
+
--skip-archived \
|
|
288
|
+
--refresh \
|
|
289
|
+
--log-level debug
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## How repository discovery works
|
|
293
|
+
|
|
294
|
+
For your own GitHub username, NyaPr collects repositories owned by your authenticated account.
|
|
295
|
+
|
|
296
|
+
For additional owners:
|
|
297
|
+
|
|
298
|
+
1. NyaPr determines whether the owner is a GitHub user or organization.
|
|
299
|
+
2. It collects their repositories.
|
|
300
|
+
3. Archived repositories can optionally be removed.
|
|
301
|
+
4. NyaPr checks whether your GitHub user has commits in each repository.
|
|
302
|
+
5. Only repositories where you contributed are retained.
|
|
303
|
+
|
|
304
|
+
Contribution checks run concurrently using a limited number of worker threads.
|
|
305
|
+
|
|
306
|
+
Empty Git repositories are simply skipped.
|
|
307
|
+
|
|
308
|
+
Repositories with pull requests disabled are also skipped when searching for PRs.
|
|
309
|
+
|
|
310
|
+
The prepared repository list is cached, but pull requests are fetched fresh on every run.
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
(c) [Elijah S. Krukowski](https://bodrovis.tech), MIT license
|
data/exe/nya-pr
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'nya_pr'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
NyaPr.run
|
|
8
|
+
rescue OptionParser::ParseError, NyaPr::Error => e
|
|
9
|
+
warn "nya-pr: #{e.message}"
|
|
10
|
+
exit 1
|
|
11
|
+
rescue Interrupt
|
|
12
|
+
warn "\nnya-pr: interrupted"
|
|
13
|
+
exit 130
|
|
14
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NyaPr
|
|
4
|
+
module Cli
|
|
5
|
+
class Command < Dry::CLI::Command
|
|
6
|
+
LOG_LEVELS = {
|
|
7
|
+
'debug' => Logger::DEBUG,
|
|
8
|
+
'info' => Logger::INFO,
|
|
9
|
+
'warn' => Logger::WARN,
|
|
10
|
+
'error' => Logger::ERROR,
|
|
11
|
+
'fatal' => Logger::FATAL
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
desc 'Collect open GitHub pull requests'
|
|
15
|
+
|
|
16
|
+
option :username,
|
|
17
|
+
aliases: ['-u', '--user'],
|
|
18
|
+
desc: 'GitHub username'
|
|
19
|
+
|
|
20
|
+
option :owners,
|
|
21
|
+
aliases: ['-o'],
|
|
22
|
+
type: :array,
|
|
23
|
+
default: [],
|
|
24
|
+
desc: 'Repository owners'
|
|
25
|
+
|
|
26
|
+
option :token,
|
|
27
|
+
desc: 'GitHub token'
|
|
28
|
+
|
|
29
|
+
option :repositories_csv,
|
|
30
|
+
desc: 'Repository cache CSV path'
|
|
31
|
+
|
|
32
|
+
option :pull_requests_dir,
|
|
33
|
+
desc: 'Directory for pull request runs'
|
|
34
|
+
|
|
35
|
+
option :limit,
|
|
36
|
+
aliases: ['-l'],
|
|
37
|
+
default: PullRequest::Finder::MAX_PULL_REQUESTS,
|
|
38
|
+
desc: 'Maximum number of pull requests'
|
|
39
|
+
|
|
40
|
+
option :refresh,
|
|
41
|
+
aliases: ['-r'],
|
|
42
|
+
type: :boolean,
|
|
43
|
+
default: false,
|
|
44
|
+
desc: 'Refresh repositories from GitHub'
|
|
45
|
+
|
|
46
|
+
option :skip_archived,
|
|
47
|
+
type: :boolean,
|
|
48
|
+
default: false,
|
|
49
|
+
desc: 'Skip archived repositories'
|
|
50
|
+
|
|
51
|
+
option :progress,
|
|
52
|
+
type: :boolean,
|
|
53
|
+
default: true,
|
|
54
|
+
desc: 'Show progress bars'
|
|
55
|
+
|
|
56
|
+
option :log_level,
|
|
57
|
+
values: LOG_LEVELS.keys,
|
|
58
|
+
default: 'info',
|
|
59
|
+
desc: 'Log level'
|
|
60
|
+
|
|
61
|
+
option :version,
|
|
62
|
+
aliases: ['-v'],
|
|
63
|
+
type: :flag,
|
|
64
|
+
desc: 'Show version'
|
|
65
|
+
|
|
66
|
+
def call(version: false, **options)
|
|
67
|
+
if version
|
|
68
|
+
puts NyaPr::VERSION
|
|
69
|
+
return
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
normalize_options!(options)
|
|
73
|
+
validate_username!(options)
|
|
74
|
+
|
|
75
|
+
Runner.new(options).run
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def normalize_options!(options)
|
|
81
|
+
options[:owners] = normalize_owners(options[:owners])
|
|
82
|
+
options[:limit] = normalize_limit(options[:limit])
|
|
83
|
+
options[:log_level] = LOG_LEVELS.fetch(options[:log_level])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def normalize_owners(owners)
|
|
87
|
+
Array(owners).
|
|
88
|
+
flat_map { |owner| owner.split(',') }.
|
|
89
|
+
map(&:strip).
|
|
90
|
+
reject(&:empty?)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def normalize_limit(value)
|
|
94
|
+
limit = Integer(value)
|
|
95
|
+
|
|
96
|
+
return limit if limit.positive?
|
|
97
|
+
|
|
98
|
+
raise NyaPr::Error, 'limit must be greater than 0'
|
|
99
|
+
rescue ArgumentError, TypeError
|
|
100
|
+
raise NyaPr::Error, 'limit must be a positive integer'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_username!(options)
|
|
104
|
+
return unless options[:username].to_s.empty?
|
|
105
|
+
|
|
106
|
+
raise NyaPr::Error, 'GitHub username is required'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
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/error.rb
ADDED
data/lib/nya_pr/paths.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
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
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NyaPr
|
|
4
|
+
class Progress
|
|
5
|
+
DEFAULT_WIDTH = 30
|
|
6
|
+
|
|
7
|
+
attr_reader :title, :total, :width, :output
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
title,
|
|
11
|
+
total:,
|
|
12
|
+
enabled: true,
|
|
13
|
+
width: DEFAULT_WIDTH,
|
|
14
|
+
output: $stderr
|
|
15
|
+
)
|
|
16
|
+
@title = title
|
|
17
|
+
@total = total
|
|
18
|
+
@width = width
|
|
19
|
+
@output = output
|
|
20
|
+
@enabled = enabled
|
|
21
|
+
@current = 0
|
|
22
|
+
@started_at = monotonic_time
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
|
|
25
|
+
render if enabled?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def advance(step = 1)
|
|
29
|
+
return unless enabled?
|
|
30
|
+
|
|
31
|
+
mutex.synchronize do
|
|
32
|
+
@current = [@current + step, total].min
|
|
33
|
+
render
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def finish
|
|
38
|
+
return unless enabled?
|
|
39
|
+
|
|
40
|
+
mutex.synchronize do
|
|
41
|
+
render
|
|
42
|
+
output.puts
|
|
43
|
+
output.flush
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
attr_reader :current, :started_at, :mutex
|
|
50
|
+
|
|
51
|
+
def enabled?
|
|
52
|
+
@enabled && total.positive?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def render
|
|
56
|
+
output.print(
|
|
57
|
+
"\r\e[2K",
|
|
58
|
+
title,
|
|
59
|
+
' [',
|
|
60
|
+
progress_bar,
|
|
61
|
+
'] ',
|
|
62
|
+
"#{current}/#{total} ",
|
|
63
|
+
"#{percentage}% ",
|
|
64
|
+
"ETA #{eta}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
output.flush
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def progress_bar
|
|
71
|
+
completed = (width * progress).round
|
|
72
|
+
remaining = width - completed
|
|
73
|
+
|
|
74
|
+
('█' * completed) + ('░' * remaining)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def progress
|
|
78
|
+
current.to_f / total
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def percentage
|
|
82
|
+
(progress * 100).round
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def eta
|
|
86
|
+
return '--' if current.zero?
|
|
87
|
+
|
|
88
|
+
elapsed = monotonic_time - started_at
|
|
89
|
+
rate = current / elapsed
|
|
90
|
+
|
|
91
|
+
return '--' unless rate.positive?
|
|
92
|
+
|
|
93
|
+
seconds = ((total - current) / rate).round
|
|
94
|
+
|
|
95
|
+
format_duration(seconds)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def format_duration(seconds)
|
|
99
|
+
return "#{seconds}s" if seconds < 60
|
|
100
|
+
|
|
101
|
+
minutes, seconds = seconds.divmod(60)
|
|
102
|
+
|
|
103
|
+
"#{minutes}m #{seconds.to_s.rjust(2, '0')}s"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def monotonic_time
|
|
107
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|