How to redirect SVN stderrs to /dev/null using perl - perl

I have a script to check if any data is available on svn repo path but not added into svn. It works fine for me but this gives stderr for adding and sending files like below;
Adding 1/a
Sending 1/a
Transmitting file data ...........
Committed revision 529.
Code:
use strict;
use warnings;
sub notAdded {
my #svnstatus = `svn st`;
foreach my $status (#svnstatus) {
chomp($status);
if ($status =~ m/^?/) {
my ($symble, $left) = split(' ', $status);
system("svn add $left");
}
}
}
&notAdded();
system("svn commit -m 'comment'");
Can anyone please suggest me how can I redirect this error to /dev/null within the script.

The normal way to hide unwanted output with SVN is to use the -q (quiet) flag:
svn -q add nothere
displays nothing.

Or the really easy way:
system("svn add $left 2>/dev/null");

Related

SVN Pre-Commit hook

I am new to svn.Svn repository is in Linux,and developers are working on windows using TSVN client.I implemented a per-commit hook with a proper comment of 32 characters.it is working in Linux.But i tried in TSVN client to commit the code with comment is less than 32 characters it is working.Can any one help me on this.
Here is the code:
$minchars = 10;
$svnlook = '/usr/bin/svnlook';
#--------------------------------------------
$repos = $ARGV[0];
$txn = $ARGV[1];
$comment = `$svnlook log -t "$txn" "$repos"`;
chomp($comment);
if ( length($comment) == 0 ) {
print STDERR "A comment is required!";
exit(1);
} elsif ( length($comment) < $minchars ) {
print STDERR "Comment must be at least $minchars characters.";
exit(1);
}
exit(0);
Try this:
Copy your script to another directory and modify it to use the -r parameter for the svnlook command rather than -t. Then, try it with a commit revision that should have failed.
For example:
$ cd $repo_dir/hooks
$ cp pre-commit $HOME
$ cd
$ vim pre-commit #Change from Transaction to Revision
$ # Revision #123 should have failed
$ ./pre-commit $repo $rev
If the script doesn't produce an error, you can try such things as printing out the comment in quotes to see whether or not it's zero in length, etc. It'll help you find the possible logic error in your script.
You should also use use strict; and use warnings; in your Perl scripts because it easily picks up errors you might not realize you have in your script. It's so easy to forget that a particular variable wasn't necessarily set, or that you mistyped a variable. These pragmas will pick up these types of errors which seem to cause about 90% of the problems in Perl:
#! /usr/bin/env perl
use strict;
use warnings;
my $svnlook = "/usr/bin/svnlook";
my $minchars = 10;
my $repos = $ARGV[0];
my $txn = $ARGV[1];
chomp ( my $comment = qx($svnlook log -t $txn $repos) );
if (not $comment) {
die "A comment is required!\n";
}
elsif ( length $comment < $minchars ) {
die "Comment must be at least $minchars characters.\n";
}
exit 0;
You can also use my pre-commit script. It can be used to verify the length and structure of the commit comment. For example, you could require the commit comment to require a defect ID. It also allows you to control who has commit rights in what parts of your repository and also enforce the use of certain properties on certain files. For example, you might want to make sure all shell scripts and Perl scripts have the svn:eol-style set to either native or LF.
It can also allow users to create a tag, but not allow users to make changes in a tag once created. This prevents users from accidentally checking out a tag, making a change, and then committing it.
And, one more thing:
Take a look at a continuous build system such as Jenkins. One of the things I've discovered is that by merely doing continuous builds, developers naturally improve their commit messages without doing any sort of enforcement.
That's because commit messages are now easily visible. Jenkins shows the changes in each build, whether the build itself was successful, test results, etc. It shows the changes and the commit comments. Suddenly, the commit comments become much more useful to the developers themselves, and they simply do better comments.
You can look at an svn log and see when I implemented Jenkins: Before there were either no commit comments, or such useful things as "reformatted code" or the very helpful "made changes" (both longer than 10 characters). Suddenly the comments are "Fixed BUG-1233. Checked for null pointer before passing it to foo method".

How to see if a plugin is already installed with Perl

