Skip to content

Commit 491c627

Browse files
otegamikomainu8
andauthored
release: trigger release processes when release workflow succeeded (#47)
GitHub: GH-43 In this PR, we set up how release flow is triggered from webhook requests. --------- Co-authored-by: Horimoto Yasuhiro <[email protected]>
1 parent d809eca commit 491c627

File tree

3 files changed

+161
-10
lines changed

3 files changed

+161
-10
lines changed

ansible/files/home/deployer/webhook/lib/deployer/app.rb

+59-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
# You should have received a copy of the GNU General Public License
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17+
require "json"
1718
require "openssl"
19+
require_relative "payload"
1820
require_relative "response"
1921

2022
module Deployer
@@ -29,23 +31,70 @@ def call(env)
2931
private
3032

3133
def process(request, response)
32-
unless request.post?
33-
response.set(:method_not_allowed, "must POST")
34-
return
35-
end
36-
37-
unless valid_signature?(request)
38-
response.set(:unauthorized, "Authorization failed")
39-
return
34+
begin
35+
unless request.post?
36+
raise RequestError.new(:method_not_allowed, "must POST")
37+
end
38+
verify_signature!(request)
39+
payload = parse_body!(request)
40+
process_payload!(payload)
41+
rescue RequestError => request_error
42+
response.set(request_error.status, request_error.message)
43+
rescue => e
44+
response.set(:internal_server_error, e.message)
4045
end
4146
end
4247

43-
def valid_signature?(request)
48+
def verify_signature!(request)
4449
hmac_sha256 = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"),
4550
ENV["SECRET_TOKEN"],
4651
request.body.read)
4752
signature = "sha256=#{hmac_sha256}"
48-
Rack::Utils.secure_compare(signature, request.env["HTTP_X_HUB_SIGNATURE_256"])
53+
unless Rack::Utils.secure_compare(signature, request.env["HTTP_X_HUB_SIGNATURE_256"])
54+
raise RequestError.new(:unauthorized, "Authorization failed")
55+
end
56+
end
57+
58+
def parse_body!(request)
59+
unless request.media_type == "application/json"
60+
raise RequestError.new(:bad_request, "invalid payload format")
61+
end
62+
63+
body = request.body.read
64+
if body.nil?
65+
raise RequestError.new(:bad_request, "request body is missing")
66+
end
67+
68+
begin
69+
raw_payload = JSON.parse(body)
70+
rescue JSON::ParserError
71+
raise RequestError.new(:bad_request, "invalid JSON format: <#{$!.message}>")
72+
end
73+
74+
metadata = {
75+
"x-github-event" => request.env["HTTP_X_GITHUB_EVENT"]
76+
}
77+
Payload.new(raw_payload, metadata)
78+
end
79+
80+
def process_payload!(payload)
81+
case payload.event_name
82+
when "ping"
83+
# Do nothing because this is a kind of healthcheck.
84+
nil
85+
when "workflow_run"
86+
return unless payload.released?
87+
deploy(payload)
88+
else
89+
raise RequestError.new(:bad_request, "Unsupported event: <#{payload.event_name}>")
90+
end
91+
end
92+
93+
def deploy(payload)
94+
Thread.new do
95+
# TODO: call rake tasks for sign packages.
96+
# TODO: write down the errors into log files.
97+
end
4998
end
5099
end
51100
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (C) 2024 Horimoto Yasuhiro <[email protected]>
2+
# Copyright (C) 2024 Takuya Kodama <[email protected]>
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
module Deployer
18+
class Error < StandardError
19+
end
20+
21+
class RequestError < Error
22+
attr_reader :status, :message
23+
24+
def initialize(status, message)
25+
@status = status
26+
@message = message
27+
end
28+
end
29+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright (C) 2010-2019 Sutou Kouhei <[email protected]>
2+
# Copyright (C) 2015 Kenji Okimoto <[email protected]>
3+
# Copyright (C) 2024 Takuya Kodama <[email protected]>
4+
#
5+
# This program is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
module Deployer
19+
class Payload
20+
RELEASE_WORKFLOWS = ["Package", "CMake"].freeze
21+
22+
def initialize(data, metadata={})
23+
@data = data
24+
@metadata = metadata
25+
end
26+
27+
def [](key)
28+
@data.dig(*key.split("."))
29+
end
30+
31+
def event_name
32+
@metadata["x-github-event"]
33+
end
34+
35+
def workflow_name
36+
self["workflow_run.name"]
37+
end
38+
39+
def workflow_succeeded?
40+
self["workflow_run.conclusion"] == "success"
41+
end
42+
43+
def branch
44+
self["workflow_run.head_branch"]
45+
end
46+
47+
def version
48+
return unless workflow_tag?
49+
branch.delete_prefix("v")
50+
end
51+
52+
def released?
53+
RELEASE_WORKFLOWS.include?(workflow_name) &&
54+
workflow_tag? &&
55+
workflow_succeeded?
56+
end
57+
58+
def repository_owner
59+
self["repository.owner.login"]
60+
end
61+
62+
def repository_name
63+
self["repository.name"]
64+
end
65+
66+
private
67+
68+
def workflow_tag?
69+
return false unless branch
70+
branch.match?(/\Av\d+(\.\d+){1,2}\z/)
71+
end
72+
end
73+
end

0 commit comments

Comments
 (0)