pra 1.7.2 → 2.0.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/ChangeLog.md +10 -0
- data/README.md +39 -8
- data/lib/pra/app.rb +11 -4
- data/lib/pra/config.rb +17 -6
- data/lib/pra/curses_pull_request_presenter.rb +30 -10
- data/lib/pra/curses_window_system.rb +120 -18
- data/lib/pra/github_pull_source.rb +97 -21
- data/lib/pra/log.rb +28 -3
- data/lib/pra/pull_request.rb +4 -1
- data/lib/pra/pull_request_service.rb +4 -5
- data/lib/pra/version.rb +1 -1
- data/lib/pra.rb +3 -1
- data/pra.gemspec +2 -1
- metadata +18 -5
- data/lib/pra/error_log.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4c0d01126812263dbcf4816f1dcd3383e99d702
|
4
|
+
data.tar.gz: 99804a97e1c2dc20cc06510e52a1c4abd9fae6ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b2e1d2f91e56a3954cc2a8236e0ddf481b19da67f108173bb3a52a4e545639040b6168b1901896d6368dd0223a523540a7e4232b0c0f716578133d5da55750f
|
7
|
+
data.tar.gz: 80296a40d2becad79596672025d243d58b300cfbbc58419b6d3b246f00fa3c75b68b1d43c37ababc8e4d10c53be99f173bdf195f062ea3e25782932d11f556af
|
data/ChangeLog.md
CHANGED
@@ -6,6 +6,16 @@ versions as well as provide a rough history.
|
|
6
6
|
|
7
7
|
#### Next Release
|
8
8
|
|
9
|
+
#### v2.0.0
|
10
|
+
* Added ability to refresh pull requests
|
11
|
+
* Added pagination when there are more pull requests than can be displayed
|
12
|
+
* Added support for github organizations
|
13
|
+
* Added last refresh time to UI
|
14
|
+
* Redesgined pull request render process to make format changes easier
|
15
|
+
* Added github labels
|
16
|
+
* Changed github query method to reduce API calls and support organizations
|
17
|
+
* Added github rate limiting protection
|
18
|
+
|
9
19
|
#### v1.7.2
|
10
20
|
|
11
21
|
* Fix bug #2, left over PRs not clearing
|
data/README.md
CHANGED
@@ -13,13 +13,21 @@ You can easily install `pra` with the following command:
|
|
13
13
|
|
14
14
|
$ gem install pra
|
15
15
|
|
16
|
+
## Migrating to 2.0
|
17
|
+
|
18
|
+
The configuration location has moved from `~/.pra.json` to
|
19
|
+
`~/.pra/config.json`. The current version of pra reads from both locations but
|
20
|
+
it is preferred to move your configuration file to the `.pra` folder. This
|
21
|
+
folder will automatically be created, if it does not exist, for logging
|
22
|
+
purposes when pra starts.
|
23
|
+
|
16
24
|
## Configuration
|
17
25
|
|
18
26
|
`pra` requires one configuration, `~/.pra.json`, to exist in your home
|
19
27
|
directory. The following is an example config that can be used as a starter.
|
20
28
|
*Note:* You will need to replace **your.username**, **your.password**,
|
21
|
-
**your.stash.server**, and the **repositories**
|
22
|
-
sources.
|
29
|
+
**your.stash.server**, and the **repositories** and **organizations** sections
|
30
|
+
of each of the pull sources.
|
23
31
|
|
24
32
|
{
|
25
33
|
"pull_sources": [
|
@@ -47,6 +55,9 @@ sources.
|
|
47
55
|
"repositories": [
|
48
56
|
{ "owner": "reachlocal", "repository": "snapdragon" },
|
49
57
|
{ "owner": "brewster", "repository": "cequel" }
|
58
|
+
],
|
59
|
+
"organizations": [
|
60
|
+
{ "name": "codebreakdown", "exclude": ["snapdragon"]}
|
50
61
|
]
|
51
62
|
}
|
52
63
|
}
|
@@ -54,14 +65,17 @@ sources.
|
|
54
65
|
"assignee_blacklist": [
|
55
66
|
"IPT-Capture",
|
56
67
|
"IPT-Core Services"
|
57
|
-
]
|
68
|
+
],
|
69
|
+
"refresh_interval": 300,
|
70
|
+
"log_level": "info"
|
58
71
|
}
|
59
72
|
|
60
|
-
I suggest copying and pasting the above starter file into your `~/.pra.json`
|
73
|
+
I suggest copying and pasting the above starter file into your `~/.pra/config.json`
|
61
74
|
file to get you started. Then simply replace the appropriate fields and the
|
62
|
-
**repositories** sections for all the pull sources with the repository
|
75
|
+
**repositories** and **organizations** sections for all the pull sources with the repository
|
63
76
|
information for the repositories you want to watch for open pull requests.
|
64
77
|
|
78
|
+
|
65
79
|
#### Stash User & Project Repositories
|
66
80
|
|
67
81
|
You may have noticed that the above example shows two repository objects in its
|
@@ -81,6 +95,13 @@ scenarios where the repository is housed under a project.
|
|
81
95
|
Reduces noise to more easily determine which pull requests are unassigned. Names
|
82
96
|
added will not appear in the assignee column.
|
83
97
|
|
98
|
+
#### Github Organizations
|
99
|
+
|
100
|
+
Instead of listing each repository, an organization can be provided and all
|
101
|
+
pull requests open for projects in that organization will be listed. To
|
102
|
+
exclude any unwanted projects, add the repository name to the `exclude` array
|
103
|
+
for that organization.
|
104
|
+
|
84
105
|
### GitHub Authentication
|
85
106
|
|
86
107
|
#### Multi-Factor Authentication
|
@@ -119,9 +140,10 @@ running the following command:
|
|
119
140
|
|
120
141
|
pra
|
121
142
|
|
122
|
-
Once it launches, it will use the information provided in the
|
123
|
-
configuration file to fetch all the open pull requests
|
124
|
-
the pull requests are displayed you can
|
143
|
+
Once it launches, it will use the information provided in the
|
144
|
+
`~/.pra/config.json` configuration file to fetch all the open pull requests
|
145
|
+
and display them. Once, the pull requests are displayed you can perform any of
|
146
|
+
the following actions.
|
125
147
|
|
126
148
|
### Move Selection Up
|
127
149
|
|
@@ -136,6 +158,15 @@ To move the selection down simply press either the `j` or `down arrow` key.
|
|
136
158
|
If you would like to open the currently selected pull request in your default
|
137
159
|
browser you can press either the `o` or `enter` key.
|
138
160
|
|
161
|
+
### Refresh
|
162
|
+
|
163
|
+
To force a refresh press the `r` key.
|
164
|
+
|
165
|
+
### Move Between Pages
|
166
|
+
|
167
|
+
To move between pages press `n` to go to the next page, and `p` to go to the
|
168
|
+
previous page.
|
169
|
+
|
139
170
|
### Quit
|
140
171
|
|
141
172
|
If you decide you have had enough and want to exit `pra` press the `q` key.
|
data/lib/pra/app.rb
CHANGED
@@ -2,7 +2,7 @@ require 'thread'
|
|
2
2
|
|
3
3
|
require 'pra/window_system_factory'
|
4
4
|
require 'pra/pull_request_service'
|
5
|
-
require 'pra/
|
5
|
+
require 'pra/log'
|
6
6
|
|
7
7
|
Thread.abort_on_exception=true
|
8
8
|
|
@@ -22,6 +22,15 @@ module Pra
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def fetch_and_refresh_pull_requests
|
25
|
+
if @window_system.force_refresh || Time.now - @window_system.last_updated > Pra.config.refresh_interval
|
26
|
+
refresh_pull_requests
|
27
|
+
end
|
28
|
+
|
29
|
+
Kernel.sleep(0.1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def refresh_pull_requests
|
33
|
+
@window_system.force_refresh = false
|
25
34
|
@window_system.fetching_pull_requests
|
26
35
|
new_pull_requests = []
|
27
36
|
|
@@ -31,14 +40,12 @@ module Pra
|
|
31
40
|
end
|
32
41
|
|
33
42
|
fetch.on_error do |error|
|
34
|
-
Pra::
|
43
|
+
Pra::Log.error(error)
|
35
44
|
@window_system.fetch_failed
|
36
45
|
end
|
37
46
|
end
|
38
47
|
|
39
48
|
@window_system.refresh_pull_requests(new_pull_requests)
|
40
|
-
|
41
|
-
Kernel.sleep(5 * 60)
|
42
49
|
end
|
43
50
|
|
44
51
|
def pull_request_fetcher_thread
|
data/lib/pra/config.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'fileutils'
|
2
3
|
|
3
4
|
module Pra
|
4
5
|
class Config
|
5
6
|
def initialize(initial_config = {})
|
6
7
|
@initial_config = initial_config
|
8
|
+
if @initial_config["log_level"]
|
9
|
+
Pra::Log.level(@initial_config["log_level"])
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
def self.load_config
|
@@ -22,15 +26,18 @@ module Pra
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def self.config_path
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
if File.exists?(File.join(self.users_home_directory, '.pra', 'config.json'))
|
30
|
+
return File.join(self.users_home_directory, '.pra', 'config.json')
|
31
|
+
else
|
32
|
+
return File.join(self.users_home_directory, '.pra.json')
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
def self.log_path
|
33
|
-
|
37
|
+
unless Dir.exists?(File.join(self.users_home_directory, '.pra', 'logs'))
|
38
|
+
FileUtils.mkdir_p(File.join(self.users_home_directory, '.pra', 'logs'))
|
39
|
+
end
|
40
|
+
return File.join(self.users_home_directory, '.pra', 'logs', '.pra.log')
|
34
41
|
end
|
35
42
|
|
36
43
|
def self.users_home_directory
|
@@ -48,5 +55,9 @@ module Pra
|
|
48
55
|
def assignee_blacklist
|
49
56
|
Array(@initial_config["assignee_blacklist"])
|
50
57
|
end
|
58
|
+
|
59
|
+
def refresh_interval
|
60
|
+
@initial_config["refresh_interval"] || 60*5
|
61
|
+
end
|
51
62
|
end
|
52
63
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pra/config'
|
2
|
+
require 'time-lord'
|
2
3
|
|
3
4
|
module Pra
|
4
5
|
class CursesPullRequestPresenter
|
@@ -7,37 +8,56 @@ module Pra
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def repository
|
10
|
-
|
11
|
+
@pull_request.repository
|
11
12
|
end
|
12
13
|
|
13
14
|
def title
|
14
|
-
|
15
|
+
@pull_request.title
|
15
16
|
end
|
16
17
|
|
17
18
|
def from_reference
|
18
|
-
|
19
|
+
@pull_request.from_reference
|
19
20
|
end
|
20
21
|
|
21
22
|
def to_reference
|
22
|
-
|
23
|
+
@pull_request.to_reference
|
23
24
|
end
|
24
25
|
|
25
26
|
def author
|
26
|
-
|
27
|
+
@pull_request.author
|
27
28
|
end
|
28
29
|
|
29
30
|
def assignee
|
30
|
-
|
31
|
-
|
31
|
+
if @pull_request.assignee.nil? || blacklisted?(@pull_request.assignee)
|
32
|
+
return ""
|
33
|
+
else
|
34
|
+
@pull_request.assignee
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def service_id
|
35
|
-
|
39
|
+
@pull_request.service_id
|
40
|
+
end
|
41
|
+
|
42
|
+
def labels
|
43
|
+
@pull_request.labels
|
44
|
+
end
|
45
|
+
|
46
|
+
def updated_at
|
47
|
+
@pull_request.updated_at.to_time.ago.to_words
|
36
48
|
end
|
37
49
|
|
38
50
|
def assignee_blacklist
|
39
|
-
|
40
|
-
|
51
|
+
Pra.config.assignee_blacklist
|
52
|
+
end
|
53
|
+
|
54
|
+
def present(columns)
|
55
|
+
row = ""
|
56
|
+
columns.each do |column|
|
57
|
+
row << force_length(send(column[:name]), column[:size])
|
58
|
+
row << (" " * column[:padding])
|
59
|
+
end
|
60
|
+
row
|
41
61
|
end
|
42
62
|
|
43
63
|
private
|
@@ -10,10 +10,37 @@ module Pra
|
|
10
10
|
ENTER_KEY = 10
|
11
11
|
|
12
12
|
def initialize
|
13
|
-
@
|
13
|
+
@selected_pull_request_page_index = 0
|
14
|
+
@current_page = 1
|
14
15
|
@current_pull_requests = []
|
15
16
|
@previous_number_of_pull_requests = 0
|
17
|
+
@last_updated = nil
|
16
18
|
@state_lock = Mutex.new
|
19
|
+
@last_updated_access_lock = Mutex.new
|
20
|
+
@force_update = true
|
21
|
+
@force_update_access_lock = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_updated
|
25
|
+
current_last_updated = nil
|
26
|
+
@last_updated_access_lock.synchronize {
|
27
|
+
current_last_updated = @last_updated.dup
|
28
|
+
}
|
29
|
+
current_last_updated
|
30
|
+
end
|
31
|
+
|
32
|
+
def force_refresh
|
33
|
+
do_force_update = false
|
34
|
+
@force_update_access_lock.synchronize {
|
35
|
+
do_force_update = @force_update
|
36
|
+
}
|
37
|
+
do_force_update
|
38
|
+
end
|
39
|
+
|
40
|
+
def force_refresh=(force_update)
|
41
|
+
@force_update_access_lock.synchronize {
|
42
|
+
@force_update = force_update
|
43
|
+
}
|
17
44
|
end
|
18
45
|
|
19
46
|
def setup
|
@@ -29,7 +56,7 @@ module Pra
|
|
29
56
|
end
|
30
57
|
|
31
58
|
def fetch_failed
|
32
|
-
output_string(4, 0, "Failed to fetch pull requests on 1 or more pull sources. Check #{Pra::Config.
|
59
|
+
output_string(4, 0, "Failed to fetch pull requests on 1 or more pull sources. Check #{Pra::Config.log_path} for details.")
|
33
60
|
end
|
34
61
|
|
35
62
|
def refresh_pull_requests(pull_requests)
|
@@ -37,6 +64,7 @@ module Pra
|
|
37
64
|
|
38
65
|
@state_lock.synchronize {
|
39
66
|
@current_pull_requests = pull_requests.dup
|
67
|
+
@last_updated = Time.now
|
40
68
|
}
|
41
69
|
draw_current_pull_requests
|
42
70
|
end
|
@@ -51,10 +79,16 @@ module Pra
|
|
51
79
|
when 'k', Curses::Key::UP
|
52
80
|
move_selection_up
|
53
81
|
draw_current_pull_requests
|
82
|
+
when 'r'
|
83
|
+
@force_update = true
|
54
84
|
when 'o', ENTER_KEY
|
55
85
|
@state_lock.synchronize {
|
56
|
-
Launchy.open(@current_pull_requests[
|
86
|
+
Launchy.open(@current_pull_requests[selected_pull_request_loc].link)
|
57
87
|
}
|
88
|
+
when 'n'
|
89
|
+
load_next_page
|
90
|
+
when 'p'
|
91
|
+
load_prev_page
|
58
92
|
end
|
59
93
|
c = Curses.getch()
|
60
94
|
end
|
@@ -82,57 +116,125 @@ module Pra
|
|
82
116
|
end
|
83
117
|
|
84
118
|
def output_highlighted_string(row, col, str)
|
85
|
-
Curses.attron(Curses
|
119
|
+
Curses.attron(Curses::A_REVERSE) {
|
86
120
|
output_string(row, col, str)
|
87
121
|
}
|
88
122
|
end
|
89
123
|
|
90
124
|
def display_instructions
|
91
125
|
output_string(0, 0, "Pra: Helping you own pull requests")
|
92
|
-
output_string(1, 0, "quit: q, up: k|#{"\u25B2".encode("UTF-8")}, down: j|#{"\u25BC".encode("UTF-8")}, open: o|#{"\u21A9".encode("UTF-8")}")
|
126
|
+
output_string(1, 0, "quit: q, up: k|#{"\u25B2".encode("UTF-8")}, down: j|#{"\u25BC".encode("UTF-8")}, open: o|#{"\u21A9".encode("UTF-8")}, refresh: r, next page: n, prev page: p")
|
93
127
|
end
|
94
128
|
|
95
129
|
def move_selection_up
|
96
130
|
@state_lock.synchronize {
|
97
|
-
if @
|
98
|
-
@
|
131
|
+
if @selected_pull_request_page_index > 0
|
132
|
+
@selected_pull_request_page_index -= 1
|
99
133
|
end
|
100
134
|
}
|
101
135
|
end
|
102
136
|
|
103
137
|
def move_selection_down
|
104
138
|
@state_lock.synchronize {
|
105
|
-
if
|
106
|
-
@
|
139
|
+
if selected_pull_request_loc < @current_pull_requests.length - 1 &&
|
140
|
+
(LIST_START_LINE + @selected_pull_request_page_index + 1) < Curses.lines
|
141
|
+
@selected_pull_request_page_index += 1
|
107
142
|
end
|
108
143
|
}
|
109
144
|
end
|
110
145
|
|
146
|
+
def selected_pull_request_loc
|
147
|
+
(@current_page - 1) * pull_requests_per_page + @selected_pull_request_page_index
|
148
|
+
end
|
149
|
+
|
111
150
|
HEADER_LINE = 6
|
112
151
|
LIST_START_LINE = HEADER_LINE + 2
|
113
152
|
|
153
|
+
def columns
|
154
|
+
[
|
155
|
+
{ name: :repository, size: 28, padding: 2 },
|
156
|
+
{ name: :title, size: 45, padding: 2 },
|
157
|
+
{ name: :author, size: 14, padding: 2 },
|
158
|
+
{ name: :assignee, size: 14, padding: 2 },
|
159
|
+
{ name: :labels, size: 12, padding: 2 },
|
160
|
+
{ name: :updated_at, size: 16, padding: 2 }
|
161
|
+
]
|
162
|
+
end
|
163
|
+
|
164
|
+
def header_width
|
165
|
+
@header_width ||= columns.reduce(0) do |t,c|
|
166
|
+
c[:size] + c[:padding] + t
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def headers
|
171
|
+
header = ""
|
172
|
+
columns.each do |column|
|
173
|
+
header << "#{column[:name]}"
|
174
|
+
header << (" " * (column[:size] - column[:name].length))
|
175
|
+
header << " " * column[:padding]
|
176
|
+
end
|
177
|
+
|
178
|
+
header
|
179
|
+
end
|
180
|
+
|
181
|
+
def load_next_page
|
182
|
+
if @current_page + 1 <= pull_request_pages
|
183
|
+
@current_page += 1
|
184
|
+
clear_pull_requests
|
185
|
+
@selected_pull_request_page_index = 0
|
186
|
+
draw_current_pull_requests
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def load_prev_page
|
191
|
+
if @current_page - 1 > 0
|
192
|
+
@current_page -= 1
|
193
|
+
clear_pull_requests
|
194
|
+
@selected_pull_request_page_index = 0
|
195
|
+
draw_current_pull_requests
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def pull_requests_per_page
|
200
|
+
Curses.lines - LIST_START_LINE
|
201
|
+
end
|
202
|
+
|
203
|
+
def pull_request_pages
|
204
|
+
(@current_pull_requests.length.to_f/pull_requests_per_page).ceil
|
205
|
+
end
|
206
|
+
|
207
|
+
def clear_pull_requests
|
208
|
+
(LIST_START_LINE..Curses.lines).each do |i|
|
209
|
+
Curses.setpos(i, 0)
|
210
|
+
Curses.clrtoeol
|
211
|
+
end
|
212
|
+
Curses.refresh
|
213
|
+
end
|
214
|
+
|
114
215
|
def draw_current_pull_requests
|
115
216
|
@state_lock.synchronize {
|
116
|
-
output_string(3, 0, "#{@current_pull_requests.length} Pull Requests")
|
117
|
-
output_string(HEADER_LINE, 0,
|
118
|
-
output_string(HEADER_LINE + 1, 0, "
|
217
|
+
output_string(3, 0, "#{@current_pull_requests.length} Pull Requests @ #{@last_updated} : Page #{@current_page} of #{pull_request_pages}")
|
218
|
+
output_string(HEADER_LINE, 0, headers)
|
219
|
+
output_string(HEADER_LINE + 1, 0, "-" * header_width)
|
119
220
|
|
221
|
+
# clear lines that should no longer exist
|
120
222
|
if @previous_number_of_pull_requests > @current_pull_requests.length
|
121
223
|
start_line_of_left_overs = LIST_START_LINE+@current_pull_requests.length
|
122
|
-
|
123
|
-
(start_line_of_left_overs..last_line_of_left_overs).each do |i|
|
224
|
+
(start_line_of_left_overs..Curses.lines).each do |i|
|
124
225
|
Curses.setpos(i, 0)
|
125
226
|
Curses.clrtoeol
|
126
227
|
end
|
127
228
|
Curses.refresh
|
128
229
|
end
|
129
230
|
|
130
|
-
|
231
|
+
# go through and redraw all the pull requests
|
232
|
+
@current_pull_requests[(@current_page-1)*pull_requests_per_page..@current_page*pull_requests_per_page-1].each_with_index do |pull_request, index|
|
131
233
|
pull_request_presenter = Pra::CursesPullRequestPresenter.new(pull_request)
|
132
|
-
if index == @
|
133
|
-
output_highlighted_string(LIST_START_LINE + index, 0,
|
234
|
+
if index == @selected_pull_request_page_index
|
235
|
+
output_highlighted_string(LIST_START_LINE + index, 0, pull_request_presenter.present(columns))
|
134
236
|
else
|
135
|
-
output_string(LIST_START_LINE + index, 0,
|
237
|
+
output_string(LIST_START_LINE + index, 0, pull_request_presenter.present(columns))
|
136
238
|
end
|
137
239
|
end
|
138
240
|
}
|
@@ -1,43 +1,119 @@
|
|
1
1
|
require 'pra/pull_source'
|
2
2
|
require 'pra/pull_request'
|
3
|
+
require 'pra/log'
|
3
4
|
require 'json'
|
4
5
|
require 'faraday'
|
5
6
|
|
6
7
|
module Pra
|
7
8
|
class GithubPullSource < Pra::PullSource
|
9
|
+
def initialize(config = {})
|
10
|
+
@ratelimit_remaining = 5000
|
11
|
+
@ratelimit_limit = 5000
|
12
|
+
@ratelimit_reset = nil
|
13
|
+
super(config)
|
14
|
+
end
|
15
|
+
|
8
16
|
def pull_requests
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
return get_all_pull_requests
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_pull_requests
|
21
|
+
pull_requests_json = "[]"
|
22
|
+
conn = Faraday.new
|
23
|
+
conn.basic_auth(@config['username'], @config['password'])
|
24
|
+
resp = conn.get do |req|
|
25
|
+
req.url rest_api_search_issues_url
|
26
|
+
req.params['q'] = "is:pr is:open sort:updated-desc #{repos_for_query}"
|
27
|
+
req.params['per_page'] = '300'
|
28
|
+
req.headers['Content-Type'] = 'application/json'
|
29
|
+
req.headers['Accept'] = 'application/json'
|
12
30
|
end
|
13
|
-
|
31
|
+
|
32
|
+
@ratelimit_reset = Time.at(resp.headers['x-ratelimit-reset'].to_i)
|
33
|
+
@ratelimit_limit = resp.headers['x-ratelimit-limit'].to_i
|
34
|
+
@ratelimit_remaining = resp.headers['x-ratelimit-remaining'].to_i
|
35
|
+
Pra::Log.debug("Fetched pull requests and updated ratelimit tracking")
|
36
|
+
Pra::Log.debug("Ratelimit Reset: #{@ratelimit_reset}")
|
37
|
+
Pra::Log.debug("Ratelimit Limit: #{@ratelimit_limit}")
|
38
|
+
Pra::Log.debug("Ratelimit Remaining: #{@ratelimit_remaining}")
|
39
|
+
pull_requests_json = resp.body
|
40
|
+
Pra::Log.debug(pull_requests_json)
|
41
|
+
JSON.parse(pull_requests_json)
|
14
42
|
end
|
15
43
|
|
16
|
-
def
|
17
|
-
|
44
|
+
def get_all_pull_requests
|
45
|
+
pull_requests = []
|
46
|
+
|
47
|
+
pull_requests_hash = fetch_pull_requests
|
48
|
+
pull_requests_hash['items'].each do |request|
|
49
|
+
begin
|
50
|
+
org, repository = extract_repository_from_html_url(request['html_url'])
|
51
|
+
unless excluded?(org, repository)
|
52
|
+
pull_requests << Pra::PullRequest.new(title: request["title"],
|
53
|
+
from_reference: "",
|
54
|
+
to_reference: "",
|
55
|
+
author: request["user"]["login"],
|
56
|
+
assignee: request["assignee"] ? request["assignee"]["login"] : nil,
|
57
|
+
link: request['html_url'],
|
58
|
+
service_id: 'github',
|
59
|
+
repository: repository,
|
60
|
+
updated_at: request["updated_at"],
|
61
|
+
labels: request["labels"].collect{|l| l["name"]}.join(","))
|
62
|
+
end
|
63
|
+
rescue StandardError => e
|
64
|
+
Pra::Log.error("Error: #{e.to_s}")
|
65
|
+
Pra::Log.error("Request: #{request.inspect}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
pull_requests
|
18
69
|
end
|
19
70
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
71
|
+
def repos_for_query
|
72
|
+
query_params = []
|
73
|
+
repositories.each do |repo|
|
74
|
+
query_params << "repo:#{repo['owner']}/#{repo['repository']}"
|
75
|
+
end
|
76
|
+
|
77
|
+
@excluded_repos = {}
|
78
|
+
organizations.each do |org|
|
79
|
+
query_params << "org:#{org['name']}"
|
80
|
+
@excluded_repos[org['name'].downcase] = org['exclude']
|
24
81
|
end
|
25
|
-
|
82
|
+
|
83
|
+
return query_params.join(" ")
|
26
84
|
end
|
27
85
|
|
28
|
-
def
|
29
|
-
|
86
|
+
def excluded_repos
|
87
|
+
@excluded_repos || collect_exclusions
|
30
88
|
end
|
31
89
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
req.url rest_api_pull_request_url(repository_config)
|
37
|
-
req.headers['Content-Type'] = 'application/json'
|
38
|
-
req.headers['Accept'] = 'application/json'
|
90
|
+
def collect_exclusions
|
91
|
+
@exclusions = {}
|
92
|
+
organizations.each do |org|
|
93
|
+
@exclusions[org['name'].downcase] = org['exclude']
|
39
94
|
end
|
40
|
-
|
95
|
+
@exclusions
|
96
|
+
end
|
97
|
+
|
98
|
+
def excluded?(org, repository)
|
99
|
+
excluded_repos[org.downcase] && excluded_repos[org.downcase].include?(repository)
|
100
|
+
end
|
101
|
+
|
102
|
+
def extract_repository_from_html_url(html_url)
|
103
|
+
/https:\/\/github.com\/(\w+)\/([\w-]+)/.match(html_url)
|
104
|
+
return $1, $2
|
105
|
+
end
|
106
|
+
|
107
|
+
def rest_api_search_issues_url
|
108
|
+
"#{@config['protocol']}://#{@config['host']}/search/issues"
|
109
|
+
end
|
110
|
+
|
111
|
+
def repositories
|
112
|
+
@config["repositories"] || []
|
113
|
+
end
|
114
|
+
|
115
|
+
def organizations
|
116
|
+
@config["organizations"] || []
|
41
117
|
end
|
42
118
|
end
|
43
119
|
end
|
data/lib/pra/log.rb
CHANGED
@@ -1,11 +1,36 @@
|
|
1
|
+
require 'logger'
|
1
2
|
require 'pra/config'
|
2
3
|
require 'date'
|
3
4
|
|
4
5
|
module Pra
|
5
6
|
class Log
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
7
|
+
def self.logger
|
8
|
+
@logger ||= begin
|
9
|
+
logger = Logger.new(Pra::Config.log_path, 10, 5000000)
|
10
|
+
logger.formatter = proc { |severity, datetime, progname, msg|
|
11
|
+
"#{datetime.iso8601} #{severity} - #{msg}\n"
|
12
|
+
}
|
13
|
+
logger.level = Logger::INFO
|
14
|
+
logger
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.level(level)
|
19
|
+
logger.level = Logger.const_get level.upcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.info(message)
|
23
|
+
logger.info(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.debug(message)
|
27
|
+
logger.debug(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.error(message)
|
31
|
+
logger.error(message)
|
32
|
+
if message.respond_to?(:backtrace)
|
33
|
+
message.backtrace.each { |line| logger.error(line) }
|
9
34
|
end
|
10
35
|
end
|
11
36
|
end
|
data/lib/pra/pull_request.rb
CHANGED
@@ -2,7 +2,8 @@ require 'pra/config'
|
|
2
2
|
|
3
3
|
module Pra
|
4
4
|
class PullRequest
|
5
|
-
attr_accessor :title, :from_reference, :to_reference, :author, :assignee,
|
5
|
+
attr_accessor :title, :from_reference, :to_reference, :author, :assignee,
|
6
|
+
:link, :service_id, :repository, :labels, :updated_at
|
6
7
|
|
7
8
|
def initialize(attributes={})
|
8
9
|
@title = attributes[:title]
|
@@ -13,6 +14,8 @@ module Pra
|
|
13
14
|
@link = attributes[:link]
|
14
15
|
@service_id = attributes[:service_id]
|
15
16
|
@repository = attributes[:repository]
|
17
|
+
@labels = attributes[:labels] || []
|
18
|
+
@updated_at = DateTime.parse(attributes[:updated_at]) unless attributes[:updated_at].nil?
|
16
19
|
end
|
17
20
|
end
|
18
21
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'pra
|
1
|
+
require 'pra'
|
2
2
|
require 'pra/pull_source_factory'
|
3
3
|
require 'pra/pull_request_service/fetch_status'
|
4
4
|
|
@@ -18,13 +18,12 @@ module Pra
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.pull_sources
|
21
|
-
|
22
|
-
return map_config_to_pull_sources(config)
|
21
|
+
return map_config_to_pull_sources
|
23
22
|
end
|
24
23
|
|
25
|
-
def self.map_config_to_pull_sources
|
24
|
+
def self.map_config_to_pull_sources
|
26
25
|
sources = []
|
27
|
-
config.pull_sources.each do |pull_source_config|
|
26
|
+
Pra.config.pull_sources.each do |pull_source_config|
|
28
27
|
sources << Pra::PullSourceFactory.build_pull_source(pull_source_config)
|
29
28
|
end
|
30
29
|
return sources
|
data/lib/pra/version.rb
CHANGED
data/lib/pra.rb
CHANGED
data/pra.gemspec
CHANGED
@@ -21,10 +21,11 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.10"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.1"
|
24
|
-
spec.add_development_dependency "rspec", "~>
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.5"
|
25
25
|
spec.add_development_dependency "timecop", "~> 0.8"
|
26
26
|
|
27
27
|
spec.add_dependency "faraday", "~> 0.9"
|
28
28
|
spec.add_dependency "launchy", "~> 2.4"
|
29
29
|
spec.add_dependency "curses", "~> 1.0"
|
30
|
+
spec.add_dependency "time-lord", "~> 1.0"
|
30
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew De Ponte
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: timecop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '1.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: time-lord
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
111
125
|
description: Command Line utility to make you aware of open pull-requests across systems
|
112
126
|
at all times.
|
113
127
|
email:
|
@@ -133,7 +147,6 @@ files:
|
|
133
147
|
- lib/pra/config.rb
|
134
148
|
- lib/pra/curses_pull_request_presenter.rb
|
135
149
|
- lib/pra/curses_window_system.rb
|
136
|
-
- lib/pra/error_log.rb
|
137
150
|
- lib/pra/github_pull_source.rb
|
138
151
|
- lib/pra/log.rb
|
139
152
|
- lib/pra/pull_request.rb
|
data/lib/pra/error_log.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'pra/config'
|
2
|
-
require 'date'
|
3
|
-
|
4
|
-
module Pra
|
5
|
-
class ErrorLog
|
6
|
-
def self.log(error)
|
7
|
-
File.open(Pra::Config.error_log_path, 'a') do |f|
|
8
|
-
f.puts("#{DateTime.now.iso8601} - #{error.message}")
|
9
|
-
error.backtrace.each { |line| f.puts(line) }
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|