Managing the Source

Chapter 6 - Module Maintenance
by?Sam Tregar?
Apress ? 2002
has companion web siteCompanion Web Site

As maintainer, you are solely responsible for the code in your module. A maintainer has to act as both an editor and a librarian. This section will describe solutions, both technological and social, to some of the most common problems you'll face managing the source code of your module.

Feeping Creaturism

All software, open source and closed, is vulnerable to a disease known as feeping creaturism. Once referred to by the less frightening creeping featurism, a new name was devised to more effectively render its hideous properties. A project suffering from feeping creaturism will exhibit symptoms of wild growth-new features sprouting from old at a wild rate. Typically the project will grow less stable with each passing release as new features create new bugs. A simple module thus afflicted becomes complex and finally moribund with features.

You can fight this disfiguring disease by keeping careful control of the growth of your modules. This is considerably harder than it sounds-every module must grow, and every feature request meets someone's need. Your only defense is to adhere to a strict definition of your module's place in world. When a request for a new feature arrives, you must evaluate it against this definition. Ask the questions, "Is it a natural extension that merely allows the module to function better in its problem space? Or is it a move into another problem space entirely, a symptom of the disease?"

Saying no isn't easy. Particularly when a developer has gone to the trouble of coding a patch, it can be very hard to turn him or her away. Your best defense is to be clear about what you see as the mission of the module and why you think his or her proposed addition is outside those bounds. Another useful strategy is to try to determine the motivation behind the change-there may be another module that user could use in conjunction to yours that would yield the desired behavior. This is in no way a failure on the part of your module-no module can be all things to all people.

Often a request that must be denied can point the way towards adding extensibility. Adding extensibility means giving the person requesting a new feature a means to achieve their goals, even though you aren't going to implement it for the person making the request. Of course, if each time you add extensibility you only deal with one feature request, then you haven't gained much. Instead your goal should be to deal with a whole class of requests.

As an example of adding extensibility, a key turning point in HTML::Template's development came with the addition of the filter option, which allows users to extend the supported template syntax. All throughout HTML::Template's life users had been asking for this or that special tag, each for its own special purpose. In the early days, I had no recourse but to respond that their special tag wasn't going to be part of HTML::Template and try to let them down as lightly as possible. But once the filter option was implemented, I could show them how to add support for the tag themselves. Not only has this improved HTML::Template's utility for its users, but it has also allowed a small number of extension modules to be developed that use the filter option to implement new functionality. All that, and the fundamental simplicity of the module itself was not harmed at all! By adding extensibility to address a class of problems, I was able to divert requests that could easily have resulted in feeping creaturism.

Another useful technique is to appeal to the silent majority of conservative users who populate most module mailing lists. Often a new feature will attract a number of radical proponents who will flood the mailing list with supportive arguments. If you allow your judgment to be swayed by the appearance of unanimity, you'll be doing the larger user community a disservice. Instead, appeal to them directly. Say publicly, "Do you guys agree with these nuts, or what?" I've found that when faced with the possibility that some craziness is about to be accepted, the wise hermits in the audience will come out of their shells.

Although uncommon, you should also be on the lookout for the opposite of feeping creaturism-stagnation. Every module must change with time to remain vital. Don't let a fear of wild growth prevent perfectly reasonable additions. After all, seeing your module grow in ways you never anticipated is part of the fun of open-source development!

Working with Patches

Accepting code contributions from other developers means working with patches. A patch is a text file that encodes the differences between two versions of the same file. Patch files are meant to be used with the patch program, written by Larry Wall (yes, that Larry Wall). Patch files are generated by the diff program.[5] diff looks at two versions of a file and outputs the differences. For this reason, patches are often called diffs, causing much confusion for the uninitiated.

A typical use of diff and patch is as a way to transmit a change to the source from a developer to a maintainer. The developer uses diff to produce a patch file that describes the changes made to the files. The patch file is then submitted to the maintainer of the project, often by e-mailing it to a mailing list or directly to the maintainer. If the maintainer accepts the change, then he or she uses the patch program to apply the changes described in the patch file. The end result is that the change made by a developer is transmitted to the project maintainer without requiring the entire set of changed files to be exchanged.

Tip 

