#!/usr/bin/env perl # # PSYC notification script for Git repositories, written by the lynX and improved by tg # based on cvs2psyc and bartman's sender-side post-commit hook. # # it simply uses git log to find out where it stands. for more elaborate # ways, find inspiration in contrib/hooks/post-receive-email from the git # distribution. # # HOW TO INSTALL: # # put git2psyc.conf in the project.git/ directory and # make a link to this script in the project.git/hooks/post-receive: # # cd project.git/hooks # ln -s /path/to/git2psyc post-receive # # or if you want to use a different config file, call it with an argument: # # #!/bin/sh # git2psyc /path/to/git2psyc.conf # # HOW TO DEBUG: # set $debug to 1 in the config then run e.g.: # echo a4ddfedb23c106fe484831d0e6df6f7f273f7db9 6a9d18cfa5d3334f446ff74710ad7cde5e0026ef refs/heads/master | ./git2psyc git2psyc.conf use strict; use warnings; use Cwd qw/cwd realpath/; use File::Basename qw/basename/; use Socket; chdir '.git' if -d '.git'; my $confname = 'git2psyc.conf'; my $config; $config = $ARGV[0] if @ARGV && -f $ARGV[0]; $config = $confname if !$config && -f $confname; $config = "../$confname" unless $config && -f $config; $config = "../../$confname" unless $config && -f $config; $config = "../../../$confname" unless $config && -f $config; die "Config file not found" unless $config && -f $config; our ($project, $target, $host, $port, $webview, $psycver, $debug); our $pwd = cwd; # extract the project name from the path if nothing provided # this works for either project/ or project.git/ directories $project = ( $pwd =~ m#/([^/]+)/?\.git\b# ) ? $1 : '-'; do $config; # GIT handling: my (@commits, @added, @deleted); # received refs with old & new hashes while () { # SP SP LF print STDERR ">> $_" if $debug; next unless m,([0-9a-f]+) ([0-9a-f]+) (\S+),; #next if $3 ne 'refs/heads/master'; # only process master branch my ($old, $new, $ref) = ($1, $2, $3); push @added, $ref if $old =~ /^0+$/; push @deleted, $ref if $new =~ /^0+$/; next if $old =~ /^0+$/ || $new =~ /^0+$/; $_ = `git whatchanged --pretty=fuller --shortstat $old..`; print STDERR if $debug; # extract commit hashes & msgs from git output while (/commit\s+([0-9a-f]+)\n Author:\s+(.*?)\ <(.*?)>\n AuthorDate:\s+(.*?)\n Commit:\s+(.*?)\ <(.*?)>\n CommitDate:\s+(.*?)\n \n \s+(.*?)\n .*? ^\ (?:(\d+)\ files?\ changed)? (?:,\ (\d+)\ insertion\S*)? (?:,\ (\d+)\ deletion\S*)? /gmsx) { # after so many years, somebody broke the output of this git command! push @commits, { ref => $ref, hash => $1, abbrev_hash => substr($1, 0, 8), author => $2, author_email => $3, author_date => $4, commit => $5, commit_email => $6, commit_date => $7, title => $8, stat_files_changed => $9 || '0', stat_insertions => $10 || '0', stat_deletions => $11 || '0', }; } } if ($debug) { print STDERR "\n\nData::Dumper of \@commits, \@added, \@deleted:\n"; use Data::Dumper; print STDERR Dumper \@commits, \@added, \@deleted; print STDERR "\n\n.\n"; } die <= 1.0) { $delim = '|'; $delimre = qr/\|/; } else { $delim = '.'; $delimre = qr/\./; } if ($target =~ m#^psyc://([\w.-]+)(?::(\d+))?#i) { $host ||= $1; $port ||= $2 || 4404; } else { die "target invalid: $target"; } my $iaddr = inet_aton($host) or die "could not resolve host: $host"; my $paddr = sockaddr_in($port, $iaddr); # there is no real reason why we aren't using UDP here... socket(S, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "socket: $!"; connect(S, $paddr) or die "connect: $!"; select S; $|=1; select STDOUT; print S <) && !/^$delimre/) { die "Error while establishing circuit: invalid greeting"; } # wait for first packet while (defined($_ = ) && !/^$delimre/) { # print if s/^=//; } #print "\n"; # SEND THE MESSAGES: # reverse them so they're in chronological order for my $c (reverse @commits) { my $packet = <{ref} :_hash_commit_long\t$c->{hash} :_hash_commit\t$c->{abbrev_hash} :_page_commit\t$webview$c->{abbrev_hash} :_name_editor\t$c->{author} :_name_committer\t$c->{commit} :_mailto_editor\t$c->{author_email} :_mailto_committer\t$c->{commit_email} :_date_editor\t$c->{author_date} :_date_committer\t$c->{commit_date} :_comment\t$c->{title} :_stat_files_changed\t$c->{stat_files_changed} :_stat_insertions\t$c->{stat_insertions} :_stat_deletions\t$c->{stat_deletions} _notice_update_software_git [_project]: [_name_editor] "[_comment]" ([_stat_files_changed] files: +[_stat_insertions] -[_stat_deletions]) [_page_commit] $delim X print S $packet; print STDERR $packet if $debug; print " \"$c->{title}\"\n" if $c->{title}; } sub print_refs { my $op = shift; my $_list_refs = join '|', @_; my @refs; for (@_) { s,^refs/,,; push @refs, $_; } my $_refs = join ', ', @refs; my $packet = <