I have a Perl script that requires a couple of plugins, for istance nmap. How can I see if the plugins are already installed and, in case they are not, install them? I tryed with the following code but it doesn't work very well, what I am trying to do is capture the "bash: nmap: command not found" output. I tryed with both stdout and stderr.
print "Checking nmap...\n";
my ($stdout, $stderr) = capture {
system("nmap");
};
if ($stdout=~m/command not found/) {
print "nmap not found, installing...\n";
system("rpm -i nmap-4.75-1.26.x86_64.rpm");
}
else {
print "nmap is already installed.\n";
}
How can I see if the plugins are already installed and, in case they are not, install them?
This is not a good idea, do not check for dependencies at run time. Instead you declare the dependencies in your distro meta file and check for them at build time and perhaps abort the build. The easiest way to do so is with requires_external_bin from Module::Install. This integrates nicely into the existing RPM infrastructure. - In other words, learn the basics of packaging and which problems this solves.
If you cannot rely on the user having permission to install it system-wide, create an Alien distro that downloads the source and installs it into the share tree. But once you go down that rabbit hole, be aware that it's deep - you would also need to take care of the deps of nmap itself somehow.
What about this?
my $cmd='which nmap';
my $output = `$cmd 2>&1`;
my $exit_value=$? >> 8;
if ($exit_value){
print "not found $cmd, error: $output\n";
}else{
print "Found $cmd at $output\n";
}
Like daxim pointed out requires_external_bin from Module::Install::External is a good way to ensure that your binary is installed.
If you can't use Module::Install in your application you might try searching the PATH environment variable like this:
use File::Spec;
$\="\n";
print installed($_) ? "$_ installed" : "$_ not installed" for qw/nmap ls cat nosuchfile less/;
sub installed {
my $name = shift;
foreach my $path (File::Spec->path()) {
my $bin = File::Spec->catfile($path, $name);
return $bin if -e -f -x $bin;
}
}
This should output something like this:
$ perl test.pl
nmap installed
ls installed
cat installed
nosuchfile not installed
less installed
The downside of this is of course, that the binary you are looking for has to reside in $PATH.

Error during reading output of "svn log" command

There is "svn: Write error" in case if I try to execute following script.
But if I disable line with "last" there are no any errors.
Why?
#!/usr/bin/perl
my $repos = $ARGV[0];
my $rev = $ARGV[1];
my $repoURL = "file:///" . $ARGV[0];
open (SVNLOG, "svn log -r $rev -v $repoURL | ");
while (my $line = <SVNLOG>) {
last;
}
close(SVNLOG);
Because the svn process is trying to write to a stream (its standard output), and you closed the stream before it could finish. If you don't want to see this error line, you will have to redirect svn standard error to /dev/null, or make sure you read all of its output before closing the file handle.
Not a direct answer you your question, but you should consider using SVN::Client from CPAN instead of using the svn command line tool directly.

How can I access the commited file from a Subversion pre-commit hook in Perl?

I need to do the following:
Write pre-commit hook in Perl
Hook should check all files being committed for presence of some text, and fail if that text is not found
Basically, I need an example of Perl hook that reads files being committed.
I am really looking for some elegant solution with the least amount of code.
Notes:
Hook should use svnlook or other better way to find files.
pre-commit hook:
my $repos = shift;
my $txn = shift;
foreach my $line (`$svnlook changed -t $txn "$repos"`)
{
chomp($line);
if ($line !~ /^([AUD_]).\s\s(.+)$/)
{
print STDERR "Can't parse [$line].\n";
exit(1);
}
else
{
my $action = $1;
my $file = $2;
chomp($file);
#If path has trailing slash, then it is a folder and we want to skip folders
if($file =~ /\/$/)
{
next;
}
my $fileContent = `$svnlook cat -t $txn "$repos" "$file"`;
if ($action =~ /[AU]/)
{
my #lines = split(/\n/, $fileContent );
#Check for whatever you need in this file's content
}
}
}
It sounds like you've got the foundation figured out already:
get list of all files being committed
search each one of them in turn for particular text
if text is found, reject commit
You'll find some information on writing pre-commit hooks in the manual.
It should not be too difficult to modify this example in Python to do what you want. See also the hooks subdirectory of your repository for some templates and hook scripts and contributed hook scripts.

