omg_pull_request 0.0.6
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.
- data/.gitignore +47 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +68 -0
- data/LICENSE +22 -0
- data/README.md +66 -0
- data/Rakefile +12 -0
- data/bin/omg_pull_request +13 -0
- data/lib/omg_pull_request.rb +32 -0
- data/lib/omg_pull_request/aws/store.rb +30 -0
- data/lib/omg_pull_request/configuration.rb +55 -0
- data/lib/omg_pull_request/context.rb +29 -0
- data/lib/omg_pull_request/git_client.rb +31 -0
- data/lib/omg_pull_request/github_wrapper.rb +50 -0
- data/lib/omg_pull_request/lolcommits.rb +39 -0
- data/lib/omg_pull_request/notifications.rb +66 -0
- data/lib/omg_pull_request/prowl.rb +77 -0
- data/lib/omg_pull_request/test_logger.rb +20 -0
- data/lib/omg_pull_request/test_runner.rb +22 -0
- data/lib/omg_pull_request/test_runner/base.rb +128 -0
- data/lib/omg_pull_request/test_runner/rails.rb +19 -0
- data/lib/omg_pull_request/test_runner/ruby.rb +9 -0
- data/lib/omg_pull_request/version.rb +3 -0
- data/locales/en.yml +23 -0
- data/locales/omg.yml +20 -0
- data/omg_pull_request.gemspec +26 -0
- data/test/fixtures/comments +431 -0
- data/test/fixtures/config.yml +20 -0
- data/test/fixtures/create_comment +24 -0
- data/test/fixtures/github_commits +183 -0
- data/test/fixtures/kenmazaika +30 -0
- data/test/fixtures/pull_requests +151 -0
- data/test/mocks/git_client.rb +6 -0
- data/test/mocks/pull_request.rb +38 -0
- data/test/omg_pull_request/.gitkeep +0 -0
- data/test/test_helper.rb +53 -0
- data/test/units/omg_pull_request/aws/store_test.rb +22 -0
- data/test/units/omg_pull_request/github_wrapper_test.rb +43 -0
- data/test/units/omg_pull_request/lolcommits_test.rb +42 -0
- data/test/units/omg_pull_request/notifications_test.rb +125 -0
- data/test/units/omg_pull_request/prowl_test.rb +51 -0
- data/test/units/omg_pull_request/test_runner/base_test.rb +58 -0
- metadata +183 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
class Lolcommits
|
3
|
+
LOLCOMMITS_PATH = '/animated_gifs'
|
4
|
+
LOLCOMMITS_URL = "http://www.lolcommits.com"
|
5
|
+
attr_accessor :configuration, :github_wrapper, :runner, :context
|
6
|
+
|
7
|
+
def initialize(attributes=Hash.new)
|
8
|
+
attributes.each do |attr, value|
|
9
|
+
self.send("#{attr}=", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def animation_url
|
14
|
+
return @animation_url if @animation_url
|
15
|
+
commits = github_wrapper.commit_shas(runner.pull_request) -
|
16
|
+
context.get_animated_shas(runner.pull_request.number)
|
17
|
+
context.add_animated_shas(runner.pull_request.number, commits)
|
18
|
+
|
19
|
+
@animation_url ||= get_animation_url(commits)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def get_animation_url(shas)
|
25
|
+
unless shas.blank?
|
26
|
+
response = lolcommits_connection.post LOLCOMMITS_PATH, :animated_gif => { :shas => shas.join(',') }
|
27
|
+
JSON.parse(response.body).fetch("image").fetch("url") if response.status == 200
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def lolcommits_connection
|
32
|
+
@lolcommits_connection ||= Faraday.new(:url => LOLCOMMITS_URL) do |f|
|
33
|
+
f.request :url_encoded
|
34
|
+
f.adapter Faraday.default_adapter
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
module Notifications
|
3
|
+
protected
|
4
|
+
# GITHUB
|
5
|
+
def make_comment_success!(output_file)
|
6
|
+
github_comment(t("completed.github.success", runner_hash(:output_file => output_file)))
|
7
|
+
end
|
8
|
+
|
9
|
+
def make_comment_failure!(output_file)
|
10
|
+
github_comment(t("completed.github.failure", runner_hash(:output_file => output_file)))
|
11
|
+
end
|
12
|
+
|
13
|
+
def make_comment_conflict!
|
14
|
+
github_comment(t("error.github.conflict", runner_hash))
|
15
|
+
self.log(t("error.github.conflict", runner_hash))
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_comment_test_running!
|
19
|
+
lol_url = lolcommits_client.animation_url
|
20
|
+
message = t("start.github.message", runner_hash)
|
21
|
+
message = t("start.github.lolcommits.message", runner_hash(:animation_url => lol_url)) if lol_url
|
22
|
+
github_comment(message)
|
23
|
+
end
|
24
|
+
|
25
|
+
# PROWL
|
26
|
+
def prowl_alert_success!
|
27
|
+
message = t("completed.prowl.success", runner_hash)
|
28
|
+
self.prowl_client.alert_all_relevant_people!(message, :success)
|
29
|
+
end
|
30
|
+
|
31
|
+
def prowl_alert_failure!
|
32
|
+
message = t("completed.prowl.failure", runner_hash)
|
33
|
+
self.prowl_client.alert_author!(message, :failure)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def process_output!
|
38
|
+
output_file = store_logger_data!
|
39
|
+
if self.success?
|
40
|
+
make_comment_success!(output_file)
|
41
|
+
prowl_alert_success!
|
42
|
+
else
|
43
|
+
make_comment_failure!(output_file)
|
44
|
+
prowl_alert_failure!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def github_comment(message)
|
49
|
+
GITHUB_WRAPPER.make_comment(self.issue_number, message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def runner_hash(also=Hash.new)
|
53
|
+
also.merge({
|
54
|
+
:abbr_from_sha => abbr_from_sha,
|
55
|
+
:abbr_to_sha => abbr_to_sha,
|
56
|
+
:minutes => runtime_minutes,
|
57
|
+
:seconds => runtime_seconds,
|
58
|
+
:issue_number => issue_number,
|
59
|
+
:title => pull_request.title
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
public
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
class Prowl
|
3
|
+
PROWL_URL = 'https://api.prowlapp.com'
|
4
|
+
PROWL_PATH = '/publicapi/add'
|
5
|
+
|
6
|
+
attr_accessor :configuration, :logger, :runner, :github_wrapper
|
7
|
+
|
8
|
+
def initialize(attributes=Hash.new)
|
9
|
+
attributes.each do |attr, value|
|
10
|
+
self.send("#{attr}=", value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def notify_users(msg, keys=[], options={})
|
15
|
+
return if keys.blank?
|
16
|
+
|
17
|
+
opts = {
|
18
|
+
:application => prowl_application,
|
19
|
+
:event => "notification",
|
20
|
+
:description => msg
|
21
|
+
}.merge( options )
|
22
|
+
|
23
|
+
keys.each do |key|
|
24
|
+
open_connection.post PROWL_PATH, opts.merge(:apikey => key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def alert_all_relevant_people!(message, event)
|
29
|
+
notify_users(message, all_keys, {
|
30
|
+
:url => runner.pull_request.html_url,
|
31
|
+
:event => event
|
32
|
+
} )
|
33
|
+
end
|
34
|
+
|
35
|
+
def alert_author!(message, event)
|
36
|
+
notify_users(message, author_keys, {
|
37
|
+
:url => runner.pull_request.html_url,
|
38
|
+
:event => event
|
39
|
+
})
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def keys_for_users(users=[])
|
45
|
+
users.collect { |u| (prowl_keys || {})[u] }.compact.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
def author_keys
|
49
|
+
author_logins = github_wrapper.author_logins(runner.pull_request)
|
50
|
+
keys_for_users(author_logins)
|
51
|
+
end
|
52
|
+
|
53
|
+
def all_keys
|
54
|
+
logins = github_wrapper.all_logins(runner.pull_request) +
|
55
|
+
runner.git_client.committers(runner.from_sha, runner.to_sha)
|
56
|
+
|
57
|
+
keys_for_users(logins)
|
58
|
+
end
|
59
|
+
|
60
|
+
def open_connection
|
61
|
+
@open_connection ||= Faraday.new(:url => PROWL_URL) do |f|
|
62
|
+
f.request :url_encoded
|
63
|
+
f.adapter Faraday.default_adapter
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
extend Configuration::Helpers
|
68
|
+
delegate_config_to(:configuration, :prowl)
|
69
|
+
|
70
|
+
[:application, :keys].each do |attr|
|
71
|
+
define_method "prowl_#{attr}" do
|
72
|
+
(prowl || Hash.new)[attr.to_s]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
class TestLogger
|
3
|
+
attr_accessor :store, :buffer
|
4
|
+
def initialize(attributes={})
|
5
|
+
attributes.each do |attr, value|
|
6
|
+
self.send("#{attr}=", value)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def log(output)
|
11
|
+
puts output
|
12
|
+
self.buffer = "#{buffer}\n#{output}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def store_logs!(file_name)
|
16
|
+
self.store.store(StringIO.new(buffer), file_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
module TestRunner
|
3
|
+
def self.start_daemon
|
4
|
+
while(true)
|
5
|
+
begin
|
6
|
+
GITHUB_WRAPPER.pull_requests.each do |pr|
|
7
|
+
runner = CONFIGURATION.runner_class.new(:configuration => CONFIGURATION, :pull_request => pr)
|
8
|
+
next if CONTEXT.ran?(runner.request_sha)
|
9
|
+
CONTEXT.ran(runner.request_sha)
|
10
|
+
|
11
|
+
runner.run
|
12
|
+
end
|
13
|
+
|
14
|
+
rescue => ex
|
15
|
+
puts "An Error Occured: #{ex.inspect}\n#{ex.backtrace}"
|
16
|
+
end
|
17
|
+
|
18
|
+
sleep(60)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
module TestRunner
|
3
|
+
class Base
|
4
|
+
attr_accessor :success, :runtime, :pull_request, :configuration
|
5
|
+
include Notifications
|
6
|
+
|
7
|
+
def initialize(attributes={})
|
8
|
+
attributes.each do |attr, value|
|
9
|
+
self.send("#{attr}=", value)
|
10
|
+
end
|
11
|
+
self.success = false
|
12
|
+
end
|
13
|
+
|
14
|
+
[:setup, :execute_tests, :teardown].each do |method|
|
15
|
+
define_method method do; true; end;
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
log_test_details!
|
20
|
+
make_comment_test_running!
|
21
|
+
|
22
|
+
git_client.checkout!(from_sha)
|
23
|
+
merge_response = git_client.merge!(to_sha)
|
24
|
+
|
25
|
+
if merge_response == :conflict
|
26
|
+
make_comment_conflict!
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
Bundler.with_original_env do
|
31
|
+
setup
|
32
|
+
|
33
|
+
t = Time.now
|
34
|
+
self.success = execute_tests
|
35
|
+
self.runtime = Time.now - t
|
36
|
+
|
37
|
+
teardown
|
38
|
+
end
|
39
|
+
|
40
|
+
process_output!
|
41
|
+
end
|
42
|
+
|
43
|
+
def success?
|
44
|
+
self.success
|
45
|
+
end
|
46
|
+
|
47
|
+
def runtime_minutes
|
48
|
+
(runtime.to_f / 60).to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
def runtime_seconds
|
52
|
+
(runtime.to_f % 60).round(3)
|
53
|
+
end
|
54
|
+
|
55
|
+
def from_sha
|
56
|
+
pull_request.base.sha
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_sha
|
60
|
+
pull_request.head.sha
|
61
|
+
end
|
62
|
+
|
63
|
+
def request_sha
|
64
|
+
"#{self.from_sha}:#{self.to_sha}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def abbr_from_sha
|
68
|
+
self.from_sha[0, 8]
|
69
|
+
end
|
70
|
+
|
71
|
+
def abbr_to_sha
|
72
|
+
self.to_sha[0, 8]
|
73
|
+
end
|
74
|
+
|
75
|
+
def issue_number
|
76
|
+
pull_request.number
|
77
|
+
end
|
78
|
+
|
79
|
+
def logger
|
80
|
+
@logger ||= TestLogger.new(:store => STORE)
|
81
|
+
end
|
82
|
+
|
83
|
+
def prowl_client
|
84
|
+
@prowl_client ||= Prowl.new(:configuration => CONFIGURATION, :logger => logger, :runner => self, :github_wrapper => GITHUB_WRAPPER)
|
85
|
+
end
|
86
|
+
|
87
|
+
def lolcommits_client
|
88
|
+
@lolcommits ||= Lolcommits.new(:configuration => CONFIGURATION, :github_wrapper => GITHUB_WRAPPER, :runner => self, :context => CONTEXT)
|
89
|
+
end
|
90
|
+
|
91
|
+
def git_client
|
92
|
+
@git_client ||= GitClient.new(:logger => logger, :configuration => CONFIGURATION)
|
93
|
+
end
|
94
|
+
|
95
|
+
def notifier
|
96
|
+
@notifier ||= Notifier.new(:runner => self, :github_wrapper => GITHUB_WRAPPER)
|
97
|
+
end
|
98
|
+
|
99
|
+
def log(message)
|
100
|
+
logger.log(message)
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
# execute shell command, return true if success
|
105
|
+
def execute!(command)
|
106
|
+
log `#{command}`
|
107
|
+
exit_code = $?.to_i
|
108
|
+
success = exit_code == 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def log_test_details!
|
112
|
+
log t("start.banner", runner_hash(:version => VERSION))
|
113
|
+
end
|
114
|
+
|
115
|
+
def store_logger_data!
|
116
|
+
logger.store_logs!("#{abbr_from_sha}-#{abbr_to_sha}-#{Time.now.to_i}.txt")
|
117
|
+
end
|
118
|
+
|
119
|
+
extend Configuration::Helpers
|
120
|
+
delegate_config_to(:configuration, :local_repo, :database_yml)
|
121
|
+
|
122
|
+
def t(key, options=Hash.new)
|
123
|
+
I18n.t(key, options)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module OmgPullRequest
|
2
|
+
module TestRunner
|
3
|
+
class Rails < TestRunner::Base
|
4
|
+
def setup
|
5
|
+
execute!("cd #{local_repo} && cp #{database_yml} config/database.yml && bundle")
|
6
|
+
execute!("cd #{local_repo} && bundle exec rake db:drop:all && rake db:create:all && bundle exec rake db:schema:load")
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute_tests
|
10
|
+
return execute!("cd #{local_repo} && bundle exec rake")
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
execute!("rm #{local_repo}/log/test.log")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
en:
|
2
|
+
error:
|
3
|
+
github:
|
4
|
+
conflict: "### Unable To Run \nA conflict prevented the test suite from being run. Please manually resolve the conflict and the tests will be rerun.\nFrom `%{abbr_from_sha}` to `%{abbr_to_sha}`"
|
5
|
+
|
6
|
+
start:
|
7
|
+
banner: "OmgPullRequest %{version}\n
|
8
|
+
From: %{abbr_from_sha}\n
|
9
|
+
To: %{abbr_to_sha}\n
|
10
|
+
Issue Number: %{issue_number}"
|
11
|
+
github:
|
12
|
+
message: ":trollface: Running tests: `%{abbr_from_sha}` to `%{abbr_to_sha}`"
|
13
|
+
lolcommits:
|
14
|
+
message: ":trollface: Running tests: `%{abbr_from_sha}` to `%{abbr_to_sha}\n\nThis is what hard work looks like\n"
|
15
|
+
|
16
|
+
completed:
|
17
|
+
github:
|
18
|
+
success: ":thumbsup: :shipit: \n### Tests Passed \nFrom `%{abbr_from_sha}` to `%{abbr_to_sha}`\nTests took %{minutes} minutes, %{seconds} seconds.\n[results](%{output_file})"
|
19
|
+
failure: ":thumbsdown: :fire: :broken_heart: \n### Tests Failed \n `%{abbr_from_sha}` to `%{abbr_to_sha}`\nTests too %{minutes} minutes, %{seconds} seconds.\n[results](%{output_file})"
|
20
|
+
prowl:
|
21
|
+
success: "Thumbs up, bro.\nPull request #%{issue_number}\n%{title}\nTests took %{minutes} minutes, %{seconds} seconds."
|
22
|
+
failure: "Sad face to the max, homie.\nPull request #%{issue_number}\n%{title}\nTests took %{minutes} minutes, %{seconds} seconds."
|
23
|
+
|
data/locales/omg.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
omg:
|
2
|
+
error:
|
3
|
+
github:
|
4
|
+
conflict: "OMG"
|
5
|
+
|
6
|
+
start:
|
7
|
+
banner: "OMG"
|
8
|
+
github:
|
9
|
+
message: "OMG"
|
10
|
+
lolcommits:
|
11
|
+
message: "OMG"
|
12
|
+
|
13
|
+
completed:
|
14
|
+
github:
|
15
|
+
success: "OMG"
|
16
|
+
failure: "OMG"
|
17
|
+
prowl:
|
18
|
+
success: "OMG"
|
19
|
+
failure: "OMG"
|
20
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/omg_pull_request/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ken Mazaika", "Jean Charles Sisk", "Master William Desmarais", "Ian Asaff"]
|
6
|
+
gem.email = ["kenmazaika@gmail.com"]
|
7
|
+
gem.description = %q{Have tests run automatically for your Github Pull Request}
|
8
|
+
gem.summary = %q{Have tests run automatically for your Github Pull Request}
|
9
|
+
gem.homepage = "http://github.com/where/omg_pull_request"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "omg_pull_request"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = OmgPullRequest::VERSION
|
17
|
+
|
18
|
+
gem.add_runtime_dependency(%q<aws-s3>, ">= 0.6.3")
|
19
|
+
gem.add_runtime_dependency(%q<faraday>, ">= 0.8.4")
|
20
|
+
gem.add_runtime_dependency(%q<github_api>, ">= 0.6.5")
|
21
|
+
gem.add_runtime_dependency(%q<uuid>, ">= 2.3.5")
|
22
|
+
gem.add_runtime_dependency(%q<rake>, ">= 0.9.2.2")
|
23
|
+
gem.add_runtime_dependency(%q<activesupport>, ">= 3.2.8")
|
24
|
+
gem.add_runtime_dependency(%q<i18n>, ">= 0.6.0")
|
25
|
+
|
26
|
+
end
|