post-recieveを遅延実行

gitのリポジトリへpushしたら、jenkinsへ通知とかは、メインリポイトリ側で、post-recieveフックの設定をすることでできるのだけど、通知先が大量に増えたりすると、git pushが重くなってしまいストレスフル、ということで、遅延実行させる仕組みをつかっている。

commit hookのときが実行されたときの環境変数、標準入力などの情報をファイルへ保存しておき、それを、cronから、定期的に遅延実行してやる、という寸法。

たぶん1.8以降のrubyであれば動くのではないかと。

#!/usr/bin/env ruby
require 'yaml'
require 'pathname'

def main queue_dir
        result_of = {}

        stdin = ""
        while line = STDIN.gets
                stdin += line
        end
        result_of["stdin"] = stdin

        result_of["env"] = {}
        ENV.each do |key, val|
                result_of["env"][key] = val
        end

        Pathname.new(queue_dir).mkpath
        base = Time.now.to_f
        File.open "#{queue_dir}/#{base}", "w" do |f|
                f.puts result_of.to_yaml
        end
end

main(ARGV.shift)

こんなのをsave-queue.rbとして保存し、

ruby save-queue.rb $DIR_TO_SAVE_QUEUE

みたいにして、post-recieveフックから呼びだすと、
git pushすると、$DIR_TO_SAVE_QUEUEにファイルができる。

それを、cronから、

ruby run-queue.rb $DIR_TO_SAVE_QUEUE $shellscript_to_execute

みたいにしてやると、環境変数を再現しつつ、$shellscript_to_executeを実行してやる。

$shellscript_to_executeは、元々のpost-recieve-hookの記述をそのまま移してやればよい。

run-queue.rbは以下のような感じ。

#!/usr/bin/env ruby

require 'pathname'
require 'yaml'
require 'open3'

def main queue_dir, cmd
        queue_path = Pathname.new(queue_dir)
        queue_path.children.each do |f|
                next unless f.file?
                run f, cmd
        end
end

def run file, cmd
        yaml = YAML.load_file(file)
        file.delete # コマンドの実行には時間がかかることがあるので、実行前にjob は消す
        env = ""
        yaml['env'].each do |key,val|
           env += %| #{key}="#{val}"|
        end
        Dir::chdir(yaml['env']["PWD"])
        IO.popen("#{env} #{cmd}", "w+") do |io|
                unless yaml['stdin'].nil?
                        io.puts yaml['stdin']
                end
                io.close_write
        end
end

main(*ARGV)

もちろん、pushしたときにexitコードを非ゼロにしておいて、rejectするようなhookはこのようなやり方はできないですが、わりと通知的なものはこういうやり方しておくと気軽に通知先が増やせてよいんじゃないかなーと思います。

特にコード書かないでもこういうことやってくれるUNIXのツールとかがあっても不思議でもないなと思いつつ。