Hack 9. Automate Checkin Code Reviews


Let Perl::Tidy be your first code reviewon every Subversion checkin!

In a multideveloper project, relying on developers to follow the coding standards without fail and to run perltidy against all of their code ("Enforce Local Style" [Hack #7]) before every checkin is unrealistic, especially because this is tedious work. Fortunately, this is an automatable process. If you use Subversion (or Svk), it's easy to write a hook that checks code for tidiness, however you define it.

The Hack

For various reasons, it's not possible to manipulate the committed files with a pre-commit hook in Subversion. That's why this is a hack.


Within your Subversion repository, copy the hooks/post-commit.tmpl file to hooks/post-commitunless you already have the file. Remove all code that runs other commands (again, unless you're already using it). Add a single line:

perl /usr/local/bin/check_tidy_file.pl "$REPOS" "$REV"

Adjust the file path appropriately. Make the hooks/post-commit file executable with chmod +x on Unix.

Finally, save the check_tidy_file.pl program to the path you used in the file. The program is:

#!/usr/bin/perl use strict; use warnings; use Perl::Tidy;                 use File::Temp; use File::Spec::Functions; my $svnlook      = '/usr/bin/svnlook'; my $diff         = '/usr/bin/diff -u'; # eat the arguments so as not to confuse Perl::Tidy my ($repo, $rev) = @ARGV; @ARGV            = ( ); my @diffs; for my $changed_file (get_changed_perl_files( $repo, $rev )) {     my $source = get_revision( $repo, $rev, $changed_file );     Perl::Tidy::perltidy( source => \\$source, destination => \\(my $dest) );     push @diffs, get_diff( $changed_file, $source, $dest ); } report_diffs( @diffs ); sub get_changed_perl_files {     my ($repo, $rev) = @_;     my @files;     for my $change (\Q$svnlook changed $repo -r $rev\Q)     {         my ($status, $file) =  split( /\\s+/, $change );         next unless $file   =~ /\\.p[lm]\\z/;         push @files, $file;     }     return @files; } sub get_revision {     my ($repo, $rev, $file) = @_;     return scalar \Q$svnlook cat $repo -r $rev $file\Q; } sub get_diff {     my $filename        = shift;     return if $_[0] eq $_[1];     my $dir   = File::Temp::tempdir( );     my @files = map { catdir( $dir, $filename . $_ ) } qw( .orig .tidy );     for my $file (@files)     {         open( my $out, '>', $file ) or die "Couldn't write $file: $!\\n";         print $out shift;         close $out;     }     return scalar \Q$diff @files\Q; } sub report_diffs {     for my $diff (@_)     {         warn "Error:\\n$diff\\n";     } }

When Subversion finishes committing a checkin to the repository, it calls the hooks/post-commit script, which itself launches other programs, passing the repository path and the number of the just-committed revision. This program uses the svnlook command to find the modified files, skipping everything that's not a Perl program or module (files ending in .pl or .pm).

For each of these files, it grabs the entire contents from the just-completed revision and runs it through Perl::Tidy (the actual engine of the perltidy utility). If the resulting file is the same as the revision, everything is fine. Otherwise, it runs a diff utility to see the changes necessary to make the file tidy. From there, report_diffs( ) receives a list of these differences.

Hacking the Hack

As it is now, the program is only useful when run directly with the path to the repository and a revision number. It could instead write the differences to a file, automatically check in the revised versions in a new checkin, or e-mail the diffs to a list of programmers.

To use a .perltidyrc file with the tidier program, add the perltidy => $rcfile_path arguments to the perltidy( ) call, where $rcfile_path contains the path to the .perltidyrc file to use.



Perl Hacks
Perl Hacks: Tips & Tools for Programming, Debugging, and Surviving
ISBN: 0596526741
EAN: 2147483647
Year: 2004
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net