From: Kouhei Sutou Date: 2006-03-13T23:58:23+09:00 Subject: Re: svn pre-commit hook ----Next_Part(Mon_Mar_13_23_58_08_2006_761)-- Content-Type: Multipart/Mixed; boundary="--Next_Part(Mon_Mar_13_23_58_08_2006_761)--" ----Next_Part(Mon_Mar_13_23_58_08_2006_761)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Hi, In "svn pre-commit hook" on Sat, 11 Mar 2006 06:28:43 +0900, Bil Kleb wrote: > Can someone (Kou?) please sketch out how to intercept > a Subversion transaction with a pre-commit hook and > throw each file matching, say, /\.rb$/ to some StyleChecker? What about attached script? You can use the attached script with the following pre-commit script: #!/bin/sh REPOS="$1" TXN="$2" /.../ruby \ /.../pre-check-file.rb "$REPOS" "$TXN" \ -p PATTERN1 -c CHECK_COMMAND1 \ -p PATTERN2 -c CHECK_COMMAND2 \ ... \ || exit 1 exit 0 For example (reject space indented Ruby scripts and C programs): #!/bin/sh REPOS="$1" TXN="$2" /usr/bin/ruby \ ~/work/ruby/pre-check-file.rb "$REPOS" "$TXN" \ -p \.rb$ -c 'grep -v ^ ' \ -p \.c$ -c 'grep -v ^ ' \ || exit 1 exit 0 Thanks, -- kou ----Next_Part(Mon_Mar_13_23_58_08_2006_761)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="pre-check-file.rb" #!/usr/bin/env ruby require 'thread' require 'optparse' require 'ostruct' def parse(args) options = OpenStruct.new options.patterns = [] options.checkers = [] opts = OptionParser.new do |opts| opts.separator "" opts.on("-I", "--include=PATH", "Add PATH to load path") do |path| $LOAD_PATH.unshift(path) end opts.on("-p", "--pattern=PATTERN", "Add PATTERN to check target patterns") do |pattern| options.patterns << /#{pattern}/ end opts.on("-c", "--checker=CHECKER", "Add CHECKER to check programs") do |checker| options.checkers << checker end opts.on_tail("-h", "--help", "Show this message") do puts opts exit!(1) end end opts.parse!(args) if options.patterns.empty? STDERR.puts("no pattern is given.") exit!(1) elsif options.patterns.size != options.checkers.size message = "patterns size (#{options.patterns.size}) and " message << "checkers size (#{options.checkers.size}) are mismatch." STDERR.puts(message) exit!(1) end options end class FileChecker def initialize(path, txn, patterns, checkers) @fs = Svn::Repos.open(path).fs @txn = @fs.open_txn(txn) @root = @txn.root @rev = @fs.youngest_rev @patterns = patterns @checkers = checkers end def check invalid_infos = run_checker if invalid_infos.empty? 0 else invalid_infos.each do |path, result, checker| STDERR.puts("#{path} wasn't passed check: #{checker}") result.each do |line| STDERR.puts(" #{line}") end end 1 end end def run_checker invalid_infos = [] changed_paths.each do |path| @patterns.each_with_index do |pattern, i| if pattern =~ path checker = @checkers[i] success, result = run(checker) do |output, input| output.print(@root.file_contents(path) {|f| f.read}) end invalid_infos << [path, result, checker] unless success end end end invalid_infos end def changed_paths(base_rev=nil) base_rev ||= @txn.base_revision base_root = @fs.root(base_rev) editor = Svn::Delta::ChangedEditor.new(@root, base_root) base_root.dir_delta('', '', @root, '', editor) (editor.added_files + editor.updated_files).uniq.sort end private def run(command) pw = IO.pipe pr = IO.pipe pe = IO.pipe pid = exit_status = nil Thread.exclusive do verbose = $VERBOSE $VERBOSE = nil pid = fork do $VERBOSE = verbose pw[1].close STDIN.reopen(pw[0]) pw[0].close pr[0].close STDOUT.reopen(pr[1]) pr[1].close pe[0].close STDERR.reopen(pe[1]) pe[1].close exec(command) exit!(1) end $VERBOSE = verbose end pw[0].close pr[1].close pe[1].close pw[1].sync = true yield(pw[1], pr[0]) if block_given? pw[1].close pid, exit_status = Process.waitpid2(pid) return [exit_status.exitstatus.zero?, pe[0].read] ensure unless exit_status Thread.start do begin until Process.waitpid(pid, Process::WNOHANG) sleep 1 end rescue # ignore end end end end end def main if ARGV.find {|x| ["-h", "--help"].include?(x)} parse(ARGV) else path = ARGV.shift txn = ARGV.shift options = parse(ARGV) end require 'svn/core' require 'svn/fs' require 'svn/delta' require 'svn/repos' require 'svn/client' checker = FileChecker.new(path, txn, options.patterns, options.checkers) checker.check end begin exit main rescue Exception if $!.is_a?(SystemExit) raise else STDERR.puts($!.class) STDERR.puts($!) STDERR.puts($@) exit!(1) end end ----Next_Part(Mon_Mar_13_23_58_08_2006_761)---- ----Next_Part(Mon_Mar_13_23_58_08_2006_761)----