The patch program is able to skip any leading junk in a file containing a valid patch. This means that developers can append patches to e-mails, and you can send the entire e-mail to patch without needing to manually extract the patch file! This is also why maintainers often prefer patches to be appended to e-mail rather than attached.

Creating a Single-File Patch

The simplest type of patch describes changes to a single file. As an example, I'll make a small change to the Data::Counter module introduced in previous chapters. I'll change the count() routine in Counter.pm to work as a class method rather than as a simple function. First, I copy Counter.pm to Counter.pm.orig:

 $ cp Counter.pm Counter.pm.orig 

Next I edit Counter.pm, changing the definition of count() from

 sub count { return scalar @_ } 

to this:

 sub count {   my $pkg = shift;   return scalar @_; } 

Now I can produce a patch file by running the diff command on the files and redirecting the output to a file called newcount.patch. The order of the arguments is important-always put the old file first and then the new file:

 diff Counter.pm.orig Counter.pm > newcount.patch 

The diff program describes the differences in terms of what parts of the file would have to change to get from one version to a different version. Since you want to be able to see how to get from the original version to the new version, the original version goes first. Reversing the filenames would tell you exactly how to get from the new version back to the old version.

After this command, newcount.patch contains the following:

 32c32,35 < sub count { return scalar @_ } --- > sub count { >   my $pkg = shift; >   return scalar @_; > } 

Lines that begin with < are the lines that have been removed from the old file, and the lines beginning with > are the lines that have been added in the new file.

The diff program can represent differences between files in a number of different formats. The patch program can be used with the diff format shown previously, but it's not the preferred format. To produce a better patch, use the -u option:

 diff -u Counter.pm.orig Counter.pm > newcount.patch 

Now newcount.patch contains the following:

 --- Counter.pm.orig  Sat Jan 26 17:37:29 2002 +++ Counter.pm       Sat Jan 26 17:44:10 2002 @@ -29,7 +29,10 @@  # Preloaded methods go here. -sub count { return scalar @_ } +sub count { +   my $pkg = shift; +   return scalar @_; +}  1;  __END__ 

This format is known as a unified diff. In a unified diff, lines that begin with a + (plus sign) are the lines that have been added, and lines beginning with a (minus sign) are the lines that have been removed. Lines without a leading character are context-lines that are unchanged between old and new that surround the changes.[6] Including context enables patch to work even if the original files have changed since the patch was created. The additional context lines also help human readers understand the patch; with a little practice, you'll be able to read and understand patches as easily as reading the original source.

Unified diffs also contain a header listing the old and new filenames and their modification dates. As I'll demonstrate later, this allows changes to multiple files to be included in one patch.

Applying a Single-File Patch

Applying patches is usually an easy process. The patch program accepts the patch file on standard input (known in Perl as STDIN). By default, it finds target files automatically by examining the patch header and modifies them in place.

Tip 

Include the -backup option to patch when you're first using patch to avoid losing data. This creates a backup file before patch changes the file.

To apply the patch created previously to the original version of Counter.pm, I can use this command:

 $ patch < newcount.patch patching file 'Counter.pm' 

The second line is output from patch-the name of the file to patch was correctly deduced from the patch file header.

There are many reasons that patches can fail to apply cleanly, from file corruption to changes made in the target file that conflict with the patch. When patch can't apply a patch file, it will issue an error message and create a reject file containing the pieces of the patch, known as hunks, that could not be applied. For more information about how patch deals with failure, see the patch manual page.

Creating Multifile Patches

It's common to need to change more than one file to bring about a single change. For example, you might need to add a new test to a module's test script to accompany a change in the module code. Although it is possible to use the technique introduced earlier and simply include multiple diff files, this will quickly become unmanageable both for the sender and the receiver. Instead, you'll probably want to use diff's support for creating diffs between two directories, as I'll demonstrate here.

For example, since I changed the way Data::Counter's count() routine is called, I need to change the test suite accordingly. Simply, I must modify the t/02basic.t file to call count() as a class method rather than a simple function. Since this will now be a multifile patch, I prepare by copying the entire Counter directory to Counter.orig (in contrast to simply copying the Counter.pm file to Counter.pm.orig as shown earlier):

 cp -rp Counter/ Counter.orig/ 

