Thread: [Phpslash-commit] CVS: CVSROOT collect_diffs.rb,NONE,1.1 cvsspam.rb,NONE,1.1 record_lastdir.rb,NONE,
Brought to you by:
joestewart,
nhruby
From: Joe S. <joe...@us...> - 2004-11-23 22:06:53
|
Update of /cvsroot/phpslash/CVSROOT In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9220 Modified Files: commitinfo Added Files: collect_diffs.rb cvsspam.rb record_lastdir.rb cvsspam.conf Log Message: switching from syncmail to cvsspam --- NEW FILE: collect_diffs.rb --- #!/usr/bin/ruby -w # Part of CVSspam # http://www.badgers-in-foil.co.uk/projects/cvsspam/ # Copyright (c) David Holroyd # CVSROOT is /var/lib/cvs # ARGV is 'CVSROOT/foo/space dir space file,NONE,1.1 some,file,1.4,1.5' # ---- # Update of /var/lib/cvs/CVSROOT/foo/space dir # In directory foil:/tmp/cvs-serv13059 # # Modified Files: # some,file # Added Files: # space file # Log Message: # msg # # Assumptions # - file names do not contain newlines or single quotes $tmpdir = ENV["TMPDIR"] || "/tmp" $dirtemplate = "#cvsspam.#{Process.getpgrp}.#{Process.uid}" def find_data_dir Dir["#{$tmpdir}/#{$dirtemplate}-*"].each do |dir| stat = File.stat(dir) return dir if stat.owned? end nil end def blah(msg) if $debug $stderr.puts "collect_diffs.rb: #{msg}" end end # Like IO.popen, but accepts multiple arguments like Kernel.exec # (So no need to escape shell metacharacters) def safer_popen(*args) IO.popen("-") do |pipe| if pipe==nil exec(*args) else yield pipe end end end class ChangeInfo def initialize(file, fromVer, toVer) @file, @fromVer, @toVer = file, fromVer, toVer if fromVer == toVer fail "'from' and 'to' versions should be different ('#{fromVer}')" end end attr_reader :file, :fromVer, :toVer def to_s "<ChangeInfo \"#{@file}\" #{@toVer}<--#{@fromVer}>" end def isAddition ; fromVer == 'NONE' end def isRemoval ; toVer == 'NONE' end #def isModification ; !(isAddition || isRemoval) end end $commitinfo_tags = nil def get_commitinfo_tag(filename) if $commitinfo_tags.nil? return nil unless FileTest.exists?("#{$datadir}/commitinfo-tags") File.open("#{$datadir}/commitinfo-tags") do |file| $commitinfo_tags = Hash.new file.each_line do |line| line =~ /([^\t]+)\t(.+)/ key = $2 val = $1 key.sub!(/^#{ENV['CVSROOT']}\//, '') $commitinfo_tags[key] = val end end end return $commitinfo_tags[filename] end # cvs_info comes from the command line, ultimately as the expansion of the # %{sVv} in $CVSROOT/loginfo. It isn't possible to parse this value # unambiguously, but we make an effort to get it right in as many cases as # possible. def collect_antique_style_args(cvs_info) unless cvs_info.slice(0, $repository_path.length+1) == "#{$repository_path} " fail "calculated repository path ('#{$repository_path}') doesn't match start of command line arg ('#{cvs_info}')" end version_info = cvs_info.slice($repository_path.length, cvs_info.length-$repository_path.length) changes = Array.new # make a list of changed files given on the command line while version_info.length>0 if version_info.sub!(/^ (.+?),(NONE|[.0-9]+),(NONE|[.0-9]+)/, '') == nil fail "'#{version_info}' doesn't match ' <name>,<ver>,<ver> ...'" end changes << ChangeInfo.new($1, $2, $3) end return changes end def collect_modern_style_args(cvs_info) # unless cvs_info[0] == $repository_path # fail "calculated repository path ('#{$repository_path}') doesn't match first command line arg ('#{cvs_info[0]}')" # end changes = Array.new i = 0 while i < cvs_info.length changes << ChangeInfo.new(cvs_info[i], cvs_info[i+=1], cvs_info[i+=1]) i+=1 end return changes end def process_log(cvs_info) cvsroot = ENV['CVSROOT'] $datadir = find_data_dir() raise "missing data dir (#{$tmpdir}/#{$dirtemplate}-XXXXXX)" if $datadir==nil line = $stdin.gets unless line =~ /^Update of (.+)/ fail "Log preamble looks suspect (doesn't start 'Update of ...')" end $path = $1 unless $path.slice(0,cvsroot.length) == cvsroot fail "CVSROOT ('#{cvsroot}') doesn't match log preamble ('#{$path}')" end $repository_path = $path.slice(cvsroot.length+1, $path.length-cvsroot.length-1) if $use_modern_argument_list changes = collect_modern_style_args(cvs_info) else changes = collect_antique_style_args(cvs_info) end # look for the start of the user's comment $stdin.each do |line| break if line =~ /^Log Message/ end unless line =~ /^Log Message/ fail "Input did not contain a 'Log Message:' entry" end File.open("#{$datadir}/logfile", File::WRONLY|File::CREAT|File::APPEND) do |file| $stdin.each do |line| file.puts "#> #{line}" end changes.each do |change| # record version information file.puts "#V #{change.fromVer},#{change.toVer}" # note if the file is on a branch tag = nil if change.isRemoval tag = get_commitinfo_tag("#{$repository_path}/#{change.file}") else status = nil safer_popen($cvs_prog, "-nq", "status", change.file) do |io| status = io.read end fail "couldn't get cvs status: #{$!} (exited with #{$?})" unless ($?>>8)==0 if status =~ /^\s*Sticky Tag:\s*(.+) \(branch: +/m tag = $1 end end file.puts "#T #{tag}" unless tag.nil? diff_cmd = Array.new << $cvs_prog << "-nq" << "diff" << "-Nu" diff_cmd << "-kk" if $diff_ignore_keywords if change.isAddition file.write "#A " # cruft up a date in the distant past, when the file would not have # existed, so that the diff will show all lines as added diff_cmd << "-D1/26/1977" << "-r#{change.toVer}" elsif change.isRemoval file.write "#R " # just specifying one version, cvs will diff between that version and # the current version (will show all lines removed) diff_cmd << "-r#{change.fromVer}" else file.write "#M " diff_cmd << "-r#{change.fromVer}" << "-r#{change.toVer}" end file.puts "#{$repository_path}/#{change.file}" diff_cmd << change.file # do a cvs diff and place the output into our temp file blah("about to run #{diff_cmd.join(' ')}") safer_popen(*diff_cmd) do |pipe| # skip over cvs-diff's preamble pipe.each do |line| break if line =~ /^diff / end file.puts "#U #{line}" pipe.each do |line| file.puts "#U #{line}" end end # TODO: don't how to do this reliably on different systems... #fail "cvsdiff did not give exit status 1 for invocation: #{diff_cmd.join(' ')}" unless ($?>>8)==1 end end end # sometimes, CVS would exit with an error like, # # cvs [server aborted]: received broken pipe signal # # consuming all the data on our standard input seems to stop this error # happening. (This problem may have been fixed in CVS 1.12.6, looking at # a message in the NEWS file.) def consume_stdin() $stdin.read() end def choose_operation(op) # in the CVS 1.12.x series, new directory notifications include versions # 'NONE' and 'NONE', whereas older versions of CVS didn't include any version # info here. The string ',NONE,NONE' is therefore optional in the regexp, if op =~ / - New directory(?:,NONE,NONE)?$/ blah("No action taken on directory creation") consume_stdin() elsif op =~ / - Imported sources$/ blah("Imported not handled") consume_stdin() else process_log(op) mailtest end end def mailtest lastdir = nil File.open("#{$datadir}/lastdir") do |file| lastdir = file.gets end if $path == lastdir blah("sending spam. (I am #{$0})") # REVISIT: $0 will not contain the path to this script on all systems cmd = File.dirname($0) + "/cvsspam.rb" unless system(cmd, "#{$datadir}/logfile", *$passthroughArgs) fail "problem running '#{cmd}'" end if $debug blah("leaving file #{$datadir}/logfile") else File.unlink("#{$datadir}/logfile") end File.unlink("#{$datadir}/lastdir") File.unlink("#{$datadir}/commitinfo-tags") if FileTest.exists?("#{$datadir}/commitinfo-tags") Dir.rmdir($datadir) unless $debug else blah("not spam time yet, #{$path}!=#{lastdir}") end end class CVSConfig def initialize(filename) @data = Hash.new File.open(filename) do |io| read(io) end end def read(io) io.each do |line| parse_line(line) end end def parse_line(line) # strip any comment (assumes values can't contain '#') line.sub!(/#.*$/, "") if line =~ /^\s*(.*?)\s*=\s*(.*?)\s*$/ @data[$1] = $2 end end def [](key) @data[key] end end $config = nil $cvs_prog = "cvs" $debug = false $diff_ignore_keywords = false $task_keywords = [] unless ENV.has_key?('CVSROOT') fail "$CVSROOT not defined. It should be when I am invoked from CVSROOT/loginfo" end require 'getoptlong' opts = GetoptLong.new( [ "--to", "-t", GetoptLong::REQUIRED_ARGUMENT ], [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--from", "-u", GetoptLong::REQUIRED_ARGUMENT ] ) # arguments to pass though to 'cvsspam.rb' $passthroughArgs = Array.new opts.each do |opt, arg| if ["--to", "--config", "--from"].include?(opt) $passthroughArgs << opt << arg end if ["--debug"].include?(opt) $passthroughArgs << opt end $config = arg if opt=="--config" $debug = true if opt == "--debug" end blah("CVSROOT is #{ENV['CVSROOT']}") blah("ARGV is <#{ARGV.join('>, <')}>") cvsroot_dir = "#{ENV['CVSROOT']}/CVSROOT" if $config == nil if FileTest.exists?("#{cvsroot_dir}/cvsspam.conf") $config = "#{cvsroot_dir}/cvsspam.conf" elsif FileTest.exists?("/etc/cvsspam/cvsspam.conf") $config = "/etc/cvsspam/cvsspam.conf" end if $config != nil $passthroughArgs << "--config" << $config end end $use_modern_argument_list = false cvs_config_filename = "#{cvsroot_dir}/config" if FileTest.exists?(cvs_config_filename) cvs_config = CVSConfig.new(cvs_config_filename) $use_modern_argument_list = cvs_config["UseNewInfoFmtStrings"] == "yes" end if $config != nil if FileTest.exists?($config) def addHeader(name,val) end def addRecipient(who) end class GUESS end load $config else blah("Config file '#{$config}' not found, ignoring") end end if $use_modern_argument_list if ARGV.length % 3 != 0 $stderr.puts "Expected 3 arguments for each file" end choose_operation(ARGV) else if ARGV.length != 1 $stderr.puts "Expected arguments missing" $stderr.puts "* You shouldn't run collect_diffs by hand, but from a CVSROOT/loginfo entry *" $stderr.puts "Usage: collect_diffs.rb [ --to <email> ] [ --config <file> ] %{sVv}" $stderr.puts " (the sequence '%{sVv}' is expanded by CVS, when found in CVSROOT/loginfo)" exit end choose_operation(ARGV[0]) end --- NEW FILE: cvsspam.rb --- #!/usr/bin/ruby -w # Part of CVSspam # http://www.badgers-in-foil.co.uk/projects/cvsspam/ # Copyright (c) David Holroyd # collect_diffs.rb expects to find this script in the same directory as it # # TODO: exemplify syntax for 'cvs admin -m' when log message is missing # TODO: make max-line limit on diff output configurable # TODO: put more exact max size limit on whole email # TODO: support non-html mail too (text/plain, multipart/alternative) # If you want another 'todo keyword' (TODO & FIXME are highlighted by default) # you could add # $task_keywords << "KEYWORD" << "MAYBEANOTHERWORD" # to your cvssppam.conf [...1502 lines suppressed...] mailer.send($from_address, $recipients) do |mail| mail.header("Subject", mailSubject) mail.header("MIME-Version", "1.0") mail.header("Content-Type", "text/html" + ($charset.nil? ? "" : "; charset=\"#{$charset}\"")) if ENV['REMOTE_HOST'] # TODO: I think this will always be an IP address. If a hostname is # possible, it may need encoding of some kind, mail.header("X-Originating-IP", "[#{ENV['REMOTE_HOST']}]") end unless ($additionalHeaders.empty?) $additionalHeaders.each do |header| mail.header(header[0], header[1]) end end mail.header("X-Mailer", "CVSspam #{$version} <http://www.badgers-in-foil.co.uk/projects/cvsspam/>") mail.body do |body| make_html_email(body) end end --- NEW FILE: record_lastdir.rb --- #!/usr/bin/ruby -w # Part of CVSspam # http://www.badgers-in-foil.co.uk/projects/cvsspam/ # Copyright (c) David Holroyd $repositorydir = ARGV.shift $tmpdir = ENV["TMPDIR"] || "/tmp" # try to pick a name to avoid collisions with other people's commits $dirtemplate = "#cvsspam.#{Process.getpgrp}.#{Process.uid}" def find_data_dir Dir["#{$tmpdir}/#{$dirtemplate}-*"].each do |dir| stat = File.stat(dir) return dir if stat.owned? end nil end $datadir = find_data_dir() if $datadir==nil $datadir = "#{$tmpdir}/#{$dirtemplate}-#{rand(99999999)}" Dir.mkdir($datadir, 0700) end $tags = nil # Record any tag name found in 'Entries' for files being commited. This is # required at pre-commit-time as we have no other way of dertermining what # branch a file was on if it's been removed. # # If the commitinfo-tags file exists from a previous, unsuccessful commit, # then it's possible for it to contain multiple entries for a particular file. # The consumer of commitinfo-tags must take care to only use the last entry # for a given filename. def write_tag(name, file) if $tags.nil? $tags = File.new("#{$datadir}/commitinfo-tags", File::WRONLY|File::CREAT|File::APPEND); return if $tags.nil? end $tags.puts("#{name}\t#{file}") end File.open("CVS/Entries") do |file| file.each_line do |line| next if line =~ /^D/ info = line.split(/\//) # skip entries not commited this invocation, if ARGV.delete(info[1]).nil? next end if info[5] =~ /^T(.+)/ write_tag($1, "#{$repositorydir}/#{info[1]}") end end end $tags.close unless $tags.nil? unless ARGV.empty? $stderr.puts "Nothing in CVS/Entries for "+ARGV.join(", ") end # Record the directory currently being commited to. # # This script (and collect_diffs.rb) will be run just for the files in a # single directory. # # A commit to files in multiple directories will therefore produce multiple # invocations of these scripts. To send the email only when the whole commit # is done, each run overwrites the 'lastdir' file; collect_diffs.rb will # later inspect the value it contains to work out if it needs to generate the # email yet. File.open("#{$datadir}/lastdir", "w") { |file| file.write $repositorydir } --- NEW FILE: cvsspam.conf --- # -*- Ruby -*- Configuration for CVSspam commit-mail generation system # Part of CVSspam # http://www.badgers-in-foil.co.uk/projects/cvsspam/ # Copyright (c) David Holroyd # Place this file in your CVSROOT/, or specify its location with # collect_diffs.rb's --config argument. # Who should be mailed? (Optional) # # Multiple addresses may specified. These will be used in addition to any # --to arguemnts given to collect_diffs.rb on the commandline (i.e. in # 'CVSROOT/loginfo'). # # If you need lots of commit-mail recipients, consider setting up a mailing # list to propogate the messages. #addRecipient "glo...@so...valid" # Link to Bug/Issue Tracking systems (Optional) # # If you want comments to include links to a Bugzilla installation, give # $bugzillaURL of the page for viewing bugs (usually show_bug.cgi) using the # string '%s' to specify where the bugId should be substituted. # # If you specify a URL, text within log-comments of the form 'bug 1234' # will be made into hyperlinks in the generated email. # # When $jiraURL is given, text of the form 'project-1234' will be linked # to this issue in JIRA. #$bugzillaURL = "http://bugzilla.mozilla.org/show_bug.cgi?id=%s" #$jiraURL = "http://jira.atlassian.com/secure/ViewIssue.jspa?key=%s" # Link to CVS web frontends (Optional) # # The generated mail can contain links to the new/original versions of a # changed file. This can be particularly useful for viewing binary files # (i.e. images) before an after a change, as no diff is included for these. # # Specify one of either $viewcvsURL or $choraURL (or neither if you don't # have them, and don't want links). For ViewCVS, the URL usually ends with # 'viewcvs.cgi'. For Chora, the URL usualy ends with the directory that # contains cvs.php/diff.php/etc. $viewcvsURL = "http://cvs.sourceforge.net/viewcvs.py/" #$choraURL = "http://localhost/hord/chora/" #$cvswebURL = "http://localhost/cgi-bin/cvsweb.cgi" # Additional SMTP Headers (Optional) # # You can specify additional headers to add to the generated email. (For # instance, you could flag mails, and tell SpamAssasin to be kind to them.) #addHeader "Reply-To", "dev...@so...valid" #addHeader "X-Been-There", "crackmonkey.org" # sendmail location (Default: '/usr/sbin/sendmail') # # If your sendmail program is installed somewhere other than the default, # specify the location here. The program specified must accept the -t option # (to make it accept mail headers on stdin). # # See also $smtp_host, below. #$sendmail_prog = "/usr/sbin/sendmail" # SMTP host name (Optional) # If you don't have a sendmail-like local MTA, you can specify an SMTP # server to connect to, instead. Setting a value here means that the # $sendmail_prog is ignored; if left unset, SMTP will not be used. # # Windows users don't normally have sendmail-like software, and so will # want to configure this option. #$smtp_host = "smtp.your.domain" # cvs location (Default: standard $PATH) # # If your cvs program is installed somewhere outside the standard $PATH, # specify the location here. #$cvs_prog = "/opt/cvs/bin/cvs" # cvsdiff keyword ignoring (Default: show changes in keywords) # # Changes in CVS keywords can be distracting. For instance, the # $Revision: 1.1 $ keyword will change on each commit. Set this value to true # to exclude changes in keyword fields (adds the -kk option to cvs diff). #$diff_ignore_keywords = true # Don't show diff for removed files (Default: show file's contents) # # If you aren't interested in seeing the contents of a file that was # removed set this option to true. The files will still appear in the index # at the top of the email. #$no_removed_file_diff = true # Email character set (Default: no charset specification) # # Allows the specification of a character set for all generated emails. # The files CVS is dealing with should already be in the character set you # specify -- no transcoding is done. #$charset="ISO-8859-1" # File names in Subject (Default: no filenames in Subject) # # Some people like file names to appear in the email subject. To make # them happy, you can say $files_in_subject = true here. #$files_in_subject = false Index: commitinfo =================================================================== RCS file: /cvsroot/phpslash/CVSROOT/commitinfo,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** commitinfo 30 Aug 2000 03:43:32 -0000 1.1 --- commitinfo 23 Nov 2004 22:06:05 -0000 1.2 *************** *** 14,15 **** --- 14,16 ---- # If the name "ALL" appears as a regular expression it is always used # in addition to the first matching regex or "DEFAULT". + ^phpslash $CVSROOT/CVSROOT/record_lastdir.rb |