#!/usr/bin/perl # Install this file into fhem_svn:/var/svn/fhem/hooks/ manually # $Id$ use strict; use warnings; sub err($$); my $svnlook='/usr/bin/svnlook'; my $repos=$ARGV[0]; my $txn=$ARGV[1]; my $arg="-t $txn"; #my $arg="-r $txn"; # local testing: perl pre-commit /var/svn/fhem 28315 my @lang = ("EN", "DE"); my $exitCode = 0; use constant TAGS => qw{ul li code b i u table tr td div h4 h3}; my $log = `$svnlook log $arg $repos`; if($log !~ m/^.*:.*$/s) { print STDERR << 'EOF'; A FHEM SVN comment must have the following format module: text-describing-the-change or module: text-describing-the-change (Forum #) EOF exit(1); } my $fList = `$svnlook changed $arg $repos`; foreach my $row (split("\n", $fList)) { chomp($row); my ($type, $fName) = split(" ", $row, 2); next if($type eq "D"); next if(!$fName); my $pl = `svnlook pl $arg $repos $fName`; die ("Setting svn:externals is prohibited") if($pl =~ m/svn:externals/); ################################ # check for 80 chars per line conformity if($fName =~ m/CHANGED/) { my ($cCount, $cLineNo, $tLineNo, $lineNo) = (0,0,0,0,0); open(FILE, "$svnlook $arg cat $repos $fName|") || die("Cant svnlook cat $fName:$!\n"); while(my $l = ) { chomp $l; $lineNo++; $tLineNo = $lineNo if(!$tLineNo && $l =~ /\t/) ; if(length($l) > 80 && !$cLineNo) { $cCount = length($l); $cLineNo = $lineNo; } last if($cLineNo && $tLineNo); } close(FILE); err $fName, "file contains tabulators in line $tLineNo" if($tLineNo); err $fName, "file has over 80 chars/line in line $cLineNo" if($cLineNo); next; } err $fName, "the filename ($fName) contains forbidden characters ". "(A-Za-z0-9_.-% is allowed)" if($fName =~ m/[^A-Za-z0-9_.%-\/]/); next unless($fName =~ /\.pm$/); ################################ # look for SVN Id if($fName =~ m,trunk/fhem/(FHEM|lib)/.*.pm$,) { my $hasId = 0; open(FILE, "$svnlook $arg cat $repos $fName|") || die("Cant svnlook cat $fName:$!\n"); while(my $l = ) { $hasId = ($l =~ /#.*?\$Id(?:\:.+)?\$/) if(!$hasId); err $fName, "DevIo must be loaded via use (Forum #110125)." if($l =~ m/require.*modpath.*DevIo.pm/); } close(FILE); err $fName, "file has no SVN Id as comment" unless($hasId); # check for activated Id property in svn:keywords my $props = `$svnlook $arg propget $repos svn:keywords $fName`; err $fName, "Id property not set in svn:keywords" unless($props =~ /Id/); } ################################ # check the documentation next if($fName !~ m+FHEM/(\d\d)_(.*).pm$+); my ($modNum, $modName) = ($1, $2); my %ninetyniners = ("SUNRISE_EL"=>1, "Utils"=>1); err $fName, "99 is a reserved prefix, not allowed for $modName" if($modNum eq "99" && !$ninetyniners{$modName}); foreach my $lang (@lang) { my $suffix = ($lang eq "EN" ? "" : "_$lang"); my $tag; my %tagcount= (); map { $tagcount{$_} = 0 } TAGS; my %llwct = (); # Last line with closed tag open(MOD, "$svnlook $arg cat $repos $fName|") || die("Cant svnlook cat $fName:$!\n"); my $skip = 1; my $line = 0; my $docCount = 0; my $hasLink = 0; my $hasSummary = 0; my $nrEnd = 0; while(my $l = ) { $line++; err $fName, "DOS line encoding is not supported." if($l =~ m/^=begin html$suffix.*\r/); if($l =~ m/^=item summary$suffix\s+(.+?)\s*$/) { err $fName, "$lang: summary is longer than 80 chars on line $line" if(length($1) > 80); $hasSummary = 1; } if($l =~ m/^=begin html$suffix$/) { $l = ; # skip one line, to be able to repeat join+split err($fName, "$lang: nonempty line after =begin html.") if($l =~ m/^...*$/); $skip = 0; $line++; $nrEnd++; } elsif($l =~ m/^=end html$suffix$/) { $skip = 1; $nrEnd--; } elsif(!$skip) { $docCount++; $hasLink = ($l =~ m/]+)>/i) { err $fName, "$lang line $line: $tag with attributes". " is not allowed\n" ; } $tagcount{$tag} +=()= ($l =~ /<$tag( [^>]+)?>/gi); $tagcount{$tag} -=()= ($l =~ /<\/$tag>/gi); if($tagcount{$tag} < 0) { err $fName, "$lang: negative tagcount for $tag, line $line"; $tagcount{$tag} = 0; } $llwct{$tag} = $line if(!$llwct{$tag} || ($ot && !$tagcount{$tag})); } } } close(MOD); err $fName, "$lang: No document text found" if(!$suffix && !$docCount); err $fName, "$lang: No link" if(!$suffix && $docCount && !$hasLink); err $fName, "$lang: No summary description found" if(!$suffix && $docCount && !$hasSummary); err $fName, "$lang: =end html$suffix: ". ($nrEnd>0 ? "missing":"there are too many")."\n" if($nrEnd); foreach $tag (TAGS) { err $fName, "$lang: Unbalanced $tag ($tagcount{$tag}, ". "last line ok: $llwct{$tag})" if($tagcount{$tag}); } } # Check if the file is in the MAINTAINER.txt, #136270 open(MAINT, "$svnlook $arg cat $repos /trunk/fhem/MAINTAINER.txt|") || die("Cant svnlook cat MAINTAINER.txt:$!\n"); my $fnd = 0; while(my $l = ) { $fnd = 1 if($l =~ m,^FHEM/${modNum}_${modName}.pm,) } close(MAINT); err $fName, "FHEM/${modNum}_${modName}.pm not found in MAINTAINER.txt" if(!$fnd); } exit($exitCode); sub err($$) { my ($fName, $txt) = @_; print STDERR "*** $fName: $txt\n"; $exitCode = 1; }