svn pre-commit hook

B

Bil Kleb

OK, I give up.

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?

Based on Kou's svnlook,

https://svn.collab.net/repos/svn/trunk/tools/examples/svnlook.rb

and the bindings,

http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/ruby/svn/

it seems that Svn:Fs might be the answer, but I'm having
trouble figuring out SVN transactions -- the manual says
very little about these creatures.

Thanks,
 
J

Justin Collins

Bil said:
OK, I give up.

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?

Based on Kou's svnlook,

https://svn.collab.net/repos/svn/trunk/tools/examples/svnlook.rb

and the bindings,

http://svn.collab.net/repos/svn/trunk/subversion/bindings/swig/ruby/svn/

it seems that Svn:Fs might be the answer, but I'm having
trouble figuring out SVN transactions -- the manual says
very little about these creatures.

Thanks,

I don't know about pre-commit hooks, but here's a post-commit hook I
wrote the other day. Not fancy or anything, but will maybe give you an idea?
I just used the normal 'svnlook'.
#!/usr/bin/env ruby
require 'net/smtp'

if ARGV.length != 2
puts "Usage: post-commit [repos] [revision]"
end

def parse_changed(change_list)
added = Array.new
modified = Array.new
deleted = Array.new
other = Array.new
change_list = change_list.split("\n")

change_list.each { |changed|

#changed file/dir is preceded by U/D/A
mod = changed[0, 1]

case mod
when 'U'
modified << changed[1..-1]
when 'A'
added << changed[1..-1]
when 'D'
deleted << changed[1..-1]
else
other << changed
end
}

change_string = String.new

if not added.empty?
change_string << "Added:\n" << added.join("\n") << "\n"
end

if not modified.empty?
change_string << "Modified:\n" << modified.join("\n") << "\n"
end

if not deleted.empty?
change_string << "Deleted:\n" << deleted.join("\n") << "\n"
end

if not other.empty?
change_string << "Other:\n" << other.join("\n") << "\n"
end

return change_string
end

recipients = ['(e-mail address removed)']

repository = ARGV[0]
revision = ARGV[1]

author = `svnlook author #{repository} --revision #{revision}`.chomp.capitalize
date = `svnlook date #{repository} --revision #{revision}`.chomp
changed = parse_changed(`svnlook changed #{repository} --revision #{revision}`.chomp)
diff = `svnlook diff #{repository} --revision #{revision} --no-diff-deleted`
log = `svnlook log #{repository} --revision #{revision}`

message = <<END_OF_MESSAGE
To: #{recipients.join(',')}
From: SVN <[email protected]>
Subject: SVN Commit: Revision #{revision} by #{author}

Author: #{author}
Date: #{date}
Revision: #{revision}

#{changed}

Log Message:
#{log}

#{diff}

END_OF_MESSAGE

if $DEBUG
puts message
exit
end

Net::SMTP.start('smtp.host.net', 25, 'host.net', '(e-mail address removed)', 'password') { |smtp|

smtp.send_message(message, '(e-mail address removed)', recipients)

}

-Justin
 
B

Bil Kleb

Justin said:
I don't know about pre-commit hooks, but here's a post-commit hook I
wrote the other day. Not fancy or anything, but will maybe give you an
idea?
Thanks.

I just used the normal 'svnlook'.

If you have the SVN Ruby bindings compiled, you could use Kouhei's
commit-email.rb for post-hook mailings (and RSS feed):

https://svn.collab.net/repos/svn/trunk/tools/hook-scripts/commit-email.rb

If you want HTML output also, see

https://svn.collab.net/repos/svn/trunk/tools/examples/svnlog2html.rb

Regards,
 
K

Kouhei Sutou

----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 <[email protected]>
"svn pre-commit hook" on Sat, 11 Mar 2006 06:28:43 +0900,
Bil Kleb said:
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
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)----
 
B

Bil Kleb

Kouhei said:
Hi,
Hello.

What about attached script?

Excellent, Thanks!

This is the nugget I most craved:
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

Thanks again,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,742
Latest member
AshliMayer

Latest Threads

Top