Now I'll make the change to the two files and generate the diff with this command:

 diff -ur Counter.orig Counter > newcount.patch 

The additional -r option tells diff to operate recursively. The contents of newcount.patch after this command are as follows:

 diff -ur Counter.orig/Counter.pm Counter/Counter.pm --- Counter.orig/Counter.pm  Sun Jan 27 14:59:49 2002 +++ Counter/Counter.pm       Sun Jan 27 15:00:00 2002 @@ -29,7 +29,10 @@  # Preloaded methods go here. -sub count { return scalar @_ } +sub count { +  my $pkg = shift; +  return scalar @_; +}  1;  __END__ diff -ur Counter.orig/t/02basic.t Counter/t/02basic.t --- Counter.orig/t/02basic.t  Sun Dec 9 21:29:35 2001 +++ Counter/t/02basic.t       Sun Jan 27 15:00:51 2002 @@ -1,3 +1,3 @@  use Test::More tests => 1;  use Data::Counter; -is(Data::Counter::count('foo', 'bar'), 2); +is(Data::Counter->count('foo', 'bar'), 2); 

As you can see, this patch contains differences to both Counter.pm and 02basic.t.

Applying Multifile Patches

To apply the multifile patch created using the technique shown in the preceding section, you need to use the -p1 option. This option tells patch to strip a single leading directory from the filenames specified in the diff headers. For example, the header for the changes to 02basic.t reads as follows:

 --- Counter.orig/t/02basic.t  Sun Dec 9 21:29:35 2001 +++ Counter/t/02basic.t       Sun Jan 27 15:00:51 2002 

When patch reads this, it will look for Counter/t/02basic.t, but since you'll be applying the patch from inside your source directory, this file won't exist. By using the -p1 option you tell patch to look for t/02basic.t instead:

 patch -p1 < newcount.patch 
Note 

When you send a patch that will require a -p1 option to be applied, you should specify that in your message. In general, it's a good idea to include application instructions when you submit a patch.

Read the Fine Manuals

For more information on diff and patch, see the manual pages on your system. Both programs support far more options than can be adequately described here.

Using CVS

Using the Concurrent Versions System (CVS) can make your life as a module maintainer significantly easier while also enhancing community interaction. In particular, CVS can make it easier for other developers to contribute to your module and for you to manage their contributions. As a bonus, CVS also functions as a powerful backup system for your code-no more self-flagellation after a careless deletion!

CVS is a revision control system, which means that it stores every version of your project source code and enables retrieval and comparison of arbitrary versions. You can think of it as a file server that doesn't overwrite a file when it is changed; instead, it saves the new revision alongside the old. Then when you want to know what changed between version X and version Y of the source, you can ask CVS for a diff that indicates these changes between the two versions.

Obtaining CVS

Many UNIX systems come with CVS. If you need to install CVS, you can download the software at http://www.cvshome.org.