How to search an entire CVS repository (all branches/history/comments)?

If I want to essentially grep every line ever in the repository, is there a way to do it? I know this would take a long time for large projects.
If not all inclusive, at least just the current branch and its entire source history?
Edit: I should have been more explicit. What if I don't have direct access to the server that the CVS repository is on? So I couldn't directly grep the filesystem that has the CVS repository.
There is no way to do this with standard CVS tools without access to the repository. A third party tool out there may do it (I don't know of one, although CS-CVS seems to claim to), but to do it programatically, you would have to do CVS logs on all the relevant files, and then retrieve and search each version reported by cvs in the logs (cvs log is a command line option in CVS that shows you the revision history of any file, but it doesn't show you the contents).
Here's what I recently used, in a case where I didn't have access to the server. It seemed to work that time. Call it from inside a working copy, with cvs in the PATH. Note that this doesn't search commit messages, but you can simply grep 'cvs log' for that.
#!/usr/bin/perl
# Searches CVS diffs and first revisions behind the current working
# directory for an expression (perlre syntax).
# Synopsis: cvsgrep [-n] <search-expression> [<file_1> ... <file_n>]
# -n means that contents of matching files should not be printed to stdout.
use Getopt::Std;
my %options=();
getopts("n",\%options);
my $no_content_dump=$options{"n"};
my $search_term=shift
or die "Error: usage is: cvsgrep [-n] <search-expression>".
" [<file_1> ... <file_n>]";
sub quote_fn
{
my $fn=shift;
$fn =~ s/\'/\'\"\'\"\'/g;
"'".$fn."'";
}
my $args_str;
while(#ARGV)
{
my $arg=shift;
$args_str.=' ' if $args_str;
$args_str.=&quote_fn($arg);
}
print
"Searching for term: $search_term",
($args_str?" in: $args_str":""),
"\n";
open CVSLOGH,"cvs log -N $args_str|" or die "Cannot execute cvs log: $!";
my #files_revisions=();
my $cur_file;
my $cur_revision;
while(<CVSLOGH>)
{
chop;
if(/^Working file\:\s*(.*)$/)
{
$cur_file=$1;
$cur_revision='';
}
elsif(/^revision\s+(.*)$/)
{
$cur_revision=$1;
}
elsif((/^\=\=\=\=/ || /^\-\-\-\-/) && $cur_revision)
{
push #files_revisions,{file=>$cur_file,rev=>$cur_revision};
}
}
close CVSLOGH;
my $matchcount=0;
my $count=0;
my $progress_msg="Scanned %d out of %d commit(s)\r";
my $erase_ln=(" " x (length($progress_msg)+20)) . "\r";
foreach my $file_revision(#files_revisions)
{
printf($progress_msg,$count++,scalar(#files_revisions));
my($file,$rev) = ($file_revision->{file},$file_revision->{rev});
$rev =~ /^(.*\.)([0-9]+)/;
my $revbase=$1;
my $revlastdigit=$2;
my $rev1=$revbase.($revlastdigit - 1);
my $diffcommand = "cvs diff -N -r $rev1 -r $rev ".&quote_fn($file);
open CVSDIFFH,"$diffcommand|" or die "Cannot execute cvs diff: $!";
my $diffresult;
while(<CVSDIFFH>)
{
if(/^[\<\>]/)
{
s/^.//;
$diffresult.=$_;
}
}
close CVSDIFFH;
if($diffresult =~ /$search_term/s)
{
print "${erase_ln}FOUND: in diff for $file $rev1:$rev\n";
$matchcount++;
system($diffcommand) unless $no_content_dump;
}
}
print "${erase_ln}Done ($matchcount match(es)).\n";
It depends on what you're looking for. CVS version files contain all of the edits that have ever happened to the file, in plaintext. So if you're simply looking for all files that contain a particular word, do a recursive grep on the repository.
If you're looking to find specific versions that contain those words, then you're going to have to extract the versions from the repository, which is expensive. However, if you can limit the set of files by grepping the repository, then it's not so bad.