This repository has been archived by the owner on Nov 17, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsitonit.rb
149 lines (134 loc) · 5.16 KB
/
sitonit.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
require 'sinatra'
require 'json'
require 'octokit'
require 'date'
require 'yaml'
require 'base64'
require 'openssl'
require 'jwt'
use Rack::Session::Cookie, :secret => rand.to_s()
set :port, ENV['PORT'].to_i
APP_ID = ENV['SITONIT_APP_ID']
CLIENT_ID = ENV['SITONIT_CLIENT_ID']
CLIENT_SECRET = ENV['SITONIT_CLIENT_SECRET']
PRIVATE_KEY = OpenSSL::PKey::RSA.new(ENV['SITONIT_PRIVATE_KEY'])
# Default time until mergable in days
TIME_TO_MERGE_DAYS = 1.0
GLOBAL = {:running => true}
Signal.trap('INT') {
GLOBAL[:running] = false
puts 'Exiting...'
exit
}
Signal.trap('TERM') {
GLOBAL[:running] = false
puts 'Exiting...'
exit
}
before do
# init here
end
get '/' do
redirect 'https://github.com/cmccandless/sitonit'
end
post '/event_handler' do
@payload = JSON.parse(request.body.read.to_s)
case request.env['HTTP_X_GITHUB_EVENT']
when "pull_request"
case @payload["action"]
when "opened", "synchronize"
t = Thread.new {
process_pull_request(@payload["pull_request"], @payload["installation"])
}
end
end
end
helpers do
def github_client(installation_id)
# TODO: drop this header once GitHub Integrations are officially released.
accept = 'application/vnd.github.machine-man-preview+json'
# Use a temporary JWT to get an access token, scoped to the integration's installation.
headers = {'Authorization' => "Bearer #{new_jwt_token}", 'Accept' => accept}
access_tokens_url = "/installations/#{installation_id}/access_tokens"
access_tokens_response = Octokit::Client.new.post(access_tokens_url, headers: headers)
access_token = access_tokens_response[:token]
Octokit::Client.new(access_token: access_token)
end
# Generate the JWT required for the initial GitHub Integrations API handshake.
# https://developer.github.com/early-access/integrations/authentication/#as-an-integration
def new_jwt_token
payload = {
iat: Time.now.to_i, # Issued at time.
exp: (10 * 60) + Time.now.to_i, # JWT expiration time.
iss: APP_ID # Integration's GitHub identifier.
}
JWT.encode(payload, PRIVATE_KEY, 'RS256')
end
def process_pull_request(pull_request, installation)
# puts "Processing pull request..."
@client = github_client(installation["id"])
context = "continuous-integration/SitOnIt"
repo = pull_request['base']['repo']['full_name']
ref = pull_request['head']['sha']
loop do
created = DateTime.strptime(pull_request['created_at'])
listing = @client.contents(repo, :ref => ref).collect { |f| f[:name] }
target = created
merge_on_fail = true
if listing.include?('.sitonit.yml')
contents = @client.contents(repo, :ref => ref, :path => '.sitonit.yml')
body = contents[:content]
case contents[:encoding]
when 'base64'
body = Base64.decode64(body)
else
puts "unknown encoding #{contents[:encoding]}"
end
config = YAML.load(body)
use_default_time = true
if ['minute', 'minutes', 'hour', 'hours', 'day', 'days'].any?(&config.method(:has_key?))
days = (
(
(config.fetch('minutes', config.fetch('minute', 0)).to_f / 60.0) +
config.fetch('hours', config.fetch('hour', 0)).to_f
) / 24.0 +
config.fetch('days', config.fetch('day', 0)).to_f
)
else
days = TIME_TO_MERGE_DAYS
end
days = 0 if days < 0
created = DateTime.strptime(pull_request['updated_at']) if config.fetch('reset_on_update', false)
target = created + days
merge_on_fail = config.fetch('merge_on_fail', true)
else
target = created + TIME_TO_MERGE_DAYS
end
time_needed = target - DateTime.now
break if time_needed <= 0
units = "days"
if time_needed < 1
time_needed *= 24
units = "hours"
end
if time_needed < 1
time_needed *= 60
units = "minutes"
end
description = "This PR needs #{time_needed.round(0)} more #{units}."
@client.create_status(repo, ref, 'pending', :context => context, :description => description)
# puts "#{repo}/#{pull_request['head']['sha']}: #{description}"
if GLOBAL[:running]
puts 'sleeping for 30s...'
else
break
end
sleep(30)
end
if GLOBAL[:running] or merge_on_fail
description = "This PR is old enough to merge."
@client.create_status(repo, ref, 'success', :context => context, :description => description)
# puts "#{repo}/#{pull_request['head']['sha']}: #{description}"
end
end
end