-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.rb
executable file
·347 lines (295 loc) · 9.46 KB
/
app.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
require "sinatra"
require "sinatra/json"
require "octokit"
require "active_support/core_ext/numeric/time"
require 'dotenv'
require "jwt"
require 'yaml'
Dotenv.load
$stdout.sync = true
CLIENT_ID = ENV.fetch("GITHUB_CLIENT_ID")
CLIENT_SECRET = ENV["GITHUB_CLIENT_SECRET"]
GITHUB_API_ENDPOINT = "https://api.github.com/"
GITHUB_KEY_LOCATION = ENV.fetch("GITHUB_KEY_LOCATION", nil)
if GITHUB_KEY_LOCATION.nil?
GITHUB_APP_KEY = ENV.fetch("GITHUB_APP_KEY")
else
info = File.read(GITHUB_KEY_LOCATION)
GITHUB_APP_KEY = info
end
GITHUB_APP_ID = ENV.fetch("GITHUB_APP_ID")
GITHUB_APP_URL = ENV.fetch("GITHUB_APP_URL")
SESSION_SECRET = ENV.fetch("SESSION_SECRET")
# Load service replacement
yml = File.open('service-replacement.yaml')
$service_replacement_list = YAML.load(yml)
enable :sessions
set :session_secret, SESSION_SECRET
Octokit.configure do |c|
c.default_media_type = "application/vnd.github.machine-man-preview+json"
c.auto_paginate = true
c.user_agent = "#{Octokit.user_agent}: service-deprecation-app"
end
# Ask the user to authorise the app.
def authenticate!
@client = Octokit::Client.new
url = @client.authorize_url(CLIENT_ID)
redirect url
end
def install!
redirect GITHUB_APP_URL
end
# Check whether the user has an access token.
def authenticated?
session[:access_token]
end
def installed?
!session[:installation_list].nil? && session[:installation_list].count > 0
end
def check_installations
@access_token = session[:access_token]
installation_ids = []
begin
@client = Octokit::Client.new :access_token => @access_token
response = @client.find_user_installations
installation_count = response.total_count
if installation_count > 0
response.installations.each do |installation|
installation_ids.push(installation.id)
end
end
session[:installation_list] = installation_ids
rescue => e
session[:installation_list] = nil
authenticate!
end
end
def get_jwt
private_pem = GITHUB_APP_KEY
private_key = OpenSSL::PKey::RSA.new(private_pem)
payload = {
# issued at time
iat: Time.now.to_i,
# JWT expiration time (10 minute maximum)
exp: 5.minutes.from_now.to_i,
# Integration's GitHub identifier
iss: GITHUB_APP_ID
}
JWT.encode(payload, private_key, "RS256")
end
def get_app_token(installation_id)
return_token = ''
begin
@jwt_client = Octokit::Client.new(:bearer_token => get_jwt, :accept => @accept_header)
new_token = @jwt_client.create_app_installation_access_token(installation_id, :accept => @accept_header)
return_token = new_token.token
rescue => error
puts error
end
return return_token
end
# Check whether the user's access token is valid.
def check_access_token
@access_token = session[:access_token]
begin
@client = Octokit::Client.new :access_token => @access_token
@user = @client.find_user_installations
rescue => e
# The token has been revoked, so invalidate the token in the session.
session[:access_token] = nil
authenticate!
end
end
def select_installation!(installation_id)
session[:selected_installation] = installation_id
end
def installation_selected?
session[:selected_installation]
end
get "/reset" do
session[:selected_installation] = nil
session[:installation_list] = nil
session[:access_token] = nil
redirect "/"
end
def installations
@client = Octokit::Client.new(:access_token => session[:access_token])
@client.find_user_installations[:installations]
end
# Wrapper route for redirecting the user to authorise the app.
get "/auth" do
authenticate!
end
get "/install" do
install!
end
# Serve the main page.
get "/" do
if !authenticated?
return erb :how, :locals => { :authenticated => authenticated? }
end
check_access_token
check_installations
if !installed?
return erb :install, :locals => { :authenticated => authenticated?, :installed => installed?}
else
erb :index, :locals => {
:authenticated => authenticated?, :installations => installations, :installation_selected => installation_selected?}
end
end
def replace_hook(installation_id, repository_name, hook_id)
app_token = get_app_token(installation_id)
return 404 unless app_token != ''
@app_client = Octokit::Client.new(:access_token => app_token)
# Get old hooks
result = @app_client.hook(repository_name, hook_id, :accept => "application/vnd.github.machine-man-preview+json")
params = ""
events = ['push']
if result.name == "jenkinsgit"
url = result.config.jenkins_url
# TODO: Look up repository URL to support GitHub Enterprise
params = "/git/notifyCommit?url=http://github.com/#{repository_name}"
hook_data = {:jenkins_url => jenkins_url}
elsif result.name == "jenkins"
url = result.config.jenkins_hook_url
hook_data = {:jenkins_hook_url => jenkins_url}
elsif result.name == "docker"
url = "https://registry.hub.docker.com/hooks/github"
hook_data = {}
elsif result.name == "codereviewhub"
url = "https://www.codereviewhub.com"
hook_data = {}
events = ['push', "pull_request", "issue_comment", "commit_comment", "pull_request_review_comment"]
else
puts "unknown error"
return nil
end
# Add repo webhook for `push` events
begin
create_result = @app_client.create_hook(repository_name, 'web',
{
:url => "#{url}#{params}",
:content_type => 'json'
},
{
:events => events,
:active => true
}
)
rescue => e
puts e
return 400
end
# Disable old Service Hook if webhook creation succeeded
begin
result = @app_client.edit_hook(repository_name, hook_id, 'jenkinsgit', hook_data, {
:active => false
})
rescue => e
puts e
return 400
end
return 201
end
# Return a all the Service hooks installed on a Repository
def get_hook_list(installation_id, repository_name, local_client)
hook_list = Array.new
begin
results = local_client.hooks(repository_name, :accept => "application/vnd.github.machine-man-preview+json")
# Search for all service hooks on a repository
results.each do |hook|
if (hook.name == 'jenkinsgit' || hook.name == 'codereviewhub') && hook.active
replacement = $service_replacement_list[hook.name]
hook_list.push({id: hook.id, hook_name: hook.name, replacement: "#{replacement['url']}?repo_name=#{repository_name}&hook_id=#{hook.id}&installation_id=#{installation_id}", message: replacement['message']})
elsif hook.name == 'jenkins' && hook.active
replacement = $service_replacement_list[hook.name]
hook_list.push({id: hook.id, hook_name: hook.name, replacement: "#{replacement['url']}?repo_name=#{repository_name}&hook_id=#{hook.id}&installation_id=#{installation_id}", message: replacement['message']})
elsif hook.name == 'docker' && hook.active
replacement = $service_replacement_list[hook.name]
hook_list.push({id: hook.id, hook_name: hook.name, replacement: "#{replacement['url']}?repo_name=#{repository_name}&hook_id=#{hook.id}&installation_id=#{installation_id}", message: replacement['message']})
elsif hook.name != 'web'
puts hook.name
end
end
rescue => err
puts err
end
hook_list
end
# Respond to requests to check a commit. The commit URL is included in the
# url param.
post "/" do
authenticate! if !authenticated?
check_access_token
install if !installed?
# Select an Installation
installation_id = params[:installation_id].to_i
begin
result = {repo_list: []}
@client.auto_paginate = true
response = @client.find_installation_repositories_for_user(installation_id)
app_token = get_app_token(installation_id)
return 404 unless app_token != ''
@app_client = Octokit::Client.new(:access_token => app_token)
@app_client.auto_paginate = true
if response.total_count > 0
response.repositories.each do |repo|
hook_list = get_hook_list(params[:installation_id], repo["full_name"], @app_client)
if !hook_list.nil? && hook_list.count > 0
return_data = {full_name: repo["full_name"], installation_id: installation_id, hooks: hook_list}
result[:repo_list].push(return_data)
end
end
end
result[:commit_url] = params[:installation_id]
rescue => err
return json :error_message => err
end
json result
end
get "/replace_jenkins" do
installation_id = params[:installation_id]
repo_name = params[:repo_name]
hook_id = params[:hook_id]
replace_hook(installation_id, repo_name, hook_id)
end
# Remove and generalize
get "/replace_codereviewhub" do
installation_id = params[:installation_id]
repo_name = params[:repo_name]
hook_id = params[:hook_id]
replace_hook(installation_id, repo_name, hook_id)
# Success
redirect "/"
end
get "/replace_docker" do
installation_id = params[:installation_id]
repo_name = params[:repo_name]
hook_id = params[:hook_id]
replace_hook(installation_id, repo_name, hook_id)
# Success
redirect "/"
end
# Handle the redirect from GitHub after someone authorises the app.
get "/callback" do
session_code = params[:code]
result = Octokit.exchange_code_for_token \
session_code, CLIENT_ID, CLIENT_SECRET, :accept => "application/json"
session[:access_token] = result.access_token
redirect "/"
end
# Show the 'How does this work?' page.
get "/how" do
erb :how, :locals => { :authenticated => authenticated? }
end
get "/debug-x" do
puts session[:access_token]
end
get "/debug" do
access_token = params[:token]
session[:access_token] = access_token
end
# Ping endpoing for uptime check.
get "/ping" do
"pong"
end