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のツールとかがあっても不思議でもないなと思いつつ。