Windows users can install the CygWin[7] toolkit, which supports CVS (it's an optional package, so be sure to choose it during installation). Additionally, a number of GUI clients for Windows are available, the best of which is definitely WinCVS. You can find WinCVS here: http://www.wincvs.org.

I'll be showing you how to use CVS through the command-line client, but it should be easy to adapt this knowledge to WinCVS.

The CVS Program

All CVS commands are run using a single program called cvs. The program always requires a command name specifying the action to perform. It can take options before the command and options after the command. Here's a few examples of CVS commands to give you an idea of what I'm talking about:

 $ cvs -q update -d $ cvs diff -u $ cvs log Template.pm 

Anything after the command that isn't an option is usually treated as a filename argument-for example, in the last command Template.pm is a filename argument. Without a filename argument, most CVS commands operate recursively on all files and directories below the current directory.

Repositories

CVS refers to the place where it stores your project's files as the repository, or sometimes the CVS root. A repository is simply a directory containing CVS files. You can use a repository on your local machine, or you can connect to one on a different machine. CVS supports two ways of connecting to remote CVS servers-using CVS's native pserver protocol or through an rsh-compatible service, such as ssh.[8] I'll cover two possibilities: setting up a local repository on your machine and connecting to a CVS repository using ssh. These are by far the most common uses of CVS.

Using a Local Repository

All you need to set up your own local repository is a directory for CVS to store its files. Once you've picked a directory for your repository, you must prepare it for CVS using the cvs init command. For example, to create a new repository in my home directory, /home/sam, called /home/sam/cvsroot, I would issue the following command:

 $ cvs -d /home/sam/cvsroot init 

Once you've initialized the repository, you need to tell CVS about it. You could specify a -d option to all your commands as I did with cvs init earlier, but that would get very tiresome. Instead, set the CVSROOT environment variable in your shell to the name of the repository directory. For example, I use the bash shell and I have a line in my .bash_profile file like this:

 export CVSROOT=/home/sam/cvsroot 

After this step, all your cvs commands will use /home/sam/cvsroot as the repository.

Using a Remote CVS Repository with SSH

The most common usage of CVS via the Internet is through ssh. This is the way the free CVS server on SourceForge works, and I'll use it as an example. Since the folks at SourceForge have already initialized your CVS repository, you don't need to do a cvs init operation. All you need to do is set up two environment variables-CVSROOT and CVS_RSH. For example, to access a repository stored on the machine cvs.foo.sourceforge.net in the path /cvsroot/foo using ssh with the username "sam", I would make these settings:

 $ export CVSROOT=:ext:sam@cvs.foo.sourceforge.net:/cvsroot/foo $ export CVS_RSH=ssh 

The first line tells CVS where to look for the repository, and the second tells CVS to use ssh instead of rsh to connect to the server. If you're using SourceForge, you can find the values for both these variables on your project's CVS page.

The SourceForge servers also provide access using CVS's pserver protocol for anonymous access. Anonymous CVS access is a read-only service-it allows external developers to access the CVS tree and produce patches from it without being able to alter its state. See the SourceForge project's CVS page for details on connecting as an anonymous user.

Importing Your Module

To start using CVS with your module, you'll need to import your source into CVS. When you import your module into CVS, space is created in the CVSROOT for your project. To get started, make sure you have just the files you want to store in CVS in your module distribution directory. You shouldn't include derivative files such as Makefile or the contents of blib in CVS, so make sure you run a make clean first to remove them.

Next, you'll need to choose a CVS module name and import your source. CVS module names are unique identifiers that you'll use to access you files in CVS. By including module names, a single CVS repository can house any number of independent source trees. An easy way to create a name that CVS will accept is to use your module's distribution filename minus the version information. For example, to import the source for Data::Counter into CVS, I would use this command in the Data::Counter distribution directory:

 $ cvs import -m "Import of Data::Counter" Data-Counter vendor start 

The -m option specifies a log message for the import-if you leave it out, then CVS will open an editor for you to enter a log message. Most CVS commands accept a log message that can later be viewed with the cvs log command. After that comes the module name-Data-Counter. The last two arguments are the "vendor" tag and the "start" tag. Unless you're a total CVS nerd, you'll never use either of these, so you can set them to whatever you want. They're theoretically useful in tracking third-party sources through CVS. Or something like that.

Getting a Working Copy

To use CVS to manage the files you just imported, you need to check out a working copy of your source. To do so, use the cvs checkout command:

 $ cvs checkout Data-Counter U Data-Counter/Changes U Data-Counter/Counter.pm U Data-Counter/MANIFEST U Data-Counter/Makefile ... 

The output from the checkout command will show you the files and directories being created from the CVS repository; the leading U stands for updated. Your working copy contains all the same stuff that your module distribution does except that each directory also has a CVS directory that contains special CVS files. I won't go into the contents of these files, but you should be careful not to delete them or you'll have to check out a new working copy.

A CVS checkout does not lock the files being checked out, as is the case in some version control systems. CVS allows many developers to work on the same files simultaneously and will automatically merge changes. If changes cannot be merged, then a conflict results. See the CVS documentation for information on merging and conflict resolution.

Making Changes

Now that I have Data::Counter in CVS, let's take it out for a spin. To demonstrate making changes, I'll add a new subroutine to Data::Counter-count_char(), which counts the occurrences of character in a string:

 sub count_char {   my ($pkg, $char, $string) = @_;   return $string =~ tr/$char/$char/; } 

I'll also add a new test to t/02basic.t:

 is(Data::Counter->count_char('a', 'abababa'), 4); 

After making the changes, I can get a list of changed files using the cvs update command:

 $ cvs -q update M Counter.pm M t/02basic.t 

The -q option stands for "quiet", and in this case suppresses the printing of unchanged files. The M in front of Counter.pm and t/02basic.t means "modified." This tells you that the files have been modified since the last time they were committed to the repository. Another useful CVS feature is the ability to request a diff against the version in the repository with cvs diff:

 $ cvs diff -u cvs diff: Diffing . Index: Counter.pm =================================================================== RCS file: /home/sam/cvs/Data-Counter/Counter.pm,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 Counter.pm --- Counter.pm       2002/02/04 01:31:54    1.1.1.1 +++ Counter.pm     2002/02/04 06:24:41 @@ -34,6 +34,11 @@   return scalar @_; } +sub count_char { + my ($pkg, $char, $string) = @_; + return $string =~ tr/$char/$char/; +} +  1;  __END__  # Below is stub documentation for your module. You better edit it! cvs diff: Diffing t Index: t/02basic.t =================================================================== RCS file: /home/sam/cvs/Data-Counter/t/02basic.t,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 02basic.t --- t/02basic.t    2002/02/04 01:31:54    1.1.1.1 +++ t/02basic.t    2002/02/04 06:24:55 @@ -1,3 +1,4 @@ -use Test::More tests => 1; +use Test::More tests => 2;  use Data::Counter;  is(Data::Counter->count('foo', 'bar'), 2); +is(Data::Counter->count_char('a', 'abababa'), 4); 

