From a82b318dbb7d42d90900557d539b6c7c093c6a05 Mon Sep 17 00:00:00 2001 From: otegami Date: Thu, 12 Dec 2024 13:17:38 +0900 Subject: [PATCH] draf relase: run asyn task --- .../home/deployer/webhook/lib/deployer/app.rb | 65 +++++++++++++++- .../deployer/webhook/lib/deployer/payload.rb | 76 +++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 ansible/files/home/deployer/webhook/lib/deployer/payload.rb diff --git a/ansible/files/home/deployer/webhook/lib/deployer/app.rb b/ansible/files/home/deployer/webhook/lib/deployer/app.rb index cf6f8c1..d6a822e 100644 --- a/ansible/files/home/deployer/webhook/lib/deployer/app.rb +++ b/ansible/files/home/deployer/webhook/lib/deployer/app.rb @@ -14,16 +14,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +require "json" require "openssl" require_relative "response" +require_relative "payload" +require_relative "source-archive" module Deployer class App def call(env) request = Rack::Request.new(env) response = Response.new - process(request, response) - response.finish + process(request, response) || response.finish end private @@ -38,6 +40,10 @@ def process(request, response) response.set(:unauthorized, "Authorization failed") return end + + payload = parse_payload(request, response) + return if payload.nil? + process_payload(request, response, payload) end def valid_signature?(request) @@ -47,5 +53,60 @@ def valid_signature?(request) signature = "sha256=#{hmac_sha256}" Rack::Utils.secure_compare(signature, request.env["HTTP_X_HUB_SIGNATURE_256"]) end + + def parse_payload(request, response) + unless request.media_type == "application/json" + response.set(:bad_request, "invalid payload format") + return + end + + payload = request.body.read + if payload.nil? + response.set(:bad_request, "payload is missing") + return + end + + begin + JSON.parse(request.body.read) + rescue JSON::ParserError + response.set(:bad_request, "invalid JSON format: <#{$!.message}>") + nil + end + end + + def process_payload(request, response, raw_payload) + metadata = { + "x-github-event" => request.env["HTTP_X_GITHUB_EVENT"] + } + + payload = Payload.new(raw_payload, metadata) + + case payload.event_name + when "ping" + # Do nothing because this is a kind of healthcheck. + nil + when "workflow_run" + return unless payload.released? + process_release(request, response, payload) + else + response.set(:bad_request, + "Unsupported event: <#{payload.event_name}>") + nil + end + end + + def process_release(request, response, payload) + response.finish do + Thread.new do + gpg_key_id = "TODO: handle a GPG key" + archive = Deployer::SourceArchive.new(payload.repository_owner, + payload.repository_name, + payload.repository_name, + payload.version, + payload.branch) + archive.process(gpg_key_id) + end + end + end end end diff --git a/ansible/files/home/deployer/webhook/lib/deployer/payload.rb b/ansible/files/home/deployer/webhook/lib/deployer/payload.rb new file mode 100644 index 0000000..eab9a7e --- /dev/null +++ b/ansible/files/home/deployer/webhook/lib/deployer/payload.rb @@ -0,0 +1,76 @@ +# Copyright (C) 2010-2019 Sutou Kouhei +# Copyright (C) 2015 Kenji Okimoto +# Copyright (C) 2024 Takuya Kodama +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +module Deployer + class Payload + RELEASE_WORKFLOWS= ["Package"].map(&:freeze).freeze + + def initialize(data, metadata={}) + @data = data + @metadata = metadata + end + + def [](key) + key.split(".").inject(@data) do |current_data, current_key| + if current_data + current_data[current_key] + else + nil + end + end + end + + def event_name + @metadata["x-github-event"] + end + + def workflow_name + self["workflow_run.name"] + end + + def workflow_tag? + branch&.match?(/^v\d+(\.\d+){2}$/) + end + + def workflow_succeeded? + self["workflow_run.conclusion"] == "success" + end + + def branch + self["workflow_run.head_branch"] + end + + def released? + RELEASE_WORKFLOWS.include?(workflow_name) && + workflow_tag? && + workflow_succeeded? + end + + def repository_owner + owner = self["repository.owner"] || {} + owner["login"] + end + + def repository_name + self["repository.name"] + end + + def version + workflow_tag? && branch.delete_prefix("v") + end + end +end