prophet 1.6.2 → 1.9.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/lib/prophet/prophet.rb +119 -75
- data/lib/prophet/railtie.rb +1 -1
- metadata +15 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afe7d79d028a1b2365986a403441c0c83ddd8833
|
4
|
+
data.tar.gz: 0819c0fd245bc51106ac93fda3a0f1669af8d8b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa4ffda35c9052a9542259acf8fdb8508126ed0e523780faa604a39ee91c459b0da5978d994011a8be3503f10dcddb68b906206180772d9cbc93461b2c12f8a0
|
7
|
+
data.tar.gz: 96c4720864de08183fc6532903b3c5da54bec83dc29274c7ba305e70c367ff3018c61b4c49c72425f3b14297a9d9e318f1a4c4d08257711eed8a263fa8e59c34
|
data/lib/prophet/prophet.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'open3'
|
3
|
+
|
1
4
|
class Prophet
|
2
5
|
|
3
6
|
attr_accessor :username,
|
@@ -15,7 +18,10 @@ class Prophet
|
|
15
18
|
:status_success,
|
16
19
|
:comment_failure,
|
17
20
|
:comment_success,
|
18
|
-
:
|
21
|
+
:disable_comments,
|
22
|
+
:reuse_comments,
|
23
|
+
:status_context,
|
24
|
+
:status_target_url
|
19
25
|
|
20
26
|
# Allow configuration blocks being passed to Prophet.
|
21
27
|
# See the README.md for examples on how to call this method.
|
@@ -42,7 +48,7 @@ class Prophet
|
|
42
48
|
begin
|
43
49
|
self.prepare_block.call
|
44
50
|
rescue Exception => e
|
45
|
-
|
51
|
+
logger.error "Preparation block raised an exception: #{e}"
|
46
52
|
end
|
47
53
|
# Loop through all 'open' pull requests.
|
48
54
|
selected_requests = pull_requests.select do |request|
|
@@ -53,30 +59,38 @@ class Prophet
|
|
53
59
|
remove_comment unless self.reuse_comments
|
54
60
|
true
|
55
61
|
end
|
62
|
+
|
56
63
|
# Run code on all selected requests.
|
57
64
|
selected_requests.each do |request|
|
58
65
|
@request = request
|
59
|
-
|
66
|
+
logger.info "Running for request ##{@request.id}."
|
60
67
|
# GitHub always creates a merge commit for its 'Merge Button'.
|
61
68
|
# Prophet reuses that commit to run the code on it.
|
62
|
-
switch_branch_to_merged_state
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
if switch_branch_to_merged_state
|
70
|
+
# Run specified code (i.e. tests) for the project.
|
71
|
+
begin
|
72
|
+
self.exec_block.call
|
73
|
+
# Unless self.success has already been set (to true/false) manually,
|
74
|
+
# the success/failure is determined by the last command's return code.
|
75
|
+
self.success = ($CHILD_STATUS && $CHILD_STATUS.exitstatus == 0) if self.success.nil?
|
76
|
+
rescue Exception => e
|
77
|
+
logger.error "Execution block raised an exception: #{e}"
|
78
|
+
self.success = false
|
79
|
+
end
|
80
|
+
switch_branch_back
|
81
|
+
comment_on_github
|
82
|
+
set_status_on_github
|
72
83
|
end
|
73
|
-
switch_branch_back
|
74
|
-
comment_on_github
|
75
|
-
set_status_on_github
|
76
84
|
self.success = nil
|
77
85
|
end
|
78
86
|
end
|
79
87
|
|
88
|
+
def logger
|
89
|
+
@logger ||= Logger.new(STDOUT).tap do |log|
|
90
|
+
log.level = Logger::INFO
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
80
94
|
|
81
95
|
private
|
82
96
|
|
@@ -86,13 +100,6 @@ class Prophet
|
|
86
100
|
end
|
87
101
|
|
88
102
|
def configure
|
89
|
-
# Use existing logger or fall back to a new one with standard log level.
|
90
|
-
if self.logger
|
91
|
-
@log = self.logger
|
92
|
-
else
|
93
|
-
@log = Logger.new(STDOUT)
|
94
|
-
@log.level = Logger::INFO
|
95
|
-
end
|
96
103
|
# Set default fall back values for options that aren't set.
|
97
104
|
self.username ||= git_config['github.login']
|
98
105
|
self.password ||= git_config['github.password']
|
@@ -101,12 +108,14 @@ class Prophet
|
|
101
108
|
self.rerun_on_source_change = true if self.rerun_on_source_change.nil?
|
102
109
|
self.rerun_on_target_change = true if self.rerun_on_target_change.nil?
|
103
110
|
self.reuse_comments = false if self.reuse_comments.nil?
|
111
|
+
self.disable_comments = false if self.disable_comments.nil?
|
104
112
|
# Allow for custom messages.
|
105
113
|
self.status_pending ||= 'Prophet is still running.'
|
106
114
|
self.status_failure ||= 'Prophet reports failure.'
|
107
115
|
self.status_success ||= 'Prophet reports success.'
|
108
116
|
self.comment_failure ||= 'Prophet reports failure.'
|
109
117
|
self.comment_success ||= 'Prophet reports success.'
|
118
|
+
self.status_context ||= 'prophet/default'
|
110
119
|
# Find environment (tasks, project, ...).
|
111
120
|
self.prepare_block ||= lambda {}
|
112
121
|
self.exec_block ||= lambda { `rake` }
|
@@ -125,7 +134,7 @@ class Prophet
|
|
125
134
|
)
|
126
135
|
# Check user login to GitHub.
|
127
136
|
github.login
|
128
|
-
|
137
|
+
logger.info "Successfully logged into GitHub with user '#{user}'."
|
129
138
|
# Ensure the user has access to desired project.
|
130
139
|
# NOTE: All three variants should work:
|
131
140
|
# 'ssh://git@github.com:user/project.git'
|
@@ -134,20 +143,20 @@ class Prophet
|
|
134
143
|
@project ||= /github\.com[\/:](.*)\.git$/.match(git_config['remote.origin.url'])[1]
|
135
144
|
begin
|
136
145
|
github.repo @project
|
137
|
-
|
146
|
+
logger.info "Successfully accessed GitHub project '#{@project}'"
|
138
147
|
github
|
139
148
|
rescue Octokit::Unauthorized => e
|
140
|
-
|
149
|
+
logger.error "Unable to access GitHub project with user '#{user}':\n#{e.message}"
|
141
150
|
abort
|
142
151
|
end
|
143
152
|
end
|
144
153
|
|
145
154
|
def pull_requests
|
146
|
-
request_list = @github.pulls @project, 'open'
|
155
|
+
request_list = @github.pulls @project, state: 'open'
|
147
156
|
requests = request_list.collect do |request|
|
148
157
|
PullRequest.new(@github.pull_request @project, request.number)
|
149
158
|
end
|
150
|
-
|
159
|
+
logger.info "Found #{requests.size > 0 ? requests.size : 'no'} open pull requests in '#{@project}'."
|
151
160
|
requests
|
152
161
|
end
|
153
162
|
|
@@ -156,72 +165,85 @@ class Prophet
|
|
156
165
|
# - the pull request has been updated since the last run.
|
157
166
|
# - the target (i.e. master) has been updated since the last run.
|
158
167
|
def run_necessary?
|
159
|
-
|
168
|
+
logger.info "Checking pull request ##{@request.id}: #{@request.content.title}"
|
160
169
|
# Compare current sha ids of target and source branch with those from the last test run.
|
161
170
|
@request.target_head_sha = @github.commits(@project).first.sha
|
162
171
|
comments = @github.issue_comments(@project, @request.id)
|
163
172
|
comments = comments.select { |c| [username, username_fail].include?(c.user.login) }.reverse
|
164
|
-
# Initialize shas to ensure it will live on after the 'each' block.
|
165
|
-
shas = nil
|
166
|
-
@request.comment = nil
|
167
173
|
comments.each do |comment|
|
168
|
-
|
169
|
-
if shas && shas[1] && shas[2]
|
170
|
-
# Remember comment to be able to update or delete it later.
|
171
|
-
@request.comment = comment
|
172
|
-
break
|
173
|
-
end
|
174
|
+
@request.comment = comment if /Merged ([\w]+) into ([\w]+)/.match(comment.body)
|
174
175
|
end
|
175
|
-
|
176
|
-
|
176
|
+
|
177
|
+
statuses = @github.status(@project, @request.head_sha).statuses.select { |s| s.context == self.status_context }
|
178
|
+
# Only run if it's mergeable.
|
179
|
+
if @request.content.mergeable
|
180
|
+
if statuses.empty?
|
181
|
+
# If there is no status yet, it has to be a new request.
|
182
|
+
logger.info 'New pull request detected, run needed.'
|
183
|
+
return true
|
184
|
+
elsif !self.disable_comments && !@request.comment
|
185
|
+
logger.info 'Rerun forced.'
|
186
|
+
return true
|
187
|
+
end
|
188
|
+
else
|
177
189
|
# Sometimes GitHub doesn't have a proper boolean value stored.
|
178
|
-
if @request.content.mergeable.nil? && switch_branch_to_merged_state
|
190
|
+
if @request.content.mergeable.nil? && switch_branch_to_merged_state
|
179
191
|
# Pull request is mergeable after all.
|
180
192
|
switch_branch_back
|
181
193
|
else
|
182
|
-
|
194
|
+
logger.info 'Pull request not auto-mergeable. Not running.'
|
183
195
|
if @request.comment
|
184
|
-
|
196
|
+
logger.info 'Deleting existing comment.'
|
185
197
|
call_github(old_comment_success?).delete_comment(@project, @request.comment.id)
|
186
198
|
end
|
199
|
+
create_status(:error, "Pull request not auto-mergeable. Not running.") if statuses.first && statuses.first.state != 'error'
|
187
200
|
return false
|
188
201
|
end
|
189
202
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
203
|
+
|
204
|
+
# Initialize shas to ensure it will live on after the 'each' block.
|
205
|
+
shas = nil
|
206
|
+
statuses.each do |status|
|
207
|
+
shas = /Merged ([\w]+) into ([\w]+)/.match(status.description)
|
208
|
+
break if shas && shas[1] && shas[2]
|
209
|
+
end
|
210
|
+
|
211
|
+
if shas
|
212
|
+
logger.info "Current target sha: '#{@request.target_head_sha}', pull sha: '#{@request.head_sha}'."
|
213
|
+
logger.info "Last test run target sha: '#{shas[2]}', pull sha: '#{shas[1]}'."
|
193
214
|
if self.rerun_on_source_change && (shas[1] != @request.head_sha)
|
194
|
-
|
215
|
+
logger.info 'Re-running due to new commit in pull request.'
|
195
216
|
return true
|
196
217
|
elsif self.rerun_on_target_change && (shas[2] != @request.target_head_sha)
|
197
|
-
|
198
|
-
|
218
|
+
logger.info 'Re-running due to new commit in target branch.'
|
219
|
+
return true
|
199
220
|
end
|
200
221
|
else
|
201
|
-
# If there are no
|
202
|
-
|
222
|
+
# If there are no SHAs yet, it has to be a new request.
|
223
|
+
logger.info 'New pull request detected, run needed.'
|
203
224
|
return true
|
204
225
|
end
|
205
|
-
|
226
|
+
|
227
|
+
logger.info "Not running for request ##{@request.id}."
|
206
228
|
false
|
207
229
|
end
|
208
230
|
|
209
|
-
def switch_branch_to_merged_state
|
231
|
+
def switch_branch_to_merged_state
|
210
232
|
# Fetch the merge-commit for the pull request.
|
211
233
|
# NOTE: This commit is automatically created by 'GitHub Merge Button'.
|
212
|
-
# FIXME: Use cheetah to pipe to
|
234
|
+
# FIXME: Use cheetah to pipe to logger.debug instead of that /dev/null hack.
|
213
235
|
`git fetch origin refs/pull/#{@request.id}/merge: &> /dev/null`
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
236
|
+
_output, status = Open3.capture2 'git checkout FETCH_HEAD &> /dev/null'
|
237
|
+
if status != 0
|
238
|
+
logger.error 'Unable to switch to merge branch.'
|
239
|
+
return false
|
218
240
|
end
|
219
241
|
true
|
220
242
|
end
|
221
243
|
|
222
244
|
def switch_branch_back
|
223
|
-
# FIXME: Use cheetah to pipe to
|
224
|
-
|
245
|
+
# FIXME: Use cheetah to pipe to logger.debug instead of that /dev/null hack.
|
246
|
+
logger.info 'Switching back to original branch.'
|
225
247
|
# FIXME: For branches other than master, remember the original branch.
|
226
248
|
`git checkout master &> /dev/null`
|
227
249
|
# Clean up potential remains and run garbage collector.
|
@@ -243,33 +265,60 @@ class Prophet
|
|
243
265
|
end
|
244
266
|
|
245
267
|
def comment_on_github
|
268
|
+
return if self.disable_comments
|
269
|
+
|
246
270
|
# Determine comment message.
|
247
271
|
message = if self.success
|
248
|
-
|
249
|
-
self.comment_success
|
272
|
+
logger.info 'Successful run.'
|
273
|
+
self.comment_success
|
250
274
|
else
|
251
|
-
|
252
|
-
self.comment_failure
|
275
|
+
logger.info 'Failing run.'
|
276
|
+
self.comment_failure
|
253
277
|
end
|
254
|
-
message +=
|
278
|
+
message += status_string
|
255
279
|
if self.reuse_comments && old_comment_success? == self.success
|
256
280
|
# Replace existing comment's body with the correct connection.
|
257
|
-
|
281
|
+
logger.info "Updating existing #{notion(self.success)} comment."
|
258
282
|
call_github(self.success).update_comment(@project, @request.comment.id, message)
|
259
283
|
else
|
260
284
|
if @request.comment
|
261
|
-
|
285
|
+
logger.info "Deleting existing #{notion(!self.success)} comment."
|
262
286
|
# Delete old comment with correct connection (if @request.comment exists).
|
263
287
|
call_github(!self.success).delete_comment(@project, @request.comment.id)
|
264
288
|
end
|
265
289
|
# Create new comment with correct connection.
|
266
|
-
|
290
|
+
logger.info "Adding new #{notion(self.success)} comment."
|
267
291
|
call_github(self.success).add_comment(@project, @request.id, message)
|
268
292
|
end
|
269
293
|
end
|
270
294
|
|
295
|
+
def status_string
|
296
|
+
case self.success
|
297
|
+
when true
|
298
|
+
" (Merged #{@request.head_sha} into #{@request.target_head_sha})"
|
299
|
+
when false
|
300
|
+
" (Merged #{@request.head_sha} into #{@request.target_head_sha})"
|
301
|
+
else
|
302
|
+
""
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def create_status(state, description)
|
307
|
+
logger.info "Setting status '#{state}': '#{description}'"
|
308
|
+
@github.create_status(
|
309
|
+
@project,
|
310
|
+
@request.head_sha,
|
311
|
+
state,
|
312
|
+
{
|
313
|
+
"description" => description,
|
314
|
+
"context" => status_context,
|
315
|
+
"target_url" => self.status_target_url
|
316
|
+
}
|
317
|
+
)
|
318
|
+
end
|
319
|
+
|
271
320
|
def set_status_on_github
|
272
|
-
|
321
|
+
logger.info 'Updating status on GitHub.'
|
273
322
|
case self.success
|
274
323
|
when true
|
275
324
|
state_symbol = :success
|
@@ -281,12 +330,7 @@ class Prophet
|
|
281
330
|
state_symbol = :pending
|
282
331
|
state_message = self.status_pending
|
283
332
|
end
|
284
|
-
|
285
|
-
"repos/#{@project}/statuses/#{@request.head_sha}", {
|
286
|
-
:state => state_symbol,
|
287
|
-
:description => state_message
|
288
|
-
}
|
289
|
-
)
|
333
|
+
create_status(state_symbol, state_message + status_string)
|
290
334
|
end
|
291
335
|
|
292
336
|
def notion(success)
|
data/lib/prophet/railtie.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prophet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Bamberger
|
@@ -10,50 +10,50 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-03-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: faraday_middleware
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 0.11.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.
|
28
|
+
version: 0.11.0
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: faraday
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version: 0.
|
35
|
+
version: 0.11.0
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: 0.
|
42
|
+
version: 0.11.0
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: octokit
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
49
|
+
version: '4.0'
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
56
|
+
version: '4.0'
|
57
57
|
description: Prophet runs custom code (i.e. your project's test suite) on open pull
|
58
58
|
requests on GitHub. Afterwards it posts the result as a comment to the respective
|
59
59
|
request. This should give you an outlook on the future state of your repository
|
@@ -89,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
89
|
version: '0'
|
90
90
|
requirements: []
|
91
91
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.
|
92
|
+
rubygems_version: 2.6.8
|
93
93
|
signing_key:
|
94
94
|
specification_version: 4
|
95
95
|
summary: An easy way to loop through open pull requests and run code onthe merged
|