Displaying CVS version control numbers in webpages.

By Mark Nielsen

  1. Introduction
  2. Perl script displaying version number
  3. Security
  4. Conclusion
  5. References

Introduction

For a few years, I have been writing articles. A silly person named Phil Hunter from Central Ohio Linux User Group suggested to me several times to put version control on the articles. Of course, that is just one more thing to do in a long list of things to do. As usual, when certain things come together, then I pick up on old ideas and make them happen. The conditions were the following:
  1. After working at Cisco in the EMAN and INFOSEC groups, I learned how to use CVS in some detail. I printed out the CVS manual available at genericbooks.com. Now I use CVS for everything I do.
  2. After using SSI, PHP, Mason, ASP, EmbPerl, ePerl, and other Perl server side include modules for Apache for quite a few years, it became trivial to create a Perl script to open of a CVS file, read it contents, and print out the results.
  3. I have a lot of documents, and it is time I keep version control.

Perl script displaying version number

In order to make it easy so that every document can use the same command, I use this server side include command that comes with Apache. PHP, Perl ASP, Mason, and other server side scripting languages also have similar commands.
<!--#include virtual="/ssi/cvs_version.pl"  -->

I also have it configured so that all webpage can use server side includes commands by putting this into my apache httpd.conf file.


<Directory "/usr/local/apache_gnujobs/htdocs">

    Options All Indexes FollowSymLinks MultiViews ExecCGI Includes 

    AllowOverride All

    Order allow,deny
    Allow from all
</Directory>

AddType text/html .shtml
AddHandler server-parsed .shtml .html .htm .shtm

Here is a text version of the perl script, or you can also copy and paste it from this document below.

#!/usr/bin/perl

print "Content-type: text/html\n\n\n\n";

  ### Get the name of the file being requested.
my $Temp = $ENV{'REQUEST_URI'};
my $Cvs = $Temp;

  ### Split the url by "/".
my (@Junk) = split(/\//, $Cvs);

  ### Get the end of the url, which is the filename.
my $File = pop @Junk;
$Cvs =~ s/[^\/]+$//g;

  ### Attach the document root directory so we get the complete path to the
  ### file on our computer server. Also, attach the CVS/Entries name so that
  ### we get the CVS information.
$Cvs = $ENV{'DOCUMENT_ROOT'} . $Cvs . "CVS/Entries"; 

  ### Open the file, and if we find a match, record it to $Match
my $Match = "";
open(FILE,$Cvs);
while (my $Line = <FILE>) 
  {
  if ($Line =~ /$File/) {$Match = $Line; chomp $Line}
  }
close FILE;

   ### If match is not found, print not found, otherwise get the information.
if ($Match eq "") {print "No CVS information found. '$File'\n";}
else 
  {
     ### Get the information we want and print it out.
  my ($Junk,$File,$Version,$Date,@Junk) = split(/\//, $Match);
  print "Version <b>$Version</b> : Date Last Changed <b>$Date</b>\n";
  }

Security

There is a potential problem with security when you use my perl script above. Anybody can read your files located in the CVS directories. I don't know if this is a big concern to most people, and it really isn't a concern to me, but just in case, I blocked reading of the files in any CVS directory by using these commands in my httpd.conf file for my Apache webserver. This is a simple way of doing it. I am not going to have any files or directories that end their names with the words "Root", "CVS", "Repository", or "Entries", so to me it works fine.
<Files ~ "CVS$">
    Order allow,deny
    Deny from all
</Files>
<Files ~ "Root$">
    Order allow,deny
    Deny from all
</Files>
<Files ~ "Repository$">
    Order allow,deny
    Deny from all
</Files>
<Files ~ "Entries$">
    Order allow,deny
    Deny from all
</Files>

Conclusion

Using CVS to manage version control of documents, programs, and scripts is great. Using CVS and my perl script to display the version number of the document is a satisfactory way of keeping version control of your documents. I have no need to make it more advanced, so it works for me.

Some silly things to do:

  1. Check to see if CVS directory and files exist before opening up to read them. Doesn't matter since I assume the person putting in the server side include command should know what they are doing and it wouldn't really do anything bad anyways.
  2. Use a better regular expression match which will make the webserver reject "/CVS/" anywhere in the requested url from the client browser. This would be better than rejecting only specific files. This doesn't matter to me.
  3. This doesn't work with Alias in the httpd.conf file. At least, I don't think it does.
  4. Check to see if the date of the file matches the date in CVS. This lets you know if your document is out of sync with the CVS respository. This isn't important to me.

Lars Kellogg-Stedman mentions that there are simpler ways to include revision information in your documentation using CVS options

I've just recently been glancing over your Linuz Gazette article, and I
can't help but think that you've gone to a lot of trouble -- and wasted
CPU cycles -- to do something that's relatively simple.

CVS, based on RCS, supports certain keywords that can be placed in your
documents that get automatically expanded when a document is checked out
or comittted.  Of particular interest, given the content of your article,
are the follow two tags:

  (1) $Revision: 1.5 $
  (2) $Date: 2001/03/15 17:21:01 $

For example, if I was to create a document that looked like this:

  This is a test document.

  Article version: $Revision: 1.5 $ Date: $Date: 2001/03/15 17:21:01 $

And this article was under control of CVS, then when after a checkin the
document might actually look something like this:

  This is a test document.

  Article version: $Revision: 1.5 $ Date: $Date: 2001/03/15 17:21:01 $

This contains almost exactly the information you want.  It may not allow
the same control over appearance that your solution does -- but it doesn't
involve running a script *every time* the page is loaded.

You can find a list of these rcs tags in the 'co' man page, and I assume
they're also in the CVS documentation.

References

  1. If this article changes, it will be available here http://www.gnujobs.com/Articles/16/CVS_SSI.html.
  2. CVS: Concurrent Versions System, by Mark Nielsen

Mark works as an independent consultant donating time to causes like GNUJobs.com, writing articles, writing free software, and working as a volunteer at eastmont.net.

Copyright © 1/2001 Mark Nielsen
Article