By specifying the -u option to cvs diff, you can produce the same unified diff format as the diff -u you met earlier.

Once you're happy with your changes, you can save them to the repository with the cvs commit command, which takes a log message using the -m option just like cvs import did:

 $ cvs commit -m "Added the count_char() method" cvs commit: Examining . cvs commit: Examining t Checking in Counter.pm; /home/sam/cvsroot/Data-Counter/Counter.pm,v <-- Counter.pm new revision: 1.2; previous revision: 1.1 done Checking in t/02basic.t; /home/sam/cvsroot/Data-Counter/t/02basic.t,v <-- 02basic.t new revision: 1.2; previous revision: 1.1 done 

Now the current version in the CVS repository matches the contents of the working copy. In CVS terms the changes have been committed to the repository. This creates a new revision in the repository-in this case 1.2 for both files. Each file carries its own revision number, which is incremented each time it is updated. You can examine the history of a file in CVS using the cvs log command. For example, here's the cvs log output for Counter.pm after the commit operation:

 $ cvs log Counter.pm RCS file: /home/sam/cvsroot/Data-Counter/Counter.pm,v Working file: Counter.pm head: 1.2 branch: locks: strict access list: symbolic names:       start: 1.1.1.1       vendor: 1.1.1 keyword substitution: kv total revisions: 3;    selected revisions: 3 description: ---------------------------- revision 1.2 date: 2002/02/04 06:31:48; author: sam; state: Exp; lines: +5 -0 Added the count_char() method ---------------------------- revision 1.1 date: 2002/02/04 01:31:54; author: sam; state: Exp; branches: 1.1.1; Initial revision ---------------------------- revision 1.1.1.1 date: 2002/02/04 01:31:54; author: sam; state: Exp; lines: +0 -0 Import of Data::Counter ============================================================================= 

At the bottom, listed in reverse chronological order, are the revisions that exist in the CVS repository-1.2, 1.1, and 1.1.1.1 in this case-along with log messages.

Adding and Removing Files and Directories

CVS needs to be told about new files and directories. This is done with the cvs add command. For example, if I created an INSTALL file in the Data::Counter project, I would run the cvs add command:

 $ cvs add INSTALL cvs add: scheduling file 'INSTALL' for addition cvs add: use 'cvs commit' to add this file permanently 

As the output indicates, a cvs commit is needed to add the new file to the repository:

 $ cvs commit -m "Added new INSTALL file." INSTALL RCS file: /home/sam/cvsroot/book/INSTALL,v done Checking in INSTALL; /home/sam/cvsroot/Data-Counter/INSTALL,v <-- INSTALL initial revision: 1.1 done 

