root/feedmelinks/bin/ciabot_cvs.pl
| Revision 573, 9.1 kB (checked in by hirokai, 4 years ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | #!/usr/bin/perl5 -w |
| 2 | # |
| 3 | # ciabot -- Mail a CVS log message to a given address, for the purposes of CIA |
| 4 | # |
| 5 | # Loosely based on cvslog by Russ Allbery <rra@stanford.edu> |
| 6 | # Copyright 1998 Board of Trustees, Leland Stanford Jr. University |
| 7 | # |
| 8 | # Copyright 2001, 2003, 2004 Petr Baudis <pasky@ucw.cz> |
| 9 | # |
| 10 | # This program is free software; you can redistribute it and/or modify it under |
| 11 | # the terms of the GNU General Public License version 2, as published by the |
| 12 | # Free Software Foundation. |
| 13 | # |
| 14 | # The master location of this file is |
| 15 | # http://pasky.or.cz/~pasky/dev/cvs/ciabot.pl. |
| 16 | # |
| 17 | # This version has been modified a bit, and is available on CIA's web site: |
| 18 | # http://cia.navi.cx/clients/cvs/ciabot_cvs.pl |
| 19 | # |
| 20 | # This program is designed to run from the loginfo CVS administration file. It |
| 21 | # takes a log message, massaging it and mailing it to the address given below. |
| 22 | # |
| 23 | # Its record in the loginfo file should look like: |
| 24 | # |
| 25 | # ALL /usr/bin/perl $CVSROOT/CVSROOT/ciabot_cvs.pl %{,,,s} $USER project from_email dest_email ignore_regexp |
| 26 | # |
| 27 | # IMPORTANT: The %{,,,s} in loginfo is new, and is required for proper operation. |
| 28 | # |
| 29 | # Make sure that you add the script to 'checkoutlist' before |
| 30 | # committing it. You may need to change /usr/bin/perl to point to your |
| 31 | # system's perl binary. |
| 32 | # |
| 33 | # Note that the last four parameters are optional, you can alternatively |
| 34 | # change the defaults below in the configuration section. |
| 35 | # |
| 36 | |
| 37 | use strict; |
| 38 | use vars qw ($project $from_email $dest_email $rpc_uri $sendmail $sync_delay |
| 39 | $xml_rpc $ignore_regexp $alt_local_message_target); |
| 40 | |
| 41 | |
| 42 | ### Configuration |
| 43 | |
| 44 | # Project name (as known to CIA). |
| 45 | $project = 'YOUR_PROJECT_HERE'; |
| 46 | |
| 47 | # The from address in generated mails. |
| 48 | $from_email = 'YOUR_EMAIL_HERE'; |
| 49 | |
| 50 | # Mail all reports to this address. |
| 51 | $dest_email = 'cia@cia.navi.cx'; |
| 52 | |
| 53 | # If using XML-RPC, connect to this URI. |
| 54 | $rpc_uri = 'http://cia.navi.cx/RPC2'; |
| 55 | |
| 56 | # Path to your USCD sendmail compatible binary (your mailer daemon created this |
| 57 | # program somewhere). |
| 58 | $sendmail = '/usr/sbin/sendmail'; |
| 59 | |
| 60 | # Number of seconds to wait for possible concurrent instances. CVS calls up |
| 61 | # this script for each involved directory separately and this is the sync |
| 62 | # delay. 5s looks as a safe value, but feel free to increase if you are running |
| 63 | # this on a slower (or overloaded) machine or if you have really a lot of |
| 64 | # directories. |
| 65 | # Increasing this could be a very good idea if you're on Sourceforge ;) |
| 66 | $sync_delay = 5; |
| 67 | |
| 68 | # This script can communicate with CIA either by mail or by an XML-RPC |
| 69 | # interface. The XML-RPC interface is faster and more efficient, however you |
| 70 | # need to have RPC::XML perl module installed, and some large CVS hosting sites |
| 71 | # (like Savannah or Sourceforge) might not allow outgoing HTTP connections |
| 72 | # while they allow outgoing mail. Also, this script will hang and eventually |
| 73 | # not deliver the event at all if CIA server happens to be down, which is |
| 74 | # unfortunately not an uncommon condition. |
| 75 | $xml_rpc = 0; |
| 76 | |
| 77 | # You can make this bot to totally ignore events concerning the objects |
| 78 | # specified below. Each object is composed of <module>/<path>/<filename>, |
| 79 | # therefore file Manifest in root directory of module gentoo will be called |
| 80 | # "gentoo/Manifest", while file src/bfu/inphist.c of module elinks will be |
| 81 | # called "elinks/src/bfu/inphist.c". Easy, isn't it? |
| 82 | # |
| 83 | # This variable should contain regexp, against which will each object be |
| 84 | # checked, and if the regexp is matched, the file is ignored. Therefore ie. to |
| 85 | # ignore all changes in the two files above and everything concerning module |
| 86 | # 'admin', use: |
| 87 | # |
| 88 | # $ignore_regexp = "^(gentoo/Manifest|elinks/src/bfu/inphist.c|admin/)"; |
| 89 | $ignore_regexp = ""; |
| 90 | |
| 91 | # It can be useful to also grab the generated XML message by some other |
| 92 | # programs and ie. autogenerate some content based on it. Here you can specify |
| 93 | # a file to which it will be appended. |
| 94 | $alt_local_message_target = ""; |
| 95 | |
| 96 | |
| 97 | |
| 98 | |
| 99 | ### The code itself |
| 100 | |
| 101 | use vars qw ($user $module $tag @files $logmsg $message); |
| 102 | |
| 103 | my @dir; # This array stores all the affected directories |
| 104 | my @dirfiles; # This array is mapped to the @dir array and contains files |
| 105 | # affected in each directory |
| 106 | |
| 107 | |
| 108 | # A nice nonprinting character we can use as a separator relatively safely. |
| 109 | # The commas in loginfo above give us 4 commas and a space between file |
| 110 | # names given to us on the command line. This is the separator used internally. |
| 111 | # Now we can handle filenames containing spaces, and probably anything except |
| 112 | # strings of 4 commas or the ASCII bell character. |
| 113 | # |
| 114 | # This was inspired by the suggestion in: |
| 115 | # http://mail.gnu.org/archive/html/info-cvs/2003-04/msg00267.html |
| 116 | # |
| 117 | $" = "\7"; |
| 118 | |
| 119 | ### Input data loading |
| 120 | |
| 121 | |
| 122 | # These arguments are from %s; first the relative path in the repository |
| 123 | # and then the list of files modified. |
| 124 | |
| 125 | @files = split (' ,,,', ($ARGV[0] or '')); |
| 126 | $dir[0] = shift @files or die "$0: no directory specified\n"; |
| 127 | $dirfiles[0] = "@files" or die "$0: no files specified\n"; |
| 128 | |
| 129 | |
| 130 | # Guess module name. |
| 131 | |
| 132 | $module = $dir[0]; $module =~ s#/.*##; |
| 133 | |
| 134 | |
| 135 | # Figure out who is doing the update. |
| 136 | |
| 137 | $user = $ARGV[1]; |
| 138 | |
| 139 | |
| 140 | # Use the optional parameters, if supplied. |
| 141 | |
| 142 | $project = $ARGV[2] if $ARGV[2]; |
| 143 | $from_email = $ARGV[3] if $ARGV[3]; |
| 144 | $dest_email = $ARGV[4] if $ARGV[4]; |
| 145 | $ignore_regexp = $ARGV[5] if $ARGV[5]; |
| 146 | |
| 147 | |
| 148 | # Parse stdin (what's interesting is the tag and log message) |
| 149 | |
| 150 | while (<STDIN>) { |
| 151 | $tag = $1 if /^\s*Tag: ([a-zA-Z0-9_-]+)/; |
| 152 | last if /^Log Message/; |
| 153 | } |
| 154 | |
| 155 | $logmsg = ""; |
| 156 | while (<STDIN>) { |
| 157 | next unless ($_ and $_ ne "\n" and $_ ne "\r\n"); |
| 158 | s/&/&/g; |
| 159 | s/</</g; |
| 160 | s/>/>/g; |
| 161 | $logmsg .= $_; |
| 162 | } |
| 163 | |
| 164 | ### Remove to-be-ignored files |
| 165 | |
| 166 | $dirfiles[0] = join (' ', |
| 167 | grep { |
| 168 | my $f = "$module/$dir[0]/$_"; |
| 169 | $f !~ m/$ignore_regexp/; |
| 170 | } split (/\s+/, $dirfiles[0]) |
| 171 | ) if ($ignore_regexp); |
| 172 | exit unless $dirfiles[0]; |
| 173 | |
| 174 | |
| 175 | |
| 176 | ### Sync between the multiple instances potentially being ran simultanously |
| 177 | |
| 178 | my $sum; # _VERY_ simple hash of the log message. It is really weak, but I'm |
| 179 | # lazy and it's really sorta exceptional to even get more commits |
| 180 | # running simultanously anyway. |
| 181 | $sum = 0; |
| 182 | map { $sum += ord $_ } split(//, $logmsg); |
| 183 | |
| 184 | my $syncfile; # Name of the file used for syncing |
| 185 | $syncfile = "/tmp/cvscia.$project.$module.$sum"; |
| 186 | |
| 187 | |
| 188 | if (-f $syncfile and -w $syncfile) { |
| 189 | # The synchronization file for this file already exists, so we are not the |
| 190 | # first ones. So let's just dump what we know and exit. |
| 191 | |
| 192 | open(FF, ">>$syncfile") or die "aieee... can't log, can't log! $syncfile blocked!"; |
| 193 | print FF "$dirfiles[0]!@!$dir[0]\n"; |
| 194 | close(FF); |
| 195 | exit; |
| 196 | |
| 197 | } else { |
| 198 | # We are the first one! Thus, we'll fork, exit the original instance, and |
| 199 | # wait a bit with the new one. Then we'll grab what the others collected and |
| 200 | # go on. |
| 201 | |
| 202 | # We don't need to care about permissions since all the instances of the one |
| 203 | # commit will obviously live as the same user. |
| 204 | |
| 205 | # system("touch") in a different way |
| 206 | open(FF, ">>$syncfile") or die "aieee... can't log, can't log! $syncfile blocked!"; |
| 207 | close(FF); |
| 208 | |
| 209 | exit if (fork); |
| 210 | sleep($sync_delay); |
| 211 | |
| 212 | open(FF, $syncfile); |
| 213 | my ($dirnum) = 1; # 0 is the one we got triggerred for |
| 214 | while (<FF>) { |
| 215 | chomp; |
| 216 | ($dirfiles[$dirnum], $dir[$dirnum]) = split(/!@!/); |
| 217 | $dirnum++; |
| 218 | } |
| 219 | close(FF); |
| 220 | |
| 221 | unlink($syncfile); |
| 222 | } |
| 223 | |
| 224 | |
| 225 | |
| 226 | ### Compose the mail message |
| 227 | |
| 228 | |
| 229 | my ($VERSION) = '2.3'; |
| 230 | my ($URL) = 'http://cia.navi.cx/clients/cvs/ciabot_cvs.pl'; |
| 231 | my $ts = time; |
| 232 | |
| 233 | $message = <<EM |
| 234 | <message> |
| 235 | <generator> |
| 236 | <name>CIA Perl client for CVS</name> |
| 237 | <version>$VERSION</version> |
| 238 | <url>$URL</url> |
| 239 | </generator> |
| 240 | <source> |
| 241 | <project>$project</project> |
| 242 | <module>$module</module> |
| 243 | EM |
| 244 | ; |
| 245 | $message .= " <branch>$tag</branch>" if ($tag); |
| 246 | $message .= <<EM |
| 247 | </source> |
| 248 | <timestamp> |
| 249 | $ts |
| 250 | </timestamp> |
| 251 | <body> |
| 252 | <commit> |
| 253 | <author>$user</author> |
| 254 | <files> |
| 255 | EM |
| 256 | ; |
| 257 | |
| 258 | for (my $dirnum = 0; $dirnum < @dir; $dirnum++) { |
| 259 | map { |
| 260 | $_ = $dir[$dirnum] . '/' . $_; |
| 261 | s#^.*?/##; # weed out the module name |
| 262 | s/&/&/g; |
| 263 | s/</</g; |
| 264 | s/>/>/g; |
| 265 | $message .= " <file>$_</file>\n"; |
| 266 | } split($", $dirfiles[$dirnum]); |
| 267 | } |
| 268 | |
| 269 | $message .= <<EM |
| 270 | </files> |
| 271 | <log> |
| 272 | $logmsg |
| 273 | </log> |
| 274 | </commit> |
| 275 | </body> |
| 276 | </message> |
| 277 | EM |
| 278 | ; |
| 279 | |
| 280 | |
| 281 | |
| 282 | ### Write the message to an alt-target |
| 283 | |
| 284 | if ($alt_local_message_target and open (ALT, ">>$alt_local_message_target")) { |
| 285 | print ALT $message; |
| 286 | close ALT; |
| 287 | } |
| 288 | |
| 289 | |
| 290 | |
| 291 | ### Send out the XML-RPC message |
| 292 | |
| 293 | |
| 294 | if ($xml_rpc) { |
| 295 | # We gotta be careful from now on. We silence all the warnings because |
| 296 | # RPC::XML code is crappy and works with undefs etc. |
| 297 | $^W = 0; |
| 298 | $RPC::XML::ERROR if (0); # silence perl's compile-time warning |
| 299 | |
| 300 | require RPC::XML; |
| 301 | require RPC::XML::Client; |
| 302 | |
| 303 | my $rpc_client = new RPC::XML::Client $rpc_uri; |
| 304 | my $rpc_request = RPC::XML::request->new('hub.deliver', $message); |
| 305 | my $rpc_response = $rpc_client->send_request($rpc_request); |
| 306 | |
| 307 | unless (ref $rpc_response) { |
| 308 | die "XML-RPC Error: $RPC::XML::ERROR\n"; |
| 309 | } |
| 310 | exit; |
| 311 | } |
| 312 | |
| 313 | |
| 314 | |
| 315 | ### Send out the mail |
| 316 | |
| 317 | |
| 318 | # Open our mail program |
| 319 | |
| 320 | open (MAIL, "| $sendmail -t -oi -oem") or die "Cannot execute $sendmail : " . ($?>>8); |
| 321 | |
| 322 | |
| 323 | # The mail header |
| 324 | |
| 325 | print MAIL <<EOM; |
| 326 | From: $from_email |
| 327 | To: $dest_email |
| 328 | Content-type: text/xml |
| 329 | Subject: DeliverXML |
| 330 | |
| 331 | EOM |
| 332 | |
| 333 | print MAIL $message; |
| 334 | |
| 335 | |
| 336 | # Close the mail |
| 337 | |
| 338 | close MAIL; |
| 339 | die "$0: sendmail exit status " . ($? >> 8) . "\n" unless ($? == 0); |
| 340 | |
| 341 | # vi: set sw=2: |
Note: See TracBrowser for help on using the browser.