Similarly, cvs remove removes files from the repository. Simply deleting a file from a working copy is not enough to remove a file from CVS-the next time you update, it will come back again! For example, to remove the newly create INSTALL file, three commands are required, rm, cvs remove, and finally cvs commit, to make the change in the repository:

 $ rm INSTALL $ cvs remove INSTALL cvs remove: scheduling 'INSTALL' for removal cvs remove: use 'cvs commit' to remove this file permanently $ cvs commit -m "Removed INSTALL" INSTALL Removing INSTALL; /home/sam/cvsroot/book/FOO,v <-- FOO new revision: delete; previous revision: 1.1 done 

However, INSTALL is not gone from the repository. You can still examine its history, request diffs, and even call it back into existence by checking out an old version and adding it to the project.

Note 

Directories cannot be removed using normal CVS commands. The best you can do is make a directory empty and then check out a working copy with -P, which omits empty directories. To make matters worse, running cvs add on directories takes effect immediately-no cvs commit command is required. Thus you should very carefully consider your directory structure before running cvs add on a directory.

Staying Up-to-Date

A single project in CVS can support any number of working copies, which allows multiple developers to work on a project simultaneously. When you make changes in one working copy and commit them, your fellow developers will need to perform a cvs update to synchronize their working copies with the repository. For example, if I performed the cvs add INSTALL operation previously in one working copy, then my coworker Jesse would need to run cvs update in his working copy to get the new file as follows:

 $ cvs -q update -d U INSTALL 

The -q ("quiet") option suppresses printing all the files and directories in the project. The -d option isn't strictly necessary in this case, but it's a good option to use when updating a project directory-it allows CVS to create new directories that have been added to the project since the last update.

Time Travel

Most often you'll be working on the most recent version of your module, and CVS makes that very convenient. Other times you'll need to travel back in time, and that's when CVS becomes nearly indispensable. For example, imagine that Bob Dobbs sends me a patch to Data::Counter that adds a new feature. I apply the patch and commit the change:

 $ cvs commit -m "New feature from Bob Dobbs" Counter.pm Checking in Counter.pm; /home/sam/cvsroot/Data-Counter/Counter.pm,v <-- Counter.pm new revision: 1.3; previous revision: 1.2 done 

Time passes, and a few weeks later I start having trouble with the function that Bob patched. In order to determine if Bob's patch is to blame, I need to roll back to the version before the patch and try my test case. Now, one option would be to dig out Bob's patch and reverse it using patch -R, but CVS offers an easier solution. Using cvs update with the -r argument, you can update a file to an arbitrary revision. For example, this command brings Counter.pm back to the state it was in before Bob's patch:

 $ cvs update -r 1.2 Counter.pm U Counter.pm 

The 1.2 is the revision number for the version I want to update to, which can be determined by looking at the output of cvs log.

Another way to use cvs update to travel into the past is to include the -D option to specify a date to travel back to. For example, to update the entire tree to the state it was in on Monday, May 28th, you'd do something like this:

 $ cvs update -D "5/28/2002" cvs update: Updating . U Changes U Counter.pm U INSTALL ... 

As you can see, this has the advantage of working across the entire project. Since revision numbers (used with the -r option) are specific to a single file, they can't be used with multiple files.

To get back to the most recent version, you need to use the -A option to cvs update:

 $ cvs update -A cvs update: Updating . U Counter.pm cvs update: Updating t 

This allows you to begin making changes to your files and committing them. In CVS you can examine the past but you can't change it.[9] Until you update with -A, CVS will prevent you from committing changes to files from the past.

Getting the Most out of CVS

You can get a lot out of CVS with only a little knowledge, which is fortunate since that's all I have space to impart. However, CVS supports many more useful features than I've had space to cover-tags, branches, conflict resolution, and much more. This section has introduced you to CVS and demonstrated a few common tasks. Hopefully you now see what a useful tool CVS can be in your development. It can allow you to work with patches and changes much more intelligently, as well as opening the door to multideveloper projects.

To complete your CVS education, you should read the online documentation available at http://www.cvshome.org.

Or, if you'd prefer the details in book form, Open Source Development with CVS by Fogel and Bar (Coriolis) is an excellent read. As a bonus, it also includes another take on many of the topics presented in this chapter. See their site for more details: http://www.cvsbook.red-bean.com/.

Bug Tracking

Every piece of software has bugs, and your modules will be no exception. The usual way of dealing with bugs is via e-mail. A user spots a bug and writes to you or to the project mailing list with a description. After some discussion, you verify the bug and fix it, or if you're really lucky someone else does and sends you a patch. The problem with this approach is that it's all too easy for a bug to slip through the cracks. E-mail also lacks visibility-it's hard for your users to get an accurate picture of the status of a bug.

Bug-tracking software provides a better solution. Users typically fill out a Web- based bug submission form describing the bug. The bug enters the bug-tracking system as a new bug. At some later point you (or another developer with the appropriate permissions) verify the bug and move it to an accepted state. If any discussion is necessary to verify the bug, then it can be carried out through the bug tracker, which automatically e-mails the participants. Finally, when the bug is fixed, it is marked closed. This makes it exceedingly hard to lose track of a bug, and some systems will even remind you of neglected bugs periodically. It also allows users to keep track of bugs they care about.

The same software that you use to track bugs can also be used to track other types of development-feature development and ideas, for example. This can increase the visibility of the project and help organize development among developers.

Of course, there are far more bug-tracking packages available than I have space to list (or even time to learn about!). The following are some of the most popular in the open-source community and should be on your list when you're ready to start tracking your bugs.

CPAN's Request Tracker

CPAN offers a free bug-tracking service for all registered CPAN authors. You can log in to the system at http://www.rt.cpan.org.

When you log in you'll see a list of your CPAN modules along with status information on open bugs (see Figure 6-1). From here you can get to detail pages for each bug as well as pages to enter new bug reports.

click to expand
Figure 6-1: rt.cpan.org author home page

At the moment rt.cpan.org is very new and still under development. It definitely has some kinks left to work out. I recommend you check it out and see how things are shaping up; if all goes as planned, it will be the standard place for reporting bugs in CPAN modules by the time this book is printed.

SourceForge

SourceForge provides free bug-tracking for registered projects through a generalized "Tracker" system. In addition to bugs, the Tracker handles support requests, patches, and feature requests. SourceForge also provides a separate task-list application that functions as a lightweight progress tracker for use by developers.

The SourceForge bug-tracking facility is well integrated into the larger SourceForge system. When a SourceForge user creates a bug report, status information on the bug is available on that user's home page as long as the bug is live. If you're already using SourceForge for CVS and mailing lists, then this may be the ideal solution for you.

Bugzilla

Bugzilla is a popular, open-source bug-tracking system created to support the Mozilla[10] project. It's written entirely in Perl and uses MySQL[11] to hold its data. If you're considering setting up your own bug tracker, then you should definitely take a look at Bugzilla. You can find more information about this bug-tracking system at http://www.mozilla.org/projects/bugzilla/.

[5]Most Unix systems come with diff and patch installed. If your system is missing these utilities, you can find the GNU versions at http://www.gnu.org. Windows users can get diff and patch by installing the CygWin toolkit available at http://www.cygwin.com. Note that patch is an optional package, and you'll have to select it manually from setup.exe

[6]You can also get context in the normal diff format using the -c option, but unified patches are usually easier to read.

[7]Available at http://www.cygwin.com

[8]rsh stands for remote shell, a common utility for running commands on remote hosts available in most UNIXes. ssh stands for secure shell, an rsh-compatible utility that also encrypts all communication between client and server.

[9]Thus CVS has solved the age-old science-fiction paradox: You can neither kill Hitler nor prevent your own conception with CVS, for better or worse.

[10]A Web-browsing, mail-and-news-reading, IRC-chatting, calendar-having mega-application. See http://www.mozilla.org for details.

[11]An open-source relational database system. See http://www.mysql.com for details.



Writing Perl Modules for CPAN
Writing Perl Modules for CPAN
ISBN: 159059018X
EAN: 2147483647
Year: 2002
Pages: 110
Authors: Sam